maximkulkin / esp-homekit-demo

Demo of Apple HomeKit accessory server library
MIT License
805 stars 233 forks source link

Faucet Service for Homekit #326

Closed kalindud closed 4 years ago

kalindud commented 4 years ago

hello, has anyone used the FAUCET service ? I know that the required characteristic is "ACTIVE". But how do I get or set it? any ideas? any examples?

I tried using .getter and .setter... but the device fails when adding to Home app.

help pls!

Thanks!

maximkulkin commented 4 years ago

Code please

kalindud commented 4 years ago

@maximkulkin

#include <stdio.h>
#include <espressif/esp_wifi.h>
#include <espressif/esp_sta.>
#include <espressif/esp_common.h>
#include <esp/uart.h>
#include <esp8266.h>
#include <FreeRTOS.h>
#include <task.h>

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

#include "button.h"

// The GPIO pin that is connected to the relay on the Sonoff Basic.
const int relay_gpio = 12;
// The GPIO pin that is connected to the LED on the Sonoff Basic.
const int led_gpio = 13;
// The GPIO pin that is connected to the button on the Sonoff Basic.
const int button_gpio = 0;

bool stc_on = false;

void switch_on_callback(homekit_characteristic_t *_ch, homekit_value_t on, void *context);
void button_callback(uint8_t gpio, button_event_t event);

void relay_write(bool on) {
    gpio_write(relay_gpio, on ? 1 : 0);
}

void led_write(bool on) {
    gpio_write(led_gpio, on ? 0 : 1);
}

void reset_configuration_task() {
    //Flash the LED first before we start the reset
    for (int i=0; i<3; i++) {
        led_write(true);
        vTaskDelay(100 / portTICK_PERIOD_MS);
        led_write(false);
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }

    printf("Resetting Wifi Config\n");    
    wifi_config_reset();    
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Resetting HomeKit Config\n");    
    homekit_server_reset();    
    vTaskDelay(1000 / portTICK_PERIOD_MS);    
    printf("Restarting\n");
    sdk_system_restart();    
    vTaskDelete(NULL);
}

void reset_configuration() {
    printf("Resetting Sonoff configuration\n");
    xTaskCreate(reset_configuration_task, "Reset configuration", 256, NULL, 2, NULL);
}

homekit_value_t stc_on_get() {
    return HOMEKIT_BOOL(stc_on);
}

void stc_on_set(homekit_value_t value) {

    stc_on = value.bool_value;
    relay_write(stc_on);
}

void gpio_init() {
    gpio_enable(led_gpio, GPIO_OUTPUT);
    led_write(false);
    gpio_enable(relay_gpio, GPIO_OUTPUT);
    relay_write(switch_on.value.bool_value);
}

void button_callback(uint8_t gpio, button_event_t event) {
    switch (event) {
        case button_event_single_press:
            printf("Toggling relay\n");
            switch_on.value.bool_value = !switch_on.value.bool_value;
            relay_write(switch_on.value.bool_value);
            homekit_characteristic_notify(&switch_on, switch_on.value);
            break;
        case button_event_long_press:
            reset_configuration();
            break;
        default:
            printf("Unknown button event: %d\n", event);
    }
}

void switch_identify_task(void *_args) {
    // We identify the Sonoff by Flashing it's LED.
    for (int i=0; i<3; i++) {
        for (int j=0; j<2; j++) {
            led_write(true);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            led_write(false);
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }

        vTaskDelay(250 / portTICK_PERIOD_MS);
    }

    led_write(false);

    vTaskDelete(NULL);
}

void switch_identify(homekit_value_t _value) {
    printf("Switch identify\n");
    xTaskCreate(switch_identify_task, "Switch identify", 128, NULL, 2, NULL);
}

