Skip to content

Commit

Permalink
Switch HomeAssistant authentication to OAuth2 (#61)
Browse files Browse the repository at this point in the history
Adds #58 

Also completes #40
  • Loading branch information
aeroniemi authored May 29, 2024
1 parent 834cc21 commit d710d00
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 198 deletions.
26 changes: 6 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,17 @@ A HomeAssistant device controller built into a cute 1.28" dial.
- Lights (brightness, colour, on/off)
- Switches (on/off)
- More may be added if requested...
- Create timers which alert using the on-board buzzer
- Automatically identifies entities from your Home Assistant install - no per-device configuration required
- Clock with timezone and daylight savings time correction
- Captive portal for configuration
- Little configuration required - authenticate in your browser with HomeAssistant

## Installation
*The software is only compatible with the [M5Dial by M5Stack](https://shop.m5stack.com/products/m5stack-dial-esp32-s3-smart-rotary-knob-w-1-28-round-touch-screen), but it would be relatively trivial to port it to other ESP32-based dials and knobs.*
- Download the latest binary (firmware_merged.bin) from [Releases](https://github.com/aeroniemi/circlehome/releases
- Open a ESPTool instance (such as [this online one from Adafruit](https://adafruit.github.io/Adafruit_WebSerial_ESPTool/))
- Erase your ESP32
- Write ``firmware_merged.bin`` at ``0x0``
- Restart your ESP

You should now be good to setup your device!

## Setup
After installing the firmware, you need to set the WiFi credentials, home assistant credentials and more. To make this as easy as possible CircleHome will create a WiFi hotspot for you to join:
- On initial startup, you'll see a green configuration message. Click "Setup"
- Connect to the open WiFi network ``M5Dial``
- Navigate to ``http://192.168.0.1``
- Fill out the details in the form.
- Note that timezone is case sensitive, and must be one of the values found [here](https://github.com/rzeldent/micro-timezonedb/blob/main/include/timezonedb.h)
- Press submit on the form
- Wait a few moments, then click ``restart`` on your dial.

The device should now be configured.
- Flash the latest release at [https://aeroniemi.github.io/circlehome/]
- Set the WiFi details using the tool (Improv Serial/WiFi)
- Visit the device URL to authenticate with your Home Assistant instance
- You're good to go!

## Operation
The interface of CircleHome should be fairly intuitive, but here's a few tips:
Expand Down
4 changes: 2 additions & 2 deletions lib/aero_error_handling/aero_error_handling.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <cppQueue.h>
#include <Arduino.h>
#include <lvgl.h>
#include <aero_web_portal.h>
// #include <aero_web_portal.h>
#define ERROR_BUTTON_LEFT 0
#define ERROR_BUTTON_RIGHT 1
enum UIMessageLevel
Expand Down Expand Up @@ -125,7 +125,7 @@ class UINeedsSetup : public UIMessageBase
};
void handleLeftButton() override
{
aero_web_portal_setup();
// aero_web_portal_setup();
};
void handleRightButton() override
{
Expand Down
9 changes: 1 addition & 8 deletions lib/aero_preferences/aero_preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,16 @@ UINeedsSetup error_needs_setup(F("Settings are not initialized"));
void setupPreferences()
{
log_d("Setting up preferences");
settings.begin("settings", true);
settings.begin("settings", false);
}
void resetPreferences()
{
log_d("Resetting preferences");
settings.end();
settings.begin("settings", false);
settings.clear();
settings.end();
setupPreferences();
}

void storeImprovSettings(const char *ssid, const char *password) {
settings.end();
settings.begin("settings", false);
settings.putString("wifi_ssid", ssid);
settings.putString("wifi_password", password);
settings.end();
settings.begin("settings", true);
}
141 changes: 0 additions & 141 deletions lib/aero_web_portal/aero_web_portal.cpp

This file was deleted.

6 changes: 0 additions & 6 deletions lib/aero_web_portal/aero_web_portal.h

This file was deleted.

2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ lib_deps =
lvgl/lvgl@>=9.1.0
smfsw/Queue@1.11
rzeldent/micro-timezonedb@1.0.3
Arduino-HomeAssistant=https://github.com/aeroniemi/arduino-homeassistant.git
Arduino-HomeAssistant=https://github.com/aeroniemi/arduino-homeassistant.git#594d357c69085c289e3d22d53c9af11f51f6857a
M5Dial-LVGL=https://github.com/aeroniemi/M5Dial-LVGL.git#v0.1.0
jnthas/Improv WiFi Library@0.0.2
extra_scripts =
Expand Down
74 changes: 61 additions & 13 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
#include "sys/aero_time.h"
#include <cppQueue.h>
#include <aero_error_handling.h>
#include <aero_web_portal.h>
#include "classes/Clock.h"
#include <ImprovWiFiLibrary.h>

#include <WiFi.h>
#include <WebServer.h>
#include <timezonedb_lookup.h>
ImprovWiFi improvSerial(&Serial);
HomeAssistant *ha;
HomeAssistant has;
HomeAssistant *ha = &has;
WebServer server(80);

Screen *global_screens[] = {
&screen_on_off,
Expand All @@ -31,6 +34,16 @@ void log_cb(lv_log_level_t level, const char *buf)
{
Serial.println(buf);
}
void update_timezone() {
String timezone = ha->getTimezone();
log_d("Timezone: %s", timezone);
if (timezone.length() ==0)
return;
String tz = lookup_posix_timezone_tz(timezone.c_str());
settings.putString("ntp_timezone", timezone);
settings.putString("ntp_tz_str", tz);
setupTime();
};

void initializeScreens()
{
Expand All @@ -55,10 +68,38 @@ void setupWifi(void *params)
}
vTaskDelete(NULL);
}

void setup_web_server()
{
server.on("/", []()
{ server.sendHeader("Location", String("http://homeassistant.local:8123/auth/authorize?client_id=http%3A%2F%2F" + WiFi.localIP().toString() + "&redirect_uri=http%3A%2F%2F" + WiFi.localIP().toString() + "/auth_callback"), true);
server.send(302, "text/plain", ""); });
server.on("/auth_callback", []()
{
for (int i = 0; i < server.args(); i++)
{
log_d("%s, %s", server.argName(i), server.arg(i));
if (server.argName(i).equals("code")) {
String access_code = server.arg(i);
String refresh_token = ha->requestRefreshToken(access_code);
if (refresh_token.length()==0) {
log_d("Error getting refresh token");
//error
};
settings.putString("ha_refresh", refresh_token);
// ESP.restart();
};
};

server.send(200, "text/plain", "Success"); });
server.begin();
};
bool initialized = false;
bool initialized_ha = false;
void setup()
{
Serial.begin(115200);
sleep(2);
setupPreferences();
xTaskCreatePinnedToCore(
setupWifi, /* Function to implement the task */
Expand All @@ -70,6 +111,8 @@ void setup()
0); /* Core where the task should run */
improvSerial.onImprovConnected(*storeImprovSettings);
improvSerial.setDeviceInfo(ImprovTypes::ChipFamily::CF_ESP32_S3, "CircleHome", "1.0.0", "My Device");
ha->setHost(settings.getString("ha_hostname", "homeasssistant.local"));
ha->setPort(settings.getInt("ha_port", 8123));
Serial.setDebugOutput(true);
m5dial_lvgl_init();
M5Dial.Display.setBrightness(70);
Expand All @@ -78,33 +121,38 @@ void setup()
m5dial_lvgl_next();
ui_init();
setupTime();
setup_web_server();
}

void loop()
{
improvSerial.handleSerial();
m5dial_lvgl_next();
if (aero_web_server_enabled)
server.handleClient();
// monitor_sleep();
server.handleClient();
monitor_sleep();

if (initialized)
{
clock_timer.update();
// log_d("HA is setup? %d %s", ha->isSetup(), settings.getString("ha_refresh", "none"));
if (ha->isSetup() and not initialized_ha)
{
log_d("Initializing HA");
ha->createEntities();
ha->updateAllStates();
initialized_ha = true;
};
}
else
{
if (improvSerial.isConnected())
{
log_d("Connected, continue");
ha = new HomeAssistant(
settings.getString("ha_hostname", "homeasssistant.local"),
settings.getString("ha_token", ""),
settings.getInt("ha_port", 8123));

ha->setRefreshToken(settings.getString("ha_refresh", ""));
log_d("access token: '%s'", ha->getToken().c_str());
initializeScreens();
ha->createEntities();
ha->updateAllStates();
screen_main_menu.makeActive();
update_timezone();
initialized = true;
}
};
Expand Down
1 change: 1 addition & 0 deletions src/ui/screens/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Screen
Screen(){};
virtual void makeActive() {
if (lv_screen_active() != _lv_screen)
load(NULL);
lv_screen_load(_lv_screen);
};
virtual bool isActive() { return lv_screen_active() == _lv_screen; };
Expand Down
Loading

0 comments on commit d710d00

Please sign in to comment.