diff --git a/factory_settings.ini b/factory_settings.ini index b9fbd60..065d72d 100644 --- a/factory_settings.ini +++ b/factory_settings.ini @@ -13,8 +13,8 @@ build_flags = ; Access point settings -D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED - -D FACTORY_AP_SSID=\"ESP8266-React-#{unique_id}\" ; 1-64 characters, supports placeholders - -D FACTORY_AP_PASSWORD=\"esp-react\" ; 8-64 characters + -D FACTORY_AP_SSID=\"WordClock-#{unique_id}\" ; 1-64 characters, supports placeholders + -D FACTORY_AP_PASSWORD=\"wordclock\" ; 8-64 characters -D FACTORY_AP_CHANNEL=1 -D FACTORY_AP_SSID_HIDDEN=false -D FACTORY_AP_MAX_CLIENTS=4 @@ -30,7 +30,7 @@ build_flags = ; NTP settings -D FACTORY_NTP_ENABLED=true - -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/London\" + -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Vienna\" -D FACTORY_NTP_TIME_ZONE_FORMAT=\"GMT0BST,M3.5.0/1,M10.5.0\" -D FACTORY_NTP_SERVER=\"time.google.com\" diff --git a/interface/src/project/DemoProject.tsx b/interface/src/project/DemoProject.tsx index 74f25e5..03bd105 100644 --- a/interface/src/project/DemoProject.tsx +++ b/interface/src/project/DemoProject.tsx @@ -8,9 +8,7 @@ import { MenuAppBar } from '../components'; import { AuthenticatedRoute } from '../authentication'; import DemoInformation from './DemoInformation'; -import LightStateRestController from './LightStateRestController'; import LightStateWebSocketController from './LightStateWebSocketController'; -import LightMqttSettingsController from './LightMqttSettingsController'; class DemoProject extends Component { @@ -20,18 +18,14 @@ class DemoProject extends Component { render() { return ( - + - - - + - - diff --git a/interface/src/project/LightMqttSettingsController.tsx b/interface/src/project/LightMqttSettingsController.tsx deleted file mode 100644 index 7e4db5c..0000000 --- a/interface/src/project/LightMqttSettingsController.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { Component } from 'react'; -import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator'; - -import { Typography, Box } from '@material-ui/core'; -import SaveIcon from '@material-ui/icons/Save'; - -import { ENDPOINT_ROOT } from '../api'; -import { restController, RestControllerProps, RestFormLoader, RestFormProps, FormActions, FormButton, SectionContent } from '../components'; - -import { LightMqttSettings } from './types'; - -export const LIGHT_BROKER_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "brokerSettings"; - -type LightMqttSettingsControllerProps = RestControllerProps; - -class LightMqttSettingsController extends Component { - - componentDidMount() { - this.props.loadData(); - } - - render() { - return ( - - ( - - )} - /> - - ) - } - -} - -export default restController(LIGHT_BROKER_SETTINGS_ENDPOINT, LightMqttSettingsController); - -type LightMqttSettingsControllerFormProps = RestFormProps; - -function LightMqttSettingsControllerForm(props: LightMqttSettingsControllerFormProps) { - const { data, saveData, handleValueChange } = props; - return ( - - - - The LED is controllable via MQTT with the demo project designed to work with Home Assistant's auto discovery feature. - - - - - - - } variant="contained" color="primary" type="submit"> - Save - - - - ); -} diff --git a/interface/src/project/LightStateRestController.tsx b/interface/src/project/LightStateRestController.tsx deleted file mode 100644 index 764ce35..0000000 --- a/interface/src/project/LightStateRestController.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { Component } from 'react'; -import { ValidatorForm } from 'react-material-ui-form-validator'; - -import { Typography, Box, Checkbox } from '@material-ui/core'; -import SaveIcon from '@material-ui/icons/Save'; - -import { ENDPOINT_ROOT } from '../api'; -import { restController, RestControllerProps, RestFormLoader, RestFormProps, FormActions, FormButton, SectionContent, BlockFormControlLabel } from '../components'; - -import { LightState } from './types'; - -export const LIGHT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "lightState"; - -type LightStateRestControllerProps = RestControllerProps; - -class LightStateRestController extends Component { - - componentDidMount() { - this.props.loadData(); - } - - render() { - return ( - - ( - - )} - /> - - ) - } - -} - -export default restController(LIGHT_SETTINGS_ENDPOINT, LightStateRestController); - -type LightStateRestControllerFormProps = RestFormProps; - -function LightStateRestControllerForm(props: LightStateRestControllerFormProps) { - const { data, saveData, handleValueChange } = props; - return ( - - - - The form below controls the LED via the RESTful service exposed by the ESP device. - - - - } - label="LED State?" - /> - - } variant="contained" color="primary" type="submit"> - Save - - - - ); -} diff --git a/interface/src/project/ProjectMenu.tsx b/interface/src/project/ProjectMenu.tsx index b7d2739..882a51d 100644 --- a/interface/src/project/ProjectMenu.tsx +++ b/interface/src/project/ProjectMenu.tsx @@ -16,7 +16,7 @@ class ProjectMenu extends Component { - + ) diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index 3221255..df45ca0 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -1,9 +1,3 @@ export interface LightState { led_on: boolean; -} - -export interface LightMqttSettings { - unique_id : string; - name: string; - mqtt_path : string; -} +} \ No newline at end of file diff --git a/src/Clock.cpp b/src/Clock.cpp index 0f498f8..5c4a53f 100644 --- a/src/Clock.cpp +++ b/src/Clock.cpp @@ -13,7 +13,19 @@ void Clock::init() { strip.clear(); strip.show(); - refreshTicker.attach(10, [this]() { this->refreshTime(); }); + turnOn(); +} + +void Clock::turnOff() { + strip.clear(); + strip.show(); + + refreshTicker.detach(); +} + +void Clock::turnOn() { + if (!refreshTicker.active()) + refreshTicker.attach(10, [this]() { this->refreshTime(); }); this->refreshTime(); } @@ -22,7 +34,7 @@ void Clock::paintAllwaysOnLeds() { printWord(types::ist, Adafruit_NeoPixel::Color(150, 0, 150)); } -void Clock::printWord(std::vector word, uint32_t color) { +void Clock::printWord(const std::vector& word, uint32_t color) { for (const int i : word) { strip.setPixelColor(i, color); } @@ -46,12 +58,7 @@ void Clock::refreshTime() { paintAllwaysOnLeds(); setTime(hour, minute); strip.show(); - - Serial.print("time now: "); - Serial.print(hour); - Serial.print("h "); - Serial.print(minute); - Serial.println("M"); + Serial.printf("Time now: %sh %sM", &hour, &minute); } void Clock::setTime(uint8_t hour, uint8_t minute) { @@ -65,11 +72,10 @@ void Clock::setTime(uint8_t hour, uint8_t minute) { if (hour >= 12) hour = hour - 12; - std::vector hourWord; - - hourWord = hour == 1 ? types::ein + std::vector hourWord; + hourWord = hour == 1 ? (minuteselector == 0 ? types::ein : types::eins) // eins only on a full hour : hour == 2 ? types::zwei - : hour == 3 ? types::drei + : hour == 3 ? types::drei2 : hour == 4 ? types::vier : hour == 5 ? types::fuenf2 : hour == 6 ? types::sechs @@ -84,7 +90,7 @@ void Clock::setTime(uint8_t hour, uint8_t minute) { printWord(hourWord, Adafruit_NeoPixel::Color(0, 150, 0)); // fuenf / zehn / viertl word - std::vector minuteWord; + std::vector minuteWord; if (minuteselector == 1 || minuteselector == 5 || minuteselector == 7 || minuteselector == 11) minuteWord = types::fuenf; else if (minuteselector == 2 || minuteselector == 4 || minuteselector == 8 || minuteselector == 10) @@ -99,7 +105,7 @@ void Clock::setTime(uint8_t hour, uint8_t minute) { printWord(minuteWord, Adafruit_NeoPixel::Color(0, 150, 0)); // vor / nach - std::vector vornachWord; + std::vector vornachWord; if (minuteselector == 1 || minuteselector == 2 || minuteselector == 3 || minuteselector == 7 || minuteselector == 8) vornachWord = types::nach; else if (minuteselector == 4 || minuteselector == 5 || minuteselector == 10 || minuteselector == 11) diff --git a/src/Clock.h b/src/Clock.h index cfe5ed5..b42a45a 100644 --- a/src/Clock.h +++ b/src/Clock.h @@ -18,13 +18,16 @@ class Clock { LoadAnimator animator; void paintAllwaysOnLeds(); - void printWord(std::vector word, uint32_t color); + void printWord(const std::vector& word, uint32_t color); void setTime(uint8_t hour, uint8_t minute); + void refreshTime(); + public: Clock(); void init(); - void refreshTime(); + void turnOff(); + void turnOn(); Ticker refreshTicker; }; diff --git a/src/ClockService.cpp b/src/ClockService.cpp index c8896f8..b9c348d 100644 --- a/src/ClockService.cpp +++ b/src/ClockService.cpp @@ -5,34 +5,36 @@ #include "ClockService.h" ClockService::ClockService(AsyncWebServer* server, SecurityManager* securityManager) : - _httpEndpoint(LightState::read, - LightState::update, + _httpEndpoint(WordClockState::read, + WordClockState::update, this, server, LIGHT_SETTINGS_ENDPOINT_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED), - _webSocket(LightState::read, - LightState::update, + _webSocket(WordClockState::read, + WordClockState::update, this, server, LIGHT_SETTINGS_SOCKET_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED), clock() { - // configure led to be output - pinMode(LED_PIN, OUTPUT); - // configure settings service update handler to update LED state addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); } void ClockService::begin() { - _state.ledOn = DEFAULT_LED_STATE; + _state.wordClockOn = DEFAULT_CLOCK_STATE; onConfigUpdated(); clock.init(); } void ClockService::onConfigUpdated() { - digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); + // digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); + if (_state.wordClockOn) { + clock.turnOn(); + } else { + clock.turnOff(); + } } diff --git a/src/ClockService.h b/src/ClockService.h index 28797ae..a0d2ac8 100644 --- a/src/ClockService.h +++ b/src/ClockService.h @@ -9,48 +9,39 @@ #include #include "Clock.h" -#define LED_PIN 2 -#define PRINT_DELAY 5000 - -#define DEFAULT_LED_STATE false +#define DEFAULT_CLOCK_STATE true #define OFF_STATE "OFF" #define ON_STATE "ON" -// Note that the built-in LED is on when the pin is low on most NodeMCU boards. -// This is because the anode is tied to VCC and the cathode to the GPIO 4 (Arduino pin 2). -#ifdef ESP32 -#define LED_ON 0x1 -#define LED_OFF 0x0 -#elif defined(ESP8266) -#define LED_ON 0x0 -#define LED_OFF 0x1 -#endif +#define CLOCK_ON 0x1 +#define CLOCK_OFF 0x0 + #define LIGHT_SETTINGS_ENDPOINT_PATH "/rest/lightState" #define LIGHT_SETTINGS_SOCKET_PATH "/ws/lightState" -class LightState { +class WordClockState { public: - bool ledOn; + bool wordClockOn; - static void read(LightState& settings, JsonObject& root) { - root["led_on"] = settings.ledOn; + static void read(WordClockState& settings, JsonObject& root) { + root["led_on"] = settings.wordClockOn; } - static StateUpdateResult update(JsonObject& root, LightState& lightState) { - boolean newState = root["led_on"] | DEFAULT_LED_STATE; - if (lightState.ledOn != newState) { - lightState.ledOn = newState; + static StateUpdateResult update(JsonObject& root, WordClockState& lightState) { + boolean newState = root["led_on"] | DEFAULT_CLOCK_STATE; + if (lightState.wordClockOn != newState) { + lightState.wordClockOn = newState; return StateUpdateResult::CHANGED; } return StateUpdateResult::UNCHANGED; } - static void haRead(LightState& settings, JsonObject& root) { - root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; + static void haRead(WordClockState& settings, JsonObject& root) { + root["state"] = settings.wordClockOn ? ON_STATE : OFF_STATE; } - static StateUpdateResult haUpdate(JsonObject& root, LightState& lightState) { + static StateUpdateResult haUpdate(JsonObject& root, WordClockState& lightState) { String state = root["state"]; // parse new led state boolean newState = false; @@ -60,22 +51,22 @@ class LightState { return StateUpdateResult::ERROR; } // change the new state, if required - if (lightState.ledOn != newState) { - lightState.ledOn = newState; + if (lightState.wordClockOn != newState) { + lightState.wordClockOn = newState; return StateUpdateResult::CHANGED; } return StateUpdateResult::UNCHANGED; } }; -class ClockService : public StatefulService { +class ClockService : public StatefulService { public: ClockService(AsyncWebServer* server, SecurityManager* securityManager); void begin(); private: - HttpEndpoint _httpEndpoint; - WebSocketTxRx _webSocket; + HttpEndpoint _httpEndpoint; + WebSocketTxRx _webSocket; Clock clock; diff --git a/src/Types.cpp b/src/Types.cpp index b2f3613..cc7a35a 100644 --- a/src/Types.cpp +++ b/src/Types.cpp @@ -4,32 +4,32 @@ #include "Types.h" -const std::vector types::es = calcPixels(0, 0, 2); -const std::vector types::ist = calcPixels(3, 0, 3); -const std::vector types::fuenf = calcPixels(8, 0, 4); -const std::vector types::zehn = calcPixels(0, 1, 4); -const std::vector types::zwanzig = calcPixels(5, 1, 7); -const std::vector types::drei = calcPixels(1, 2, 3); -const std::vector types::viertel = calcPixels(5, 2, 7); -const std::vector types::dreiviertel = calcPixels(1, 2, 11); -const std::vector types::vor = calcPixels(0, 3, 3); -const std::vector types::nach = calcPixels(3, 3, 4); -const std::vector types::halb = calcPixels(8, 3, 4); -const std::vector types::zwoelf = calcPixels(0, 4, 5); -const std::vector types::sieben = calcPixels(6, 4, 6); -const std::vector types::ein = calcPixels(0, 5, 3); -const std::vector types::eins = calcPixels(0, 5, 4); -const std::vector types::vier = calcPixels(4, 5, 4); -const std::vector types::acht = calcPixels(8, 5, 4); -const std::vector types::sechs = calcPixels(0, 6, 5); -const std::vector types::zwei = calcPixels(6, 6, 5); -const std::vector types::fuenf2 = calcPixels(1, 7, 5); -const std::vector types::elf = calcPixels(5, 7, 3); -const std::vector types::zehn2 = calcPixels(8, 7, 4); -const std::vector types::neun = calcPixels(0, 8, 4); -const std::vector types::drei2 = calcPixels(4, 8, 4); -const std::vector types::ein2 = calcPixels(6, 8, 3); -const std::vector types::uhr = calcPixels(9, 8, 3); +const std::vector types::es = calcPixels(0, 0, 2); +const std::vector types::ist = calcPixels(3, 0, 3); +const std::vector types::fuenf = calcPixels(8, 0, 4); +const std::vector types::zehn = calcPixels(0, 1, 4); +const std::vector types::zwanzig = calcPixels(5, 1, 7); +const std::vector types::drei = calcPixels(1, 2, 4); +const std::vector types::viertel = calcPixels(5, 2, 7); +const std::vector types::dreiviertel = calcPixels(1, 2, 11); +const std::vector types::vor = calcPixels(0, 3, 3); +const std::vector types::nach = calcPixels(3, 3, 4); +const std::vector types::halb = calcPixels(8, 3, 4); +const std::vector types::zwoelf = calcPixels(0, 4, 5); +const std::vector types::sieben = calcPixels(6, 4, 6); +const std::vector types::ein = calcPixels(0, 5, 3); +const std::vector types::eins = calcPixels(0, 5, 4); +const std::vector types::vier = calcPixels(4, 5, 4); +const std::vector types::acht = calcPixels(8, 5, 4); +const std::vector types::sechs = calcPixels(0, 6, 5); +const std::vector types::zwei = calcPixels(6, 6, 4); +const std::vector types::fuenf2 = calcPixels(1, 7, 4); +const std::vector types::elf = calcPixels(5, 7, 3); +const std::vector types::zehn2 = calcPixels(8, 7, 4); +const std::vector types::neun = calcPixels(0, 8, 4); +const std::vector types::drei2 = calcPixels(4, 8, 4); +const std::vector types::ein2 = calcPixels(6, 8, 3); +const std::vector types::uhr = calcPixels(9, 8, 3); uint8_t types::convert(uint8_t x, uint8_t y) { const bool upRow = (x % 2) == 0; @@ -42,8 +42,8 @@ uint8_t types::convert(uint8_t x, uint8_t y) { return val; } -std::vector types::calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels) { - std::vector vec; +std::vector types::calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels) { + std::vector vec; for (uint8_t i = 0; i < nrPixels; i++) { vec.push_back(convert(x + i, y)); } diff --git a/src/Types.h b/src/Types.h index 2587d2e..b6b64fa 100644 --- a/src/Types.h +++ b/src/Types.h @@ -8,7 +8,7 @@ #include "Arduino.h" struct types { - static const std::vector + static const std::vector es, ist, fuenf, zehn, zwanzig, drei, viertel, dreiviertel, vor, @@ -19,9 +19,22 @@ struct types { zehn2, neun, drei2, ein2, uhr; + /** + * convert an x/y coordinate into the corresponding strip index value + * @param x x axis value + * @param y y axis value + * @return strip led index (zero beginning) + */ static uint8_t convert(uint8_t x, uint8_t y); - static std::vector calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels); + /** + * calculate all pixels within a pixelrange for a word + * @param x x axis value + * @param y y axis value + * @param nrPixels number of pixels the word contains + * @return array of strip indexes + */ + static std::vector calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels); }; #endif // LEDSTRIPINTERFACE_TYPES_H