Newer
Older
smart-home-server / devices / sensor / SensorLogic.cpp
#include "SensorLogic.h"
#include "sh_core.h"

#include "ld2420_radar.h"
#include "bh1750_sensor.h"
#include "bme280_sensor.h"
#include "max4466_mic.h"

/*
    =========================================================
    Внешние объекты — объявлены и инициализированы в sensor.ino
    =========================================================
*/
extern Ld2420Radar    radar;
extern Bh1750Sensor   light_sensor;
extern Bme280Sensor   climate_sensor;
extern Max4466Mic     mic;

/*
    =========================================================
    NeoPixel
    =========================================================
*/
static Adafruit_NeoPixel led(SENSOR_LED_COUNT, SENSOR_LED_PIN, NEO_GRB + NEO_KHZ800);

static uint32_t color(uint8_t r, uint8_t g, uint8_t b) {
    return led.Color(r, g, b);
}

/*
    =========================================================
    Состояние presence — для отслеживания смены
    =========================================================
*/
static bool _prev_presence      = false;
static bool _presence_init_done = false;   // первый цикл — не шлём событие

/*
    =========================================================
    Вспомогательные функции
    =========================================================
*/

static bool _is_setup() {
    return (deviceMode == DEVICE_MODE_SETUP);
}

static bool _is_nowifi() {
    if (deviceMode == DEVICE_MODE_SETUP) return false;
    return (WiFi.status() != WL_CONNECTED);
}

/*
    Хотя бы один датчик online — считаем устройство рабочим.
*/
static bool _any_sensor_online() {
    return radar.is_online()
        || light_sensor.is_online()
        || climate_sensor.is_online()
        || mic.is_online();
}

/*
    =========================================================
    Индикатор
    =========================================================
*/
static void _render_led() {
    uint32_t t = millis();

    /* --- Глобальные режимы --- */
    if (_is_setup()) {
        bool on = ((t / SENSOR_BLINK_SLOW_MS) % 2) == 0;
        led.setPixelColor(0, on ? color(255, 255, 255) : color(0, 0, 0));
        led.show();
        return;
    }

    if (_is_nowifi()) {
        bool on = ((t / SENSOR_BLINK_SLOW_MS) % 2) == 0;
        led.setPixelColor(0, on ? color(0, 0, 255) : color(0, 0, 0));
        led.show();
        return;
    }

    if (!_any_sensor_online()) {
        bool on = ((t / SENSOR_BLINK_SLOW_MS) % 2) == 0;
        led.setPixelColor(0, on ? color(255, 0, 0) : color(0, 0, 0));
        led.show();
        return;
    }

    /* --- Рабочий режим --- */
    if (radar.is_presence()) {
        /* Присутствие — постоянный мягкий белый */
        led.setPixelColor(0, color(60, 60, 60));
    } else {
        /* Тишина — тихий зелёный */
        led.setPixelColor(0, color(0, 20, 0));
    }

    led.show();
}

/*
    =========================================================
    Событие presence_changed
    =========================================================
*/
static void _send_presence_event(bool presence) {
    if (deviceMode != DEVICE_MODE_NORMAL) return;
    if (WiFi.status() != WL_CONNECTED)   return;

    String body = "{";
    body += "\"event_name\":\"presence_changed\",";
    body += "\"data\":{";
    body += "\"presence\":"                + String(presence ? "true" : "false") + ",";
    body += "\"activity_score\":"          + String(radar.get_activity_score()) + ",";
    body += "\"activity_score_current\":"  + String(radar.get_activity_score_current()) + ",";
    body += "\"distance_m\":"              + String(radar.get_distance_m(), 2);
    body += "},";
    body += "\"device_id\":\"" + getUniqueID() + "\"";
    body += "}";

    int http_code = -1;
    core_post_json_to_server(SENSOR_EVENT_PATH, body, SENSOR_EVENT_TIMEOUT_MS, http_code);
}

/*
    =========================================================
    Отслеживание смены presence
    =========================================================
*/
static void _tick_presence() {
    bool current = radar.is_presence();

    if (!_presence_init_done) {
        _prev_presence      = current;
        _presence_init_done = true;
        return;
    }

    if (current != _prev_presence) {
        _prev_presence = current;
        _send_presence_event(current);
    }
}

/*
    =========================================================
    weak-хуки ядра
    =========================================================
*/

/*
    /status — добавляем поля sensors и indicators.
    Ядро уже открыло '{' и написало "status":"ok",
    мы дописываем в json начиная с ','.
*/
void appendStatusJsonFields(String &json) {
    /* indicators */
    String ind;
    if (_is_setup())        ind = "setup";
    else if (_is_nowifi())  ind = "nowifi";
    else                    ind = "ok";

    json += ",\"indicators\":\"" + ind + "\"";

    /* sensors */
    json += ",\"sensors\":{";

    /* light */
    json += "\"light\":"       + light_sensor.get_state_json();

    /* temperature */
    json += ",\"temperature\":" + climate_sensor.get_temperature_json();

    /* pressure */
    json += ",\"pressure\":"    + climate_sensor.get_pressure_json();

    /* humidity — только если BME280 */
    if (climate_sensor.has_humidity()) {
        json += ",\"humidity\":" + climate_sensor.get_humidity_json();
    }

    /* radar */
    json += ",\"radar\":"       + String(radar.get_state_json());

    /* microphone */
    json += ",\"microphone\":"  + mic.get_state_json();

    json += "}";
}

/*
    /action — датчик пассивный.
    Поддерживается одно действие: reset_peak_noise.
*/
bool deviceHandleAction(const String &action,
                        const String &paramsJson,
                        String &errorCode,
                        String &errorMessage)
{
    (void)paramsJson;

    if (action == "reset_peak_noise") {
        mic.reset_peak();
        return true;
    }

    errorCode    = "IllegalActionOrParams";
    errorMessage = "Device does not support this action";
    return false;
}

/*
    =========================================================
    Setup / Loop
    =========================================================
*/

void sensor_logic_setup() {
    led.begin();
    led.setBrightness(40);
    led.clear();
    led.show();
}

void sensor_logic_loop() {
    _tick_presence();
    _render_led();
}