/* HOME ASSISTANT MODULE Copyright (C) 2017-2019 by Xose PĂ©rez */ #if HOMEASSISTANT_SUPPORT #include bool _haEnabled = false; bool _haSendFlag = false; // ----------------------------------------------------------------------------- // UTILS // ----------------------------------------------------------------------------- // per yaml 1.1 spec, following scalars are converted to bool. we want the string, so quoting the output // y|Y|yes|Yes|YES|n|N|no|No|NO |true|True|TRUE|false|False|FALSE |on|On|ON|off|Off|OFF String _haFixPayload(const String& value) { if (value.equalsIgnoreCase("y") || value.equalsIgnoreCase("n") || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false") || value.equalsIgnoreCase("on") || value.equalsIgnoreCase("off") ) { String temp; temp.reserve(value.length() + 2); temp = "\""; temp += value; temp += "\""; return temp; } return value; } String& _haFixName(String& name) { for (unsigned char i=0; i 1) { name += String("_") + String(i); } config.set("name", _haFixName(name)); if (relayCount()) { config["state_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, false); config["command_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, true); config["payload_on"] = relayPayload(true); config["payload_off"] = relayPayload(false); config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false); config["payload_available"] = mqttPayloadStatus(true); config["payload_not_available"] = mqttPayloadStatus(false); } #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE if (i == 0) { config["brightness_state_topic"] = mqttTopic(MQTT_TOPIC_BRIGHTNESS, false); config["brightness_command_topic"] = mqttTopic(MQTT_TOPIC_BRIGHTNESS, true); if (lightHasColor()) { config["rgb_state_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, false); config["rgb_command_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, true); } if (lightUseCCT()) { config["color_temp_command_topic"] = mqttTopic(MQTT_TOPIC_MIRED, true); } if (lightChannels() > 3) { config["white_value_state_topic"] = mqttTopic(MQTT_TOPIC_CHANNEL, 3, false); config["white_value_command_topic"] = mqttTopic(MQTT_TOPIC_CHANNEL, 3, true); } } #endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE } void _haSendSwitches(ha_config_t& config) { for (unsigned char i=0; i()); } else { output += kv.value.as(); } output += "\n"; } output += " "; root.remove("config"); root["haConfig"] = output; } #if SENSOR_SUPPORT void _haSensorYaml(unsigned char index, JsonObject& root) { String output; output.reserve(HA_YAML_BUFFER_SIZE); JsonObject& config = root.createNestedObject("config"); _haSendMagnitude(index, config); if (index == 0) output += "\n\nsensor:"; output += "\n"; bool first = true; for (auto kv : config) { if (first) { output += " - "; first = false; } else { output += " "; } String value = kv.value.as(); value.replace("%", "'%'"); output += kv.key; output += ": "; output += value; output += "\n"; } output += " "; root.remove("config"); root["haConfig"] = output; } #endif // SENSOR_SUPPORT void _haGetDeviceConfig(JsonObject& config) { config.createNestedArray("identifiers").add(getIdentifier()); config["name"] = getSetting("desc", getSetting("hostname")); config["manufacturer"] = MANUFACTURER; config["model"] = DEVICE; config["sw_version"] = String(APP_NAME) + " " + APP_VERSION + " (" + getCoreVersion() + ")"; } void _haSend() { // Pending message to send? if (!_haSendFlag) return; // Are we connected? if (!mqttConnected()) return; DEBUG_MSG_P(PSTR("[HA] Sending autodiscovery MQTT message\n")); // Get common device config ha_config_t config; // Send messages _haSendSwitches(config); #if SENSOR_SUPPORT _haSendMagnitudes(config); #endif _haSendFlag = false; } void _haConfigure() { bool enabled = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1; _haSendFlag = (enabled != _haEnabled); _haEnabled = enabled; _haSend(); } #if WEB_SUPPORT bool _haWebSocketOnKeyCheck(const char * key, JsonVariant& value) { return (strncmp(key, "ha", 2) == 0); } void _haWebSocketOnVisible(JsonObject& root) { root["haVisible"] = 1; } void _haWebSocketOnConnected(JsonObject& root) { root["haPrefix"] = getSetting("haPrefix", HOMEASSISTANT_PREFIX); root["haEnabled"] = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1; } void _haWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) { if (strcmp(action, "haconfig") == 0) { ws_on_send_callback_list_t callbacks; #if SENSOR_SUPPORT callbacks.reserve(magnitudeCount() + relayCount()); #else callbacks.reserve(relayCount()); #endif // SENSOR_SUPPORT { for (unsigned char idx=0; idx().c_str()); } #if SENSOR_SUPPORT for (unsigned char idx=0; idx().c_str()); } #endif // SENSOR_SUPPORT DEBUG_MSG("\n"); terminalOK(); }); terminalRegisterCommand(F("HA.SEND"), [](Embedis* e) { setSetting("haEnabled", "1"); _haConfigure(); #if WEB_SUPPORT wsPost(_haWebSocketOnConnected); #endif terminalOK(); }); terminalRegisterCommand(F("HA.CLEAR"), [](Embedis* e) { setSetting("haEnabled", "0"); _haConfigure(); #if WEB_SUPPORT wsPost(_haWebSocketOnConnected); #endif terminalOK(); }); } #endif // ----------------------------------------------------------------------------- void haSetup() { _haConfigure(); #if WEB_SUPPORT wsRegister() .onVisible(_haWebSocketOnVisible) .onConnected(_haWebSocketOnConnected) .onAction(_haWebSocketOnAction) .onKeyCheck(_haWebSocketOnKeyCheck); #endif #if TERMINAL_SUPPORT _haInitCommands(); #endif // On MQTT connect check if we have something to send mqttRegister([](unsigned int type, const char * topic, const char * payload) { if (type == MQTT_CONNECT_EVENT) _haSend(); if (type == MQTT_DISCONNECT_EVENT) _haSendFlag = false; }); // Main callbacks espurnaRegisterReload(_haConfigure); } #endif // HOMEASSISTANT_SUPPORT