#include #include "Globals.h" #include "DisplayManager.h" #include "ServerManager.h" #include #include #include #include "Dictionary.h" #include "PeripheryManager.h" #include "UpdateManager.h" WiFiClient espClient; uint8_t lastBrightness; HADevice device; HAMqtt mqtt(espClient, device, 25); unsigned long reconnectTimer = 0; const unsigned long reconnectInterval = 30000; // 30 Sekunden HALight *Matrix = nullptr; HALight *Indikator1 = nullptr; HALight *Indikator2 = nullptr; HASelect *BriMode = nullptr; HAButton *dismiss = nullptr; HAButton *nextApp = nullptr; HAButton *prevApp = nullptr; HASwitch *transition = nullptr; HASensor *curApp = nullptr; #ifdef ULANZI HASensor *battery = nullptr; #endif HASensor *temperature = nullptr; HASensor *humidity = nullptr; HASensor *illuminance = nullptr; HASensor *uptime = nullptr; HASensor *strength = nullptr; HASensor *version = nullptr; HASensor *ram = nullptr; HABinarySensor *btnleft = nullptr; HABinarySensor *btnmid = nullptr; HABinarySensor *btnright = nullptr; HABinarySensor *update = nullptr; HAButton *doUpdate = nullptr; // The getter for the instantiated singleton instance MQTTManager_ &MQTTManager_::getInstance() { static MQTTManager_ instance; return instance; } // Initialize the global shared instance MQTTManager_ &MQTTManager = MQTTManager.getInstance(); void onButtonCommand(HAButton *sender) { if (sender == dismiss) { DisplayManager.dismissNotify(); } else if (sender == nextApp) { DisplayManager.nextApp(); } else if (sender == prevApp) { DisplayManager.previousApp(); } else if (sender == doUpdate) { if (UPDATE_AVAILABLE) UpdateManager.updateFirmware(); } } void onSwitchCommand(bool state, HASwitch *sender) { AUTO_TRANSITION = state; DisplayManager.setAutoTransition(state); saveSettings(); sender->setState(state); } void onSelectCommand(int8_t index, HASelect *sender) { switch (index) { case 0: AUTO_BRIGHTNESS = true; break; case 1: AUTO_BRIGHTNESS = false; break; default: AUTO_BRIGHTNESS = true; return; } Matrix->setBrightness(BRIGHTNESS); saveSettings(); sender->setState(index); // report the selected option back to the HA panel } void onRGBColorCommand(HALight::RGBColor color, HALight *sender) { if (sender == Matrix) { TEXTCOLOR_565 = ((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F); saveSettings(); } else if (sender == Indikator1) { DisplayManager.setIndicator1(true, ((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F)); } else if (sender == Indikator2) { DisplayManager.setIndicator2(true, ((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F)); } sender->setRGBColor(color); // report color back to the Home Assistant } void onStateCommand(bool state, HALight *sender) { if (sender == Matrix) { if (state) { MATRIX_OFF = false; DisplayManager.setBrightness(lastBrightness); } else { MATRIX_OFF = true; lastBrightness = BRIGHTNESS; DisplayManager.setBrightness(0); } } else if (sender == Indikator1) { DisplayManager.setIndicator1(state, 0); } else if (sender == Indikator2) { DisplayManager.setIndicator2(state, 0); } sender->setState(state); } void onBrightnessCommand(uint8_t brightness, HALight *sender) { sender->setBrightness(brightness); if (AUTO_BRIGHTNESS) return; BRIGHTNESS = brightness; lastBrightness = brightness; saveSettings(); DisplayManager.setBrightness(brightness); } void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) { String strTopic = String(topic); char *payloadCopy = new char[length + 1]; memcpy(payloadCopy, payload, length); payloadCopy[length] = '\0'; ++RECEIVED_MESSAGES; if (strTopic.equals(MQTT_PREFIX + "/notify")) { if (payload[0] != '{' || payload[length - 1] != '}') { delete[] payloadCopy; return; } DisplayManager.generateNotification(payloadCopy); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/timer")) { DisplayManager.gererateTimer(payloadCopy); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/notify/dismiss")) { DisplayManager.dismissNotify(); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/apps")) { DisplayManager.updateAppVector(payloadCopy); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/switch")) { DisplayManager.switchToApp(payloadCopy); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/settings")) { DisplayManager.setNewSettings(payloadCopy); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/nextapp")) { DisplayManager.nextApp(); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/previousapp")) { DisplayManager.previousApp(); delete[] payloadCopy; return; } if (strTopic.equals(MQTT_PREFIX + "/doupdate")) { if (UPDATE_AVAILABLE) UpdateManager.updateFirmware(); delete[] payloadCopy; return; } else if (strTopic.startsWith(MQTT_PREFIX + "/custom")) { String topic_str = topic; String prefix = MQTT_PREFIX + "/custom/"; if (topic_str.startsWith(prefix)) { topic_str = topic_str.substring(prefix.length()); } DisplayManager.generateCustomPage(topic_str, payloadCopy); delete[] payloadCopy; return; } } void onMqttConnected() { String prefix = MQTT_PREFIX; const char *topics[] PROGMEM = { "/brightness", "/notify/dismiss", "/notify", "/timer", "/custom/#", "/switch", "/settings", "/previousapp", "/nextapp", "/doupdate", "/nextapp", "/apps"}; for (const char *topic : topics) { String fullTopic = prefix + topic; mqtt.subscribe(fullTopic.c_str()); } Serial.println(F("MQTT Connected")); } void connect() { mqtt.onMessage(onMqttMessage); mqtt.onConnected(onMqttConnected); if (MQTT_USER == "" || MQTT_PASS == "") { Serial.println(F("Connecting to MQTT w/o login")); mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, nullptr, nullptr, MQTT_PREFIX.c_str()); } else { Serial.println(F("Connecting to MQTT with login")); mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, MQTT_USER.c_str(), MQTT_PASS.c_str(), MQTT_PREFIX.c_str()); } } char matID[40], ind1ID[40], ind2ID[40], briID[40]; char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40], updateID[40], doUpdateID[40]; #ifdef ULANZI char batID[40]; #endif void MQTTManager_::setup() { if (HA_DISCOVERY) { Serial.println(F("Starting Homeassistant discorvery")); uint8_t mac[6]; WiFi.macAddress(mac); char *macStr = new char[18 + 1]; snprintf(macStr, 24, "%02x%02x%02x", mac[3], mac[4], mac[5]); device.setUniqueId(mac, sizeof(mac)); device.setName(uniqueID); device.setSoftwareVersion(VERSION); device.setManufacturer(HAmanufacturer); device.setModel(HAmodel); device.setAvailability(true); device.enableSharedAvailability(); device.enableLastWill(); String uniqueIDWithSuffix; sprintf(matID, HAmatID, macStr); Matrix = new HALight(matID, HALight::BrightnessFeature | HALight::RGBFeature); Matrix->setIcon(HAmatIcon); Matrix->setName(HAmatName); Matrix->onStateCommand(onStateCommand); Matrix->onBrightnessCommand(onBrightnessCommand); Matrix->onRGBColorCommand(onRGBColorCommand); Matrix->setCurrentState(true); Matrix->setBRIGHTNESS(BRIGHTNESS); sprintf(ind1ID, HAi1ID, macStr); Indikator1 = new HALight(ind1ID, HALight::RGBFeature); Indikator1->setIcon(HAi1Icon); Indikator1->setName(HAi1Name); Indikator1->onStateCommand(onStateCommand); Indikator1->onRGBColorCommand(onRGBColorCommand); sprintf(ind2ID, HAi2ID, macStr); Indikator2 = new HALight(ind2ID, HALight::RGBFeature); Indikator2->setIcon(HAi2Icon); Indikator2->setName(HAi2Name); Indikator2->onStateCommand(onStateCommand); Indikator2->onRGBColorCommand(onRGBColorCommand); HALight::RGBColor color; color.red = (TEXTCOLOR_565 >> 11) << 3; color.green = ((TEXTCOLOR_565 >> 5) & 0x3F) << 2; color.blue = (TEXTCOLOR_565 & 0x1F) << 3; Matrix->setCurrentRGBColor(color); Matrix->setState(true, true); sprintf(briID, HAbriID, macStr); BriMode = new HASelect(briID); BriMode->setOptions(HAbriOptions); // use semicolons as separator of options BriMode->onCommand(onSelectCommand); BriMode->setIcon(HAbriIcon); // optional BriMode->setName(HAbriName); // optional BriMode->setState(AUTO_BRIGHTNESS, true); sprintf(btnAID, HAbtnaID, macStr); dismiss = new HAButton(btnAID); dismiss->setIcon(HAbtnaIcon); dismiss->setName(HAbtnaName); sprintf(doUpdateID, HAdoUpID, macStr); doUpdate = new HAButton(doUpdateID); doUpdate->setIcon(HAdoUpIcon); doUpdate->setName(HAdoUpName); doUpdate->onCommand(onButtonCommand); sprintf(transID, HAtransID, macStr); transition = new HASwitch(transID); transition->setIcon(HAtransIcon); transition->setName(HAtransName); transition->onCommand(onSwitchCommand); sprintf(appID, HAappID, macStr); curApp = new HASensor(appID); curApp->setIcon(HAappIcon); curApp->setName(HAappName); sprintf(btnBID, HAbtnbID, macStr); nextApp = new HAButton(btnBID); nextApp->setIcon(HAbtnbIcon); nextApp->setName(HAbtnbName); sprintf(btnCID, HAbtncID, macStr); prevApp = new HAButton(btnCID); prevApp->setIcon(HAbtncIcon); prevApp->setName(HAbtncName); dismiss->onCommand(onButtonCommand); nextApp->onCommand(onButtonCommand); prevApp->onCommand(onButtonCommand); sprintf(tempID, HAtempID, macStr); temperature = new HASensor(tempID); temperature->setIcon(HAtempIcon); temperature->setName(HAtempName); temperature->setDeviceClass(HAtempClass); temperature->setUnitOfMeasurement(HAtempUnit); sprintf(humID, HAhumID, macStr); humidity = new HASensor(humID); humidity->setIcon(HAhumIcon); humidity->setName(HAhumName); humidity->setDeviceClass(HAhumClass); humidity->setUnitOfMeasurement(HAhumUnit); #ifdef ULANZI sprintf(batID, HAbatID, macStr); battery = new HASensor(batID); battery->setIcon(HAbatIcon); battery->setName(HAbatName); battery->setDeviceClass(HAbatClass); battery->setUnitOfMeasurement(HAbatUnit); #endif sprintf(luxID, HAluxID, macStr); illuminance = new HASensor(luxID); illuminance->setIcon(HAluxIcon); illuminance->setName(HAluxName); illuminance->setDeviceClass(HAluxClass); illuminance->setUnitOfMeasurement(HAluxUnit); sprintf(verID, HAverID, macStr); version = new HASensor(verID); version->setName(HAverName); sprintf(sigID, HAsigID, macStr); strength = new HASensor(sigID); strength->setName(HAsigName); strength->setDeviceClass(HAsigClass); strength->setUnitOfMeasurement(HAsigUnit); sprintf(upID, HAupID, macStr); uptime = new HASensor(upID); uptime->setName(HAupName); uptime->setDeviceClass(HAupClass); uptime->setUnitOfMeasurement("s"); sprintf(btnLID, HAbtnLID, macStr); btnleft = new HABinarySensor(btnLID); btnleft->setName(HAbtnLName); sprintf(updateID, HAupdateID, macStr); update = new HABinarySensor(updateID); update->setIcon(HAupdateIcon); update->setName(HAupdateName); update->setDeviceClass(HAupdateClass); sprintf(btnMID, HAbtnMID, macStr); btnmid = new HABinarySensor(btnMID); btnmid->setName(HAbtnMName); sprintf(btnRID, HAbtnRID, macStr); btnright = new HABinarySensor(btnRID); btnright->setName(HAbtnRName); sprintf(ramID, HAramRID, macStr); ram = new HASensor(ramID); ram->setDeviceClass(HAramClass); ram->setIcon(HAramIcon); ram->setName(HAramName); ram->setUnitOfMeasurement(HAramUnit); } else { Serial.println(F("Homeassistant discovery disabled")); mqtt.disableHA(); } connect(); } void MQTTManager_::tick() { if (MQTT_HOST != "") { mqtt.loop(); } } void MQTTManager_::publish(const char *topic, const char *payload) { if (!mqtt.isConnected()) return; char result[100]; strcpy(result, MQTT_PREFIX.c_str()); strcat(result, "/"); strcat(result, topic); mqtt.publish(result, payload, false); } void MQTTManager_::setCurrentApp(String value) { if (HA_DISCOVERY) curApp->setValue(value.c_str()); } void MQTTManager_::sendStats() { if (HA_DISCOVERY) { char buffer[5]; #ifdef ULANZI snprintf(buffer, 5, "%d", BATTERY_PERCENT); battery->setValue(buffer); #endif snprintf(buffer, 5, "%.0f", CURRENT_TEMP); temperature->setValue(buffer); snprintf(buffer, 5, "%.0f", CURRENT_HUM); humidity->setValue(buffer); snprintf(buffer, 5, "%.0f", CURRENT_LUX); illuminance->setValue(buffer); BriMode->setState(AUTO_BRIGHTNESS, true); Matrix->setBRIGHTNESS(BRIGHTNESS); Matrix->setState(!MATRIX_OFF, false); int8_t rssiValue = WiFi.RSSI(); char rssiString[4]; snprintf(rssiString, sizeof(rssiString), "%d", rssiValue); strength->setValue(rssiString); char rambuffer[10]; int freeHeapBytes = ESP.getFreeHeap(); itoa(freeHeapBytes, rambuffer, 10); ram->setValue(rambuffer); uptime->setValue(PeripheryManager.readUptime()); version->setValue(VERSION); transition->setState(AUTO_TRANSITION, false); update->setState(UPDATE_AVAILABLE, false); } else { } publish(StatsTopic, DisplayManager.getStat().c_str()); } void MQTTManager_::sendButton(byte btn, bool state) { static bool btn0State, btn1State, btn2State; switch (btn) { case 0: if (btn0State != state) { if (HA_DISCOVERY) btnleft->setState(state, false); btn0State = state; publish(ButtonLeftTopic, state ? State1 : State0); } break; case 1: if (btn1State != state) { if (HA_DISCOVERY) btnmid->setState(state, false); btn1State = state; publish(ButtonSelectTopic, state ? State1 : State0); } break; case 2: if (btn2State != state) { if (HA_DISCOVERY) btnright->setState(state, false); btn2State = state; publish(ButtonRightTopic, state ? State1 : State0); } break; default: break; } }