homekit_characteristic_t name = HOMEKIT_CHARACTERISTIC_(NAME, Faucet 1");

homekit_accessory_t *accessories[] = {
    HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_faucet, .services=(homekit_service_t*[]){
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]){
            &name,
            HOMEKIT_CHARACTERISTIC(MANUFACTURER, "iTEAD"),
            HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "037A2BABF19D"),
            HOMEKIT_CHARACTERISTIC(MODEL, "Basic"),
            HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.1.6"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, switch_identify),
            NULL
        }),
        HOMEKIT_SERVICE(FAUCET, .primary=true, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "faucet 1"),
            HOMEKIT_CHARACTERISTIC(
                ACTIVE, false,
                .getter=stc_on_get,
                .setter=stc_on_set
            ),
            NULL
        }),
        NULL
    }),
    NULL
};

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

void on_wifi_ready() {
    homekit_server_init(&config);
}

void create_accessory_name() {
    uint8_t macaddr[6];
    sdk_wifi_get_macaddr(STATION_IF, macaddr);

    int name_len = snprintf(NULL, 0, "faucet 1-%02X%02X%02X",
                            macaddr[3], macaddr[4], macaddr[5]);
    char *name_value = malloc(name_len+1);
    snprintf(name_value, name_len+1, "faucet 1-%02X%02X%02X",
             macaddr[3], macaddr[4], macaddr[5]);

    name.value = HOMEKIT_STRING(name_value);
}

void user_init(void) {
    uart_set_baud(0, 115200);

    create_accessory_name();

    wifi_config_init("faucet-relay", NULL, on_wifi_ready);
    gpio_init();

    if (button_create(button_gpio, 0, 4000, button_callback)) {
        printf("Failed to initialize button\n");
    }
}
maximkulkin commented 4 years ago

Ok, I've checked specification and here is what I found:

  1. What you probably need is Valve service (the thing that actually controls water flowing). You can have just Valve service exposed by accessory and it will show up just fine.
  2. Faucet service is a general grouping of several Valve services and optional Heater Cooler service. E.g. you have cold and hot valves (as on most kitchen/bathroom faucets), or have a multiple shower valves (e.g. top shower large, top shower sides, side shower). Faucet service, as far as I understand, allows to turn them on/off all together (accessory still need to update individual valve's "Active" and "In Use" characteristics accordingly). If you still want to use Faucet, make sure that you provide Valve services as linked services:

#define VALVE_TYPE_GENERIC 0
#define VALVE_TYPE_IRRIGATION 1
#define VALVE_TYPE_SHOWER_HEAD 2
#define VALVE_TYPE_WATER_FAUCET 3

homekit_characteristic_t valve1_active = HOMEKIT_CHARACTERISTIC_(ACTIVE, false);
homekit_characteristic_t valve1_in_use = HOMEKIT_CHARACTERISTIC_(IN_USE, 0);

homekit_characteristic_t valve2_active = HOMEKIT_CHARACTERISTIC_(ACTIVE, false);
homekit_characteristic_t valve2_in_use = HOMEKIT_CHARACTERISTIC_(IN_USE, 0);

homekit_service_t valve1 = HOMEKIT_SERVICE_(VALVE, .characteristics=(homekit_characteristic_t*[]){
    HOMEKIT_CHARACTERISTIC(NAME, "Cold Valve"),
    HOMEKIT_CHARACTERISTIC(VALVE_TYPE, VALVE_TYPE_GENERIC),
    &valve1_active,
    &valve1_in_use,
});

homekit_service_t valve2 = HOMEKIT_SERVICE_(VALVE, .characteristics=(homekit_characteristic_t*[]){
    HOMEKIT_CHARACTERISTIC(NAME, "Hot Valve"),
    HOMEKIT_CHARACTERISTIC(VALVE_TYPE, VALVE_TYPE_GENERIC),
    &valve2_active,
    &valve2_in_use,
});

homekit_characteristic_t faucet_active = HOMEKIT_CHARACTERISTIC_(ACTIVE, false);

homekit_accessory_t *accessories[] = {
    HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_faucet, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFO, .characteristics=(homekit_characteristic_t*[]) {
            // ...
        }),
        HOMEKIT_SERVICE(FAUCET, .primary=true, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "Faucet 1"),
            &faucet_active,
            NULL
        }, .linked=(homekit_service_t*[] {
            &valve1,
            &valve2,
            NULL
        }),
        &valve1,
        &valve2,
        NULL
    }),
    NULL
}