Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add home assistant integration #66

Merged
merged 8 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ build_src_filter = +<*> -<./angular/>
board_build.filesystem = littlefs
check_tool = cppcheck, clangtidy
build_flags =
-D OLED_ENABLED=false
-D OLED_ENABLED=true
-D PWM_MOTOR_CONTROL=false
-D HOME_ASSISTANT_ENABLED=true
mwood77 marked this conversation as resolved.
Show resolved Hide resolved
check_flags =
clangtidy: -fix-errors,--format-style=google
lib_deps =
Expand All @@ -34,3 +35,4 @@ lib_deps =
fbiego/ESP32Time@^2.0.0
adafruit/Adafruit SSD1306@^2.5.9
electromagus/ESPMX1508@^1.0.5
dawidchyrzynski/home-assistant-integration@^2.1.0
222 changes: 221 additions & 1 deletion src/platformio/osww-server/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ bool OLED_ROTATE_SCREEN_180 = false;
int SCREEN_WIDTH = 128; // OLED display width, in pixels
int SCREEN_HEIGHT = 64; // OLED display height, in pixels
int OLED_RESET = -1; // Reset pin number (or -1 if sharing Arduino reset pin)

// Home Assistant Configuration
char* HOME_ASSISTANT_BROKER_IP = "YOUR_HOME_ASSISTANT_IP";
char* HOME_ASSISTANT_USERNAME = "YOUR_HOME_ASSISTANT_LOGIN_USERNAME";
char* HOME_ASSISTANT_PASSWORD = "YOUR_HOME_ASSISTANT_LOGIN_PASSWORD";
/*
* *************************************************************************************
* ******************************* END CONFIGURABLES ***********************************
Expand Down Expand Up @@ -100,6 +105,21 @@ ESP32Time rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#endif

#ifdef HOME_ASSISTANT_ENABLED
#include <ArduinoHA.h>

HADevice device;
HAMqtt mqtt(client, device);

// Define HA Sensors
HASwitch ha_oledSwitch("oled");
HANumber ha_rpd("rpd");
HASelect ha_select("direction");
HASwitch ha_timerSwitch("timerEnabled");
HAButton ha_startButton("startButton");
HAButton ha_stopButton("stopButton");
#endif

void drawCentreStringToMemory(const char *buf, int x, int y)
{
int16_t x1, y1;
Expand Down Expand Up @@ -261,6 +281,23 @@ template <int N> static void drawMultiLineText(const String (&message)[N]) {
}
}

// Home Assistant Helper Function
int getDirectionIndexForHomeAssistant(String direction)
{
if (direction == "CCW")
{
return 0;
}
else if (direction == "BOTH")
{
return 1;
}
else
{
return 2;
}
}

/**
* Calclates the duration and estimated finish time of the winding routine
*
Expand Down Expand Up @@ -473,6 +510,7 @@ void startWebserver()
if( strcmp(p->name().c_str(), "timerEnabled") == 0 )
{
userDefinedSettings.timerEnabled = p->value().c_str();
ha_timerSwitch.setState(userDefinedSettings.timerEnabled.toInt());
}
}

Expand Down Expand Up @@ -552,12 +590,21 @@ void startWebserver()
userDefinedSettings.minutes = json["minutes"].as<String>();
userDefinedSettings.timerEnabled = json["timerEnabled"].as<String>();

// Update Home Assistant State
ha_timerSwitch.setState(userDefinedSettings.timerEnabled.toInt());

// These values need to be compared to the current settings / running state
String requestRotationDirection = json["rotationDirection"].as<String>();
String requestTPD = json["tpd"].as<String>();
String requestAction = json["action"].as<String>();
screenSleep = json["screenSleep"].as<bool>();

// Update Home Assistant state
ha_oledSwitch.setState(!screenSleep); // Invert state because naming is hard...
ha_rpd.setState(static_cast<int>(requestTPD.toInt()));

ha_select.setState(getDirectionIndexForHomeAssistant(requestRotationDirection));

// Update motor direction
if (strcmp(requestRotationDirection.c_str(), userDefinedSettings.direction.c_str()) != 0)
{
Expand Down Expand Up @@ -767,11 +814,116 @@ void saveWifiCallback()
delay(1500);
}

// MQTT & Home Assistant Handlers
void mqttOnConnected()
{
Serial.println("[STATUS] - MQTT connected!");
}

void mqttOnDisconnected()
{
Serial.println("[STATUS] - MQTT disconnected!");
}

void onOledSwitchCommand(bool state, HASwitch* sender)
{
if (state)
{
screenSleep = false;
display.clearDisplay();
drawStaticGUI(true);
drawDynamicGUI();
}
else
{
screenSleep = true;
display.clearDisplay();
display.display();
}

// report state back to the Home Assistant
sender->setState(state);
}

void onRpdChangeCommand(HANumeric number, HANumber* sender)
{
char buffer[10];
number.toStr(buffer);
userDefinedSettings.rotationsPerDay = String(buffer);

bool writeSuccess = writeConfigVarsToFile(settingsFile, userDefinedSettings);
if ( !writeSuccess )
{
Serial.println("[ERROR] - Failed to write number state [MQTT]");
}

sender->setCurrentState(number);
}

void onSelectDirectionCommand(int8_t index, HASelect* sender) {
switch (index) {
case 0:
// Option "CCW" was selected
userDefinedSettings.direction = "CCW";
break;

case 1:
// Option "BOTH" was selected
userDefinedSettings.direction = "BOTH";
break;

case 2:
// Option "CW" was selected
userDefinedSettings.direction = "CW";
break;

default:
// unknown option
return;
}

bool writeSuccess = writeConfigVarsToFile(settingsFile, userDefinedSettings);
if ( !writeSuccess )
{
Serial.println("[ERROR] - Failed to write direction select state [MQTT]");
}

sender->setState(index);
}

void onTimerSwitchCommand(bool state, HASwitch* sender)
{
userDefinedSettings.timerEnabled = state ? "1" : "0";
bool writeSuccess = writeConfigVarsToFile(settingsFile, userDefinedSettings);
if ( !writeSuccess )
{
Serial.println("[ERROR] - Failed to write timer switch state [MQTT]");
}

sender->setState(state);
}

void handleHAStartButton(HAButton* sender)
{
if (!routineRunning)
{
beginWindingRoutine();
}
}

void handleHAStopButton(HAButton* sender)
{
motor.stop();
routineRunning = false;
userDefinedSettings.status = "Stopped";
drawNotification("Stopped");
}

void setup()
{
WiFi.mode(WIFI_STA);
Serial.begin(115200);
setCpuFrequencyMhz(80);
setCpuFrequencyMhz(240);

// Prepare pins
pinMode(directionalPinA, OUTPUT);
Expand Down Expand Up @@ -827,14 +979,77 @@ void setup()
}
MDNS.addService("_winderoo", "_tcp", 80);
Serial.println("[STATUS] - mDNS started");

// Configure Home Assistant
if (HOME_ASSISTANT_ENABLED)
{
byte mac[6];
WiFi.macAddress(mac);
device.setUniqueId(mac, sizeof(mac));

device.setName("Winderoo");
device.setManufacturer("mwood77");
device.setModel("Winderoo");
device.setSoftwareVersion("1.0.0");
device.enableSharedAvailability();

ha_oledSwitch.setName("OLED");
ha_oledSwitch.setIcon("mdi:overscan");
ha_oledSwitch.setCurrentState(!screenSleep);
ha_oledSwitch.onCommand(onOledSwitchCommand);

ha_rpd.setName("Rotations Per Day");
ha_rpd.setIcon("mdi:rotate-3d-variant");
ha_rpd.setMin(100);
ha_rpd.setMax(960);
ha_rpd.setStep(10);
ha_rpd.setCurrentState(static_cast<int32_t>(userDefinedSettings.rotationsPerDay.toInt()));
ha_rpd.setOptimistic(true);
ha_rpd.onCommand(onRpdChangeCommand);

ha_select.setName("Direction");
ha_select.setIcon("mdi:arrow-left-right");
ha_select.setOptions("CCW;BOTH;CW");
ha_select.onCommand(onSelectDirectionCommand);
ha_select.setCurrentState(getDirectionIndexForHomeAssistant(userDefinedSettings.direction));

ha_timerSwitch.setName("Timer Enabled");
ha_timerSwitch.setIcon("mdi:timer");
ha_timerSwitch.setCurrentState(userDefinedSettings.timerEnabled.toInt());
ha_timerSwitch.onCommand(onTimerSwitchCommand);

ha_startButton.setName("Start");
ha_startButton.setIcon("mdi:play");
ha_startButton.onCommand(handleHAStartButton);

ha_stopButton.setName("Stop");
ha_stopButton.setIcon("mdi:stop");
ha_stopButton.onCommand(handleHAStopButton);

mqtt.onConnected(mqttOnConnected);
mqtt.onDisconnected(mqttOnDisconnected);
mqtt.begin(HOME_ASSISTANT_BROKER_IP, HOME_ASSISTANT_USERNAME, HOME_ASSISTANT_PASSWORD);
Serial.println("[STATUS] - HA Configured - Will attempt to connect to MQTT broker");

if (OLED_ENABLED)
{
String configuredHomeAssistantMessage[3] = {"Connfigured", "for", "Home Assistant"};
drawMultiLineText(configuredHomeAssistantMessage);
delay(1500);
}
}

if (OLED_ENABLED)
{
display.clearDisplay();
drawStaticGUI();
drawNotification("Connected to WiFi");
}

drawNotification("Getting time...");
getTime();

drawNotification("Starting webserver...");
startWebserver();

if (strcmp(userDefinedSettings.status.c_str(), "Winding") == 0)
Expand Down Expand Up @@ -971,5 +1186,10 @@ void loop()
drawDynamicGUI();
}

if (HOME_ASSISTANT_ENABLED)
{
mqtt.loop();
}

wm.process();
}
Loading