Skip to content

Commit

Permalink
Added HA MQTT support
Browse files Browse the repository at this point in the history
  • Loading branch information
jellewie committed Feb 28, 2024
1 parent 629e357 commit 99415f9
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 4 deletions.
10 changes: 10 additions & 0 deletions Arduino/Arduino.ino
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define OTA_SerialEnabled //OTA:
#define RGBL_SerialEnabled //RG:
#define Animation_SerialEnabled //AN
#define HomeAssistant_SerialEnabled //HA
//#define Audio_SerialEnabled //AU:
//#define LoopTime_SerialEnabled //LT:
//#define TimeExtra_SerialEnabled //TME:
Expand Down Expand Up @@ -100,6 +101,7 @@ StableAnalog LIGHT = StableAnalog(PAI_LIGHT);
StableAnalog AUDIO = StableAnalog(PAO_MIC);

#include "Functions.h"
#include "MQTT_HA.h"
#include "time.h" //We need this for the clock function to get the time (Time library)
#include "Task.h"
#include "WiFiManagerLater.h" //Define options of WiFiManager (can also be done before), but WiFiManager can also be called here (example for DoRequest)
Expand Down Expand Up @@ -176,6 +178,8 @@ void setup() {
#endif //SerialEnabled
loopLEDS();
UpdateBrightness(true); //Force Update the brightness
if (HA_MQTT_Enabled_On_Boot)
HA_MQTT_Enabled = true;
#ifdef SetupTime_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("ST: Setup took ms:\t" + String(millis()));
#endif //SetupTime_SerialEnabled
Expand All @@ -194,6 +198,12 @@ void loop() {
ExecuteTask();
if (AnimationCounter != 0) //Animation needs to be shown
ShowAnimation(false);
if (HA_MQTT_Enabled) { //If we need to talk to MQTT
if (HA_MQTT_Enabled_old != HA_MQTT_Enabled) //If this is the first time
HaSetup();
HaLoop();
}
HA_MQTT_Enabled_old = HA_MQTT_Enabled;
EVERY_N_MILLISECONDS(1000 / 60) { //Limit to 60FPS
Button_Time Value = ButtonsA.CheckButton(); //Read buttonstate
#ifdef SerialEnabled //DEBUG, print button state to serial
Expand Down
103 changes: 103 additions & 0 deletions Arduino/MQTT_HA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include <ArduinoHA.h> //https://github.com/dawidchyrzynski/arduino-home-assistant/tree/main
bool HA_MQTT_Enabled = false;
bool HA_MQTT_Enabled_old = HA_MQTT_Enabled;
bool HA_MQTT_Enabled_On_Boot = false;
IPAddress HA_BROKER_ADDR = IPAddress(0, 0, 0, 0);
String HA_BROKER_USERNAME = "";
String HA_BROKER_PASSWORD = "";

#define HA_deviceSoftwareVersion "1.0" //Device info - Firmware:
#define HA_deviceManufacturer "JelleWho" //Manufacturer
#define HA_deviceModel "Smart-light" //Model
#define HA_lightName1 "All" //Entity ID
#define HA_lightName2 "Outer" //Entity ID
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4B};
WiFiClient client;
HADevice device(mac, sizeof(mac));
HAMqtt mqtt(client, device);
HALight light1("Smart-all", HALight::BrightnessFeature | HALight::RGBFeature); //unique LighID
HALight light2("Smart-Outer", HALight::BrightnessFeature | HALight::RGBFeature); //unique LighID

void onBrightnessCommand(uint8_t brightness, HALight* sender) {
FastLED.setBrightness(brightness);
UpdateLEDs = true;
sender->setBrightness(brightness); // report brightness back to the Home Assistant
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Change light1 brightness = " + String(brightness));
#endif //HomeAssistant_SerialEnabled
}
void onStateCommand1(bool state, HALight* sender) {
if (state) {
LED_Fill(0, TotalLEDs, RGBColor); //Change the whole LED strip
Mode = WIFI;
} else {
LED_Fill(0, TotalLEDs, CRGB(0, 0, 0)); //Change the whole LED strip
//Mode = LastMode;
//LastMode = -1;
Mode = CLOCK;
AutoBrightness = true;
}
UpdateLEDs = true;
sender->setState(state); //Report state back to the Home Assistant
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Change light1 = " + String(state));
#endif //HomeAssistant_SerialEnabled
}
void onStateCommand2(bool state, HALight* sender) {
if (state) {
LED_Fill(TotalLEDsClock, TotalLEDs - TotalLEDsClock, RGBColor);//Change all NON-Clock LEDs
} else {
LED_Fill(TotalLEDsClock, TotalLEDs - TotalLEDsClock, CRGB(0, 0, 0));//Clear all NON-Clock LEDs
}
sender->setState(state); //Report state back to the Home Assistant
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Change light2 = " + String(state));
#endif //HomeAssistant_SerialEnabled
}
void onRGBColorCommand1(HALight::RGBColor color, HALight* sender) {
RGBColor = CRGB(color.red, color.green, color.blue);
LED_Fill(0, TotalLEDs, RGBColor); //Change the whole LED strip
UpdateLEDs = true;
sender->setRGBColor(color); //Report color back to the Home Assistant
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Change light1 color = " + String(color.red) + "," + String(color.green) + "," + String(color.blue));
#endif //HomeAssistant_SerialEnabled
}
void onRGBColorCommand2(HALight::RGBColor color, HALight* sender) {
RGBColor = CRGB(color.red, color.green, color.blue);
LED_Fill(TotalLEDsClock, TotalLEDs - TotalLEDsClock, RGBColor);//Change all NON-Clock LEDs
UpdateLEDs = true;
sender->setRGBColor(color); //Report color back to the Home Assistant
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Change light2 color = " + String(color.red) + "," + String(color.green) + "," + String(color.blue));
#endif //HomeAssistant_SerialEnabled
}

void HaLoop() {
mqtt.loop();
}
void HaSetup() {
device.setName(Name);
device.setSoftwareVersion(HA_deviceSoftwareVersion);
device.setManufacturer(HA_deviceManufacturer);
device.setModel(HA_deviceModel);
//device.setConfigurationUrl(IpAddress2String(WiFi.localIP()).c_str());

light1.setName(HA_lightName1);
light1.onStateCommand(onStateCommand1);
light1.onBrightnessCommand(onBrightnessCommand);
light1.onRGBColorCommand(onRGBColorCommand1);

light2.setName(HA_lightName2);
light2.onStateCommand(onStateCommand2);
light2.onBrightnessCommand(onBrightnessCommand);
light2.onRGBColorCommand(onRGBColorCommand2);

mqtt.begin(HA_BROKER_ADDR, HA_BROKER_USERNAME.c_str(), HA_BROKER_PASSWORD.c_str());
HaLoop();
light1.setState(LEDs[TotalLEDs - 1] == CRGB(0, 0, 0) ? false : true); //When booting, inform HA about the light state
light2.setState(LEDs[TotalLEDs - 1] == CRGB(0, 0, 0) ? false : true); //When booting, inform HA about the light state
#ifdef HomeAssistant_SerialEnabled //Just a way to measure setup speed, so the performance can be checked
Serial.println("HA: Informed HA about our pressence");
#endif //HomeAssistant_SerialEnabled
}
6 changes: 6 additions & 0 deletions Arduino/Structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ const byte Modes_Amount = sizeof(ModesString) / sizeof(ModesString[0]);//Why fil
String IpAddress2String(const IPAddress& ipAddress) {
return String(ipAddress[0]) + String(".") + String(ipAddress[1]) + String(".") + String(ipAddress[2]) + String(".") + String(ipAddress[3]) ;
}
IPAddress String2IpAddress(String ipString) {
IPAddress result;
if (result.fromString(ipString))
return result;
return IPAddress(0,0,0,0);
}
bool StringIsDigit(String IN, char IgnoreCharA = '0', char IgnoreCharB = '0');
bool StringIsDigit(String IN, char IgnoreCharA, char IgnoreCharB) {
//IgnoreChar can be used to ignore ',' or '.' or '-'
Expand Down
3 changes: 2 additions & 1 deletion Arduino/WiFiManagerBefore.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
#define WiFiManagerUser_VariableNames_Defined
const String WiFiManager_VariableNames[] = {"SSID", "Password", "BootMode", "HourlyAnimationS", "DoublePressMode", "AutoBrightness", "AutoBrightnessN", "AutoBrightnessP", "AutoBrightnessO", "ClockHourLines", "ClockHourAnalog", "LEDOffset", "ClockAnalog", "timeZone", "AudioLink", "PotMinChange", "PotStick", "PotMin", "Name",
"Task0", "Task1", "Task2", "Task3", "Task4", "Task5", "Task6", "Task7", "Task8", "Task9", "Task10", "Task11", "Task12", "Task13", "Task14", "Task15",
"AudioMultiplier", "AudioAddition", "MinAudioBrightness", "MaxAudioBrightness", "AmountAudioAverageEnd"
"AudioMultiplier", "AudioAddition", "MinAudioBrightness", "MaxAudioBrightness", "AmountAudioAverageEnd",
"HABrokerIP", "HABrokerUser", "HABrokerPass", "HABrokerEnabledOnBoot"
};
const int EEPROM_size = 512;

Expand Down
19 changes: 19 additions & 0 deletions Arduino/WiFiManagerLater.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ bool WiFiManagerUser_Set_Value(byte ValueID, String Value) {
if (not StringIsDigit(Value)) return false; //No number given
AmountAudioAverageEnd = ToByte(Value); return true;
} break;
case 38: {
if (HA_MQTT_Enabled) return false; //Can only be changed before MQTT is enabled/settup
IPAddress result = String2IpAddress(Value);
if (result == IPAddress(0, 0, 0, 0)) return false; //No valid IP
HA_BROKER_ADDR = result; return true;
} break;
case 39: {
if (HA_MQTT_Enabled) return false; //Can only be changed before MQTT is enabled/settup
HA_BROKER_USERNAME = Value; return true;
} break;
case 40: {
if (HA_MQTT_Enabled) return false; //Can only be changed before MQTT is enabled/settup
HA_BROKER_PASSWORD = Value; return true;
} break;
case 41: HA_MQTT_Enabled_On_Boot = IsTrue(Value); return true; break;
//==============================
//Tasks
//==============================
Expand Down Expand Up @@ -147,6 +162,10 @@ String WiFiManagerUser_Get_Value(byte ValueID, bool Safe, bool Convert) {
case 35: return String(MinAudioBrightness); break;
case 36: return String(MaxAudioBrightness); break;
case 37: return String(AmountAudioAverageEnd); break;
case 38: return IpAddress2String(HA_BROKER_ADDR); break;
case 39: return HA_BROKER_USERNAME; break;
case 40: return HA_BROKER_PASSWORD; break;
case 41: return Convert ? IsTrueToString(HA_MQTT_Enabled_On_Boot) : String(HA_MQTT_Enabled_On_Boot); break;
//==============================
//Tasks
//==============================
Expand Down
21 changes: 18 additions & 3 deletions Arduino/handler.ino
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#define PreFixSetClockHourAnalog "a"
#define PreFixSetLEDOffset "o"
#define PreFixSetClockAnalog "c"
#define PreFixSection "s" //''or'0'=All, 1=TotalLEDsClock, 2=!(TotalLEDsClock)
#define PreFixSection "s" //''or'0'=All, 1=TotalLEDsClock, 2=!(TotalLEDsClock)
#define PreFixAudioLink "d"
#define PreFixHAMQTT "h"
#define PreFixHAMQTTOnBoot "he"

//<ip>/time[?PreFix=Value][&....] //These are currently HARDCODED into the HTML page, so shouldn't be changed if you want to use the webpage
#define PreFixTimeHour "h"
Expand Down Expand Up @@ -104,6 +106,11 @@ void handle_Set() {
DoWriteToEEPROM = true;
} else if (ArguName == PreFixSection) {
Section = ArgValue.toInt();
} else if (ArguName == PreFixHAMQTT) {
HA_MQTT_Enabled = IsTrue(ArgValue);
} else if (ArguName == PreFixHAMQTTOnBoot) {
HA_MQTT_Enabled_On_Boot = IsTrue(ArgValue);
DoWriteToEEPROM = true;
} else
ERRORMSG += "Unknown arg '" + ArguName + "' with value '" + ArgValue + "'\n";
}
Expand Down Expand Up @@ -151,7 +158,7 @@ void handle_Set() {
FromLED = TotalLEDsClock + 1;
AmountLED = TotalLEDs - FromLED;
}
fill_solid(&(LEDs[FromLED]), AmountLED, RGBColor); //Change the whole LED strip to have the color of the last set LED
LED_Fill(FromLED, AmountLED, RGBColor); //Change the whole LED strip to have the color of the last set LED
} else if (Mode == RESET) {
server.send(200, "text/plain", "OK");
for (int i = 0; i < 100; i++) { //Just wait for a few ms to make sure the "reset command recieved" has been send
Expand Down Expand Up @@ -180,7 +187,9 @@ void handle_Getcolors() {
"\"hl\":\"" + ClockHourLines + "\","
"\"a\":\"" + IsTrueToString(ClockHourAnalog) + "\","
"\"c\":\"" + IsTrueToString(ClockAnalog) + "\","
"\"d\":\"" + IsTrueToString(AudioLink) + "\",";
"\"d\":\"" + IsTrueToString(AudioLink) + "\","
"\"h\":\"" + IsTrueToString(HA_MQTT_Enabled) + "\","
"\"he\":\"" + IsTrueToString(HA_MQTT_Enabled_On_Boot) + "\",";
byte r = LEDs[TotalLEDs - 1].r, g = LEDs[TotalLEDs - 1].g, b = LEDs[TotalLEDs - 1].b; //Set RGB to be the color of the last LED
if (AnimationCounter != 0) { //Animation needs to be shown (this is used to show the set animation color)
r = AnimationRGB[0];
Expand Down Expand Up @@ -268,6 +277,9 @@ void handle_Main() {
"let Dhl=new DropDown({name:'Hourly lines',setParamName:'hl',possibleValues:['FALSE','1','2','4','8','16','32']});" //This one is actually a Byte
"let Da=new DropDown({name:'Analog hours',setParamName:'a',possibleValues:['FALSE','TRUE']});"
"let Dc=new DropDown({name:'Analog clock',setParamName:'c',possibleValues:['FALSE','TRUE']});"
"let Dq=new DropDown({name:'HA MQTT',setParamName:'h',possibleValues:['FALSE','TRUE']});"
"let De=new DropDown({name:'HA MQTT after boot',setParamName:'he',possibleValues:['FALSE','TRUE']});"


"settingsContainer.appendChild(document.createElement('br'));"
"new Button('Tasks', _ =>{setSettingsVisibility(false); setTasksVisibility(true);});"
Expand Down Expand Up @@ -313,6 +325,9 @@ void handle_Main() {
"Dhl.value=json.hl;"
"Da.value=json.a;"
"Dc.value=json.c;"
"Dq.value=json.h;"
"De.value=json.he;"

"let col=json.RGBL[0];Sr.value=col.R;Sg.value=col.G;Sb.value=col.B;Sl.value=col.L;}"

"async function updateTaskList(){let response=await fetch('/gettasks');if(response.ok){await updateTaskListFromResponse(response);}else{doToastMessage(await response.text(),true);}}let currentTaskListTimeItems=[];async function updateTaskListFromResponse(response){if(!response.ok)return;const json=await response.json();taskListEl.innerHTML='';currentTaskListTimeItems=[];for(const task of json.tasks){const liEl=document.createElement('li');liEl.classList.add('taskItem');taskListEl.appendChild(liEl);const timeEl=document.createElement('div');timeEl.classList.add('taskTime');timeEl.innerText=task.time;liEl.appendChild(timeEl);if(task.timeFromNow){currentTaskListTimeItems.push({el:timeEl,fireTime:performance.now()+task.timeFromNow,});}const typeEl=document.createElement('div');typeEl.classList.add('taskType');typeEl.innerText=task.type;liEl.appendChild(typeEl);const varEl=document.createElement('div');varEl.classList.add('taskVar');if(task.var){varEl.innerText='Var: '+task.var;}liEl.appendChild(varEl);const closeBtn=document.createElement('div');closeBtn.classList.add('taskCloseBtn');liEl.appendChild(closeBtn);closeBtn.addEventListener('click',async _=>{const response=await fetch('/settask?o=2&i='+task.id);doToastMessage(await response.text(), !response.ok);await updateTaskList();});}}function updateTaskTimes(){for(const timeItem of currentTaskListTimeItems){const dt=timeItem.fireTime-performance.now();let str='';if(dt<0){str='now';}else{const dtSeconds=dt/1000;const dtMinutes=dtSeconds/60;const dtHours=dtMinutes/60;const hours=Math.floor(dtHours);const minutes=Math.floor(dtMinutes-hours*60);const seconds=Math.floor(dtSeconds-hours*60*60-minutes*60);str=(''+seconds).padStart(2,'0');if(hours>0){str=(''+minutes).padStart(2,'0')+':'+str;str=hours+':'+str;}else{str=minutes+':'+str;}}timeItem.el.textContent=str;}}setInterval(updateTaskTimes,100);const addTaskBtn=new Button('add',async _=>{const params={i:addTaskTypeEl.value,a:addTaskVarEl.value,};if(addTaskTimeTypeEl.value==\"ABSOLUTE\"){params.h=addTaskTimeEl.valueAsDate.getUTCHours();params.m=addTaskTimeEl.valueAsDate.getUTCMinutes();params.s=addTaskTimeEl.valueAsDate.getUTCSeconds();}else{const ticks=addTaskTimeEl.valueAsNumber;params.t=ticks;}let searchParams=new URLSearchParams(params);try{let response=await fetch('/settask?o=1&'+searchParams);doToastMessage(await response.text(),!response.ok);}catch(e){doToastMessage('Failed to add task',true);}updateTaskList();});addTaskVarEl.parentElement.appendChild(addTaskBtn.el);"
Expand Down

0 comments on commit 99415f9

Please sign in to comment.