diff --git a/README.md b/README.md index 12efc8f..d49132a 100644 --- a/README.md +++ b/README.md @@ -178,11 +178,12 @@ Read more: [Garage Door Opener Project](./examples/GarageDoor/README.md) ### Change Log -#### v2.0.4 (2024-12-10) +#### v2.0.4 (2024-12-11) * The ability to update firmware via a URL. (Firmware is the executable image). * Evolution to improve position measurement with the SR04 sensor. * Device information notices booting * Switch sample revised +* New sample StatelessSwitch added #### v2.0.3 (2024-10-11) * Support and examples for a heater/cooler/thermostat (temperature, humidity, etc.) diff --git a/examples/StatelessSwitch/StatelessSwitch.h b/examples/StatelessSwitch/StatelessSwitch.h new file mode 100644 index 0000000..62d69b0 --- /dev/null +++ b/examples/StatelessSwitch/StatelessSwitch.h @@ -0,0 +1,117 @@ +#pragma region Prolog +/******************************************************************* +$CRT 11 Dez 2024 : hb + +$AUT Holger Burkarth +$DAT >>StatelessSwitch.h<< 11 Dez 2024 16:24:30 - (c) proDAD +*******************************************************************/ +#pragma endregion +#pragma region Spelling +// Ignore Spelling: +#pragma endregion +#pragma region Description +/* +--EN-- + + +--DE-- + +*/ +#pragma endregion +#pragma region Includes +#include + +using namespace HBHomeKit; +using namespace HBHomeKit::Switch; +#pragma endregion + +#pragma region PIN Config + +/* +* @brief +*/ +#define SWITCHER_PIN D7 + + +#pragma endregion + +#pragma region HomeKit Config + +/* +* @brief A HomeKit device service contains the basic information of the device +* @note The device name is also used to create the Bonjour service name. +*/ +const CDeviceService Device +{ + { + .DeviceName{"Switch"}, // available as http://Switch.local + /* + * ... more optional device information: You can read more about the CDeviceService in hb_homekit.h + */ + } +}; + + +/* +* @brief Switcher as a service for HomeKit +*/ +CStatelessSwitchService StatelessSwitch +({ + .Name = "SlessSwitch", + }); + +/* +* @brief HomeKit provides everything you need to interact with Apple HomeKit and host a homepage. +*/ +CHomeKit HomeKit +( + Device, + homekit_accessory_category_programmable_switch, + &StatelessSwitch +); + +CPrgSwitchEventHandler gbEventHandler(SWITCHER_PIN, StatelessSwitch); + +#pragma endregion + + +#pragma region setup +void setup() +{ + Serial.begin(115200); + delay(500); // Important for ESP32: Wait until the serial interface is ready + Serial.println("\n\n\nEnter Setup"); + + // Installs and configures everything for Switcher. + InstallVarsAndCmds(HomeKit); + AddMenuItems(HomeKit); + gbEventHandler.Setup(HomeKit); + + // Adds a menu (WiFi) that allows the user to connect to a WiFi network. + AddWiFiLoginMenu(HomeKit); + + // Adds standard menu items to the controller. + AddStandardMenus(HomeKit); + + // Installs the action UI, required for garage/WiFi web-pages. + InstallActionUI(HomeKit); + + // Starts HomeKit + if(!HomeKit.Setup()) + { + ERROR("HomeKit setup failed"); + return; + } + + INFO("Setup finished"); +} + +#pragma endregion + +#pragma region loop +void loop() +{ + HomeKit.Loop(); +} + +#pragma endregion diff --git a/examples/StatelessSwitch/StatelessSwitch.ino b/examples/StatelessSwitch/StatelessSwitch.ino new file mode 100644 index 0000000..fa5438c --- /dev/null +++ b/examples/StatelessSwitch/StatelessSwitch.ino @@ -0,0 +1,12 @@ +/******************************************************************* + $CRT 11 Dez 2024 : hb + + $AUT Holger Burkarth + $DAT >>StatelessSwitch.ino<< 11 Dez 2024 16:24:43 - (c) proDAD +*******************************************************************/ +/* + * The source code has been moved to a .h file to take advantage + * of syntax highlighting and other features of preferred IDEs + * such as Visual Studio. +*/ +#include "StatelessSwitch.h" diff --git a/examples/Switch/Switch.h b/examples/Switch/Switch.h index 779b494..b4c9755 100644 --- a/examples/Switch/Switch.h +++ b/examples/Switch/Switch.h @@ -3,7 +3,7 @@ $CRT 03 Okt 2024 : hb $AUT Holger Burkarth -$DAT >>Switch.h<< 03 Okt 2024 15:36:50 - (c) proDAD +$DAT >>Switch.h<< 11 Dez 2024 15:39:39 - (c) proDAD *******************************************************************/ #pragma endregion #pragma region Spelling @@ -58,7 +58,8 @@ const CDeviceService Device CSwitchService Switcher ({ .Name = "Switch", - .Setter = [](homekit_characteristic_t* pC, homekit_value_t value) +#if 1 + .Setter = [](homekit_characteristic_t* pC, homekit_value_t value) { VERBOSE("Switch::Setter"); if(modify_value(pC, value)) @@ -67,11 +68,13 @@ CSwitchService Switcher digitalWrite(SWITCHER_PIN, Stat ? HIGH : LOW); } }, + #else .Getter = [](const homekit_characteristic_t* pC) -> homekit_value_t { VERBOSE("Switch::Getter"); return static_value_cast(digitalRead(SWITCHER_PIN) == HIGH); } + #endif }); /* @@ -86,112 +89,6 @@ CHomeKit HomeKit #pragma endregion -#pragma region InstallVarsAndCmds -void InstallVarsAndCmds(CController& c) -{ - c - .SetVar("SWITCH", [](auto p) - { - if(p.Args[0] != nullptr) // case of: /?SWITCH=true - { - /* "true", "TRUE", "1", "ON", "on" ==> bool */ - bool NewState = convert_value(*p.Args[0]); - modify_value_and_notify(&Switcher.State, NewState); - } - - /* return "true" or "false" */ - return MakeTextEmitter(Switcher.State); - }) - - ; -} - -#pragma endregion - -#pragma region Switch_JavaScript -CTextEmitter Switch_JavaScript() -{ - return MakeTextEmitter(F(R"( - -function UIUpdateSwitch(state) -{ - var State = (state == 'true'); - SetElementInnerHTML('state', State ? 'ON' : 'OFF'); - SetElementChecked('switch', State); -} - -function SetSwitch(state) -{ - ForSetVar('SWITCH', state, responseText => UIUpdateSwitch(responseText) ); -} - -function Update() -{ - ForVar('SWITCH', responseText => UIUpdateSwitch(responseText) ); -} - - -onload = function() -{ - Update(); - - setInterval - ( - function() - { - Update(); - }, - 1000 - ); -}; - -)")); -} -#pragma endregion - -#pragma region Switch_BodyHtml -CTextEmitter Switch_BodyHtml() -{ - return MakeTextEmitter(F(R"( -
Switcher is
- -

- {ACTION_CHECKBOX:switch:SetSwitch(this.checked)} -   -   -   - {ACTION_BUTTON:SetSwitch(false):OFF} - {ACTION_BUTTON:SetSwitch(true):ON} -

-)")); -} -#pragma endregion - -#pragma region AddMenuItems -void AddMenuItems(CController& c) -{ - c - .AddMenuItem - ( - { - .Title = "Switch", - .MenuName = "Start", - .URI = "/", - .CSS = ActionUI_CSS(), - .JavaScript = [](Stream& out) - { - out << ActionUI_JavaScript(); - out << UIUtils_JavaScript(); - out << Switch_JavaScript(); - }, - .Body = Switch_BodyHtml() - } - ) - ; -} - -#pragma endregion - #pragma region setup void setup() @@ -201,7 +98,6 @@ void setup() Serial.println("\n\n\nEnter Setup"); pinMode(SWITCHER_PIN, OUTPUT); - //digitalWrite(SWITCHER_PIN, LOW); // Installs and configures everything for Switcher. diff --git a/src/HomeKit_Switch.cpp b/src/HomeKit_Switch.cpp index 0e3f752..322864a 100644 --- a/src/HomeKit_Switch.cpp +++ b/src/HomeKit_Switch.cpp @@ -3,7 +3,7 @@ $CRT 08 Dez 2024 : hb $AUT Holger Burkarth -$DAT >>HomeKit_Switch.cpp<< 10 Dez 2024 16:29:12 - (c) proDAD +$DAT >>HomeKit_Switch.cpp<< 11 Dez 2024 09:56:23 - (c) proDAD *******************************************************************/ #pragma endregion #pragma region Spelling @@ -82,6 +82,7 @@ CTextEmitter Switch_JavaScript() // vector of all switches var SwitchNames = []; var SwitchTypes = []; +var AddUIElement = function(name, type, state) {}; function UIUpdateSwitch(name, state) { @@ -98,7 +99,7 @@ function SetStatelessSwitch(name, state) ForSetVar('SWITCH_STATE', name + '|' + state, responseText => {} ); } -function Update() +function UpdateUI() { for(var i = 0; i < SwitchNames.length; ++i) { @@ -113,6 +114,39 @@ function Update() } } +function AddCheckbox(cell, name) +{ + var Label = document.createElement('label'); + Label.className = 'checkbox'; + cell.appendChild(Label); + + var Input = document.createElement('input'); + Input.id = name + 'Checkbox'; + Input.type = 'checkbox'; + Input.onclick = function() { SetSwitch(name, this.checked); }; + Label.appendChild(Input); + + var Span = document.createElement('span'); + Span.className = 'slider round'; + Label.appendChild(Span); +} + +function AddButton(cell, name, text, type) +{ + var Button = document.createElement('button'); + Button.innerHTML = text; + Button.onclick = function() { SetSwitch(name, type); }; + cell.appendChild(Button); +} + +function AddEmptyRow(table) +{ + var Row = table.insertRow(-1); + var Cell = Row.insertCell(-1); + Cell.innerHTML = ' '; +} + + function BuildSwitches() { ForVar('SWITCHES', responseText => @@ -121,13 +155,18 @@ function BuildSwitches() for(var i = 0; i < Switches.length; ++i) { var Switch = Switches[i].split(';'); - if(Switch.length == 2) + if(Switch.length >= 2) { var Name = Switch[0]; var Type = parseInt(Switch[1]); + var State = ''; + + if(Switch.length > 2) + State = Switch[2]; SwitchNames.push(Name); SwitchTypes.push(Type); + AddUIElement(Name, Type, State); } } }); @@ -137,11 +176,15 @@ function BuildSwitches() window.addEventListener('load', (event) => { - setTimeout(BuildSwitches, 100); + setTimeout + ( + function() { BuildSwitches(); UpdateUI(); }, + 100 + ); setInterval ( - function() { Update(); }, + function() { UpdateUI(); }, 1000 ); }); @@ -150,6 +193,48 @@ window.addEventListener('load', (event) => } #pragma endregion +#pragma region Switch_CreateUI_JavaScript +CTextEmitter Switch_CreateUI_JavaScript() +{ + return MakeTextEmitter(F(R"( + +AddUIElement = function(name, type, state) +{ + var Table = document.getElementById('SwitchTable'); + var Row = Table.insertRow(-1); + var Cell = Row.insertCell(-1); + Cell.innerHTML = '' + name + ': '; + Cell = Row.insertCell(-1); + switch(type) + { + case 1: + AddCheckbox(Cell, name); + AddEmptyRow(Table); + break; + + case 2: + AddButton(Cell, name, 'SINGLE', 0); + AddButton(Cell, name, 'DOUBLE', 1); + AddButton(Cell, name, 'LONG', 2); + AddEmptyRow(Table); + break; + } +}; + + +)")); +} +#pragma endregion + +#pragma region Switch_BodyHtml +CTextEmitter Switch_BodyHtml() +{ + return MakeTextEmitter(F(R"( +
+)")); +} +#pragma endregion + //END Website - Elements #pragma endregion @@ -220,13 +305,11 @@ void InstallVarsAndCmds(CController& c) { case CSwitchDsc::Type_Switch: //VERBOSE("SWITCH_STATE %s: %d", Dsc.Name, static_value_cast(Dsc.pSwitch)); - return MakeTextEmitter(convert_value(Dsc.pSwitch)); - break; + return MakeTextEmitter(*Dsc.pSwitch); case CSwitchDsc::Type_StatelessProgrammableSwitch: //VERBOSE("SWITCH_STATE %s: %d", Dsc.Name); return MakeTextEmitter(); - break; } } } @@ -239,18 +322,27 @@ void InstallVarsAndCmds(CController& c) #pragma region SWITCHES /* - * @return a list of all switches: "name;type|name;type|..." + * @return a list of all switches: "name;type;val|name;type|..." */ .SetVar("SWITCHES", [](auto p) { String Text; ForEach_Switch([&](CSwitchDsc dsc) { - char Buf[64]; - sprintf_P(Buf, PSTR("%.32s;%d|") + char Buf[64], Buf2[16]; + sprintf_P(Buf, PSTR("%.32s;%d") , dsc.Name , dsc.Type ); + + switch(dsc.Type) + { + case 1: // Switch + strcat(Buf, ";"); + strcat(Buf, to_string(dsc.pSwitch->value, Buf2)); + break; + } + strcat(Buf, "|"); Text += Buf; }); @@ -265,6 +357,31 @@ void InstallVarsAndCmds(CController& c) #pragma endregion +#pragma region AddMenuItems +void AddMenuItems(CController& c) +{ + c + .AddMenuItem + ( + { + .Title = "Switches", + .MenuName = "Start", + .URI = "/", + .CSS = ActionUI_CSS(), + .JavaScript = [](Stream& out) + { + out << ActionUI_JavaScript(); + out << UIUtils_JavaScript(); + out << Switch_JavaScript(); + out << Switch_CreateUI_JavaScript(); + }, + .Body = Switch_BodyHtml() + } + ) + ; +} + +#pragma endregion //END Install and Setup #pragma endregion diff --git a/src/HomeKit_Switch.h b/src/HomeKit_Switch.h index be0cc03..a8d49f1 100644 --- a/src/HomeKit_Switch.h +++ b/src/HomeKit_Switch.h @@ -3,7 +3,7 @@ $CRT 27 Sep 2024 : hb $AUT Holger Burkarth -$DAT >>HomeKit_Switch.h<< 09 Dez 2024 11:31:02 - (c) proDAD +$DAT >>HomeKit_Switch.h<< 11 Dez 2024 07:51:03 - (c) proDAD using namespace HBHomeKit::Switch; *******************************************************************/ @@ -24,127 +24,28 @@ namespace Switch #pragma region +Support -#pragma region CPrgSwitchEventController -struct CPrgSwitchEventController -{ - #pragma region static const - constexpr static uint32_t ToShortMS = 1; // [ms] - constexpr static uint32_t DoubleClickMS = 500; // [ms] - constexpr static uint32_t LongPressMS = 3000; // [ms] - constexpr static uint32_t MaxEventCount = 4; - - #pragma endregion - - #pragma region Fields - std::array mEventTime{}; - uint16_t mEventIndex{}; - uint8_t mNeedClear{}; - - #pragma endregion - - #pragma region NextEvent - - std::optional NextEvent() - { - /* - Press Release Press Release Press Release - |---------|---------|---------|--------|--------| - Single:# # - Double:# # # # - Long: # #.... - */ - if(mNeedClear == 0) - { - HOMEKIT_PROGRAMMABLE_SWITCH_EVENT Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_UNDEF; - uint32_t CurMS = millis(); - switch(mEventIndex) - { - case 1: // Pressed an not released - if(CurMS - mEventTime[0] >= LongPressMS) - Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_LONG_PRESS; - break; - - case 2: // Pressed and released - if(CurMS - mEventTime[0] > DoubleClickMS) - Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS; - break; - - case 4: // Pressed, released, pressed and released - if(mEventTime[3] - mEventTime[0] > DoubleClickMS) - Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS; - else - Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_DOUBLE_PRESS; - break; - } - - if(Evt != HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_UNDEF) - { - mNeedClear = 1; - return Evt; - } - } - return {}; - } - - #pragma endregion - - #pragma region Trigger - void Trigger(bool pressed) - { - uint32_t CurMS = millis(); - -L_Enter: - if(mNeedClear) - { - mEventIndex = 0; - - if(pressed) - { - mNeedClear = 0; // force pressed always on index=0 - mEventTime[0] = CurMS; - mEventIndex = 1; - } - } - else if(mEventIndex < MaxEventCount) - { - if(ToShortMS > 0 && CurMS - mEventTime[mEventIndex - 1] < ToShortMS) - { - mNeedClear = 1; - goto L_Enter; - } - else - { - static_assert((MaxEventCount & 1) == 0); // must be even - if(pressed) - mEventIndex &= ~1; // even index for pressed - else - mEventIndex |= 1; // odd index for released - - - mEventTime[mEventIndex] = CurMS; - ++mEventIndex; - } - - } - } - - #pragma endregion - -}; - -#pragma endregion - #pragma region ForEach_Switch struct CSwitchDsc { + /* + * From CSwitchService::Service, CStatelessSwitchService::Service + */ const homekit_service_t* pService{}; + + /* + * From CSwitchService::State, CStatelessSwitchService::Event + */ homekit_characteristic_t* pSwitch{}; - const char* Name{}; + + /* + * Name from CSwitchService::Name, CStatelessSwitchService::Name + */ + const char* Name{}; enum EType : uint8_t { - Type_Switch = 1, - Type_StatelessProgrammableSwitch = 2 + Type_Switch = 1, // homekit_accessory_category_switch + Type_StatelessProgrammableSwitch = 2, // homekit_accessory_category_programmable_switch } Type{}; @@ -159,6 +60,7 @@ void ForEach_Switch(std::function functor); /* Calls the functor for each switch service * (homekit_accessory_category_switch, homekit_accessory_category_programmable_switch) * @param functor The functor to call: void functor(CSwitchDsc) +* @note The functor will only be called if all parameters are valid. * @example * ForEach_Switch([](CSwitchDsc Dsc) * { @@ -170,9 +72,6 @@ void ForEach_Switch(std::function functor); #pragma endregion - - - //END Support #pragma endregion @@ -283,6 +182,16 @@ struct CSwitchService return static_value_cast(&Name); } + void SetSate(bool value) noexcept + { + modify_value_and_notify(&State, static_value_cast(value)); + } + + bool GetState() const noexcept + { + // convert_value is used so that any existing getter_ex is considered. + return convert_value(&State); + } #pragma endregion @@ -299,7 +208,7 @@ struct CSwitchService #pragma endregion #pragma region CStatelessSwitchService -/* A HomeKit switch service +/* A HomeKit stateless switch service * @example * CStatelessSwitchService Switch @@ -357,13 +266,14 @@ struct CStatelessSwitchService return static_value_cast(&Name); } - void SetEvent(HOMEKIT_PROGRAMMABLE_SWITCH_EVENT value) noexcept + /* Trigger an event by sending it to HomeKit. + */ + void TriggerEvent(HOMEKIT_PROGRAMMABLE_SWITCH_EVENT value) noexcept { Event.value = HOMEKIT_NULL_CPP(); // clear the value and force a notification modify_value_and_notify(&Event, static_value_cast(value)); } - #pragma endregion #pragma region Operators @@ -383,23 +293,320 @@ struct CStatelessSwitchService #pragma endregion +}; + +#pragma endregion + +#pragma region CController + +#pragma region CPrgSwitchEventController +/* Single/double/long events are determined from press and release events. */ +struct CPrgSwitchEventController +{ + #pragma region static const + constexpr static uint32_t ToShortMS = 1; // [ms] + constexpr static uint32_t DoubleClickMS = 500; // [ms] + constexpr static uint32_t LongPressMS = 3000; // [ms] + constexpr static uint32_t MaxEventCount = 4; + + #pragma endregion + + #pragma region Fields + std::array mEventTime{}; + uint16_t mEventIndex{}; + uint8_t mNeedClear{}; + + #pragma endregion + + #pragma region NextEvent + + std::optional NextEvent() + { + /* + Press Release Press Release Press Release + |---------|---------|---------|--------|--------| + Single:# # + Double:# # # # + Long: # #.... + */ + if(mNeedClear == 0) + { + HOMEKIT_PROGRAMMABLE_SWITCH_EVENT Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_UNDEF; + uint32_t CurMS = millis(); + switch(mEventIndex) + { + case 1: // Pressed an not released + if(CurMS - mEventTime[0] >= LongPressMS) + Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_LONG_PRESS; + break; + + case 2: // Pressed and released + if(CurMS - mEventTime[0] > DoubleClickMS) + Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS; + break; + + case 4: // Pressed, released, pressed and released + if(mEventTime[3] - mEventTime[0] > DoubleClickMS) + Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS; + else + Evt = HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_DOUBLE_PRESS; + break; + } + + if(Evt != HOMEKIT_PROGRAMMABLE_SWITCH_EVENT_UNDEF) + { + mNeedClear = 1; + return Evt; + } + } + return {}; + } + + #pragma endregion + + #pragma region Trigger + void Trigger(bool pressed) + { + uint32_t CurMS = millis(); + +L_Enter: + if(mNeedClear) + { + mEventIndex = 0; + + if(pressed) + { + mNeedClear = 0; // force pressed always on index=0 + mEventTime[0] = CurMS; + mEventIndex = 1; + } + } + else if(mEventIndex < MaxEventCount) + { + if(ToShortMS > 0 && CurMS - mEventTime[mEventIndex - 1] < ToShortMS) + { + mNeedClear = 1; + goto L_Enter; + } + else + { + static_assert((MaxEventCount & 1) == 0); // must be even + if(pressed) + mEventIndex &= ~1; // even index for pressed + else + mEventIndex |= 1; // odd index for released + + + mEventTime[mEventIndex] = CurMS; + ++mEventIndex; + } + + } + } + + #pragma endregion }; #pragma endregion -#pragma region +#pragma region CPrgSwitchEventHandler +/* Single/double/long events are determined by the status of a digital pin and sent to HomeKit. +* +* @example +* extern CStatelessSwitchService StatelessSwitch; +* +* CPrgSwitchEventHandler gbEventHandler(SWITCHER_PIN, StatelessSwitch); +* +* void setup() +* { +* [...] +* gbEventHandler.Setup(HomeKit); +* [...] +* } +*/ +struct CPrgSwitchEventHandler +{ + #pragma region Fields + CPrgSwitchEventController EventCtrl; + CStatelessSwitchService& Service; + const uint16_t TriggerPin; + const uint16_t PressedPinState; -/* + #pragma endregion + + #pragma region Construction + CPrgSwitchEventHandler(const CPrgSwitchEventHandler&) = delete; + CPrgSwitchEventHandler(CPrgSwitchEventHandler&&) = delete; + + /* + * @param triggerPin The pin that triggers the event + * @param service The stateless switch service to trigger + * @param pressedPinState The state of the pin when pressed (default: HIGH) + */ + CPrgSwitchEventHandler(uint16_t triggerPin, CStatelessSwitchService& service, uint16_t pressedPinState = HIGH) + : + Service(service), + TriggerPin(triggerPin), + PressedPinState(pressedPinState) + { + } + + #pragma endregion + + #pragma region Setup + /* Setup the event handler (call in main-setup) + * @param c The controller to install the event handler + */ + void Setup(CController& c) + { + pinMode(TriggerPin, INPUT); + attachInterruptArg(TriggerPin, TriggerISR, reinterpret_cast(this), CHANGE); + + /* Call the functor every 5ms to send events */ + c.Ticker.Start(5, [this]() + { + auto Event = EventCtrl.NextEvent(); + if(Event) + Service.TriggerEvent(*Event); + }); + + } + + void Setup(CHomeKit& hk) + { + Setup(hk.Controller); + } + + #pragma endregion + + #pragma region TriggerISR + static void ICACHE_RAM_ATTR TriggerISR(void* arg) + { + CPrgSwitchEventHandler* const This = reinterpret_cast(arg); + This->EventCtrl.Trigger(digitalRead(This->TriggerPin) == This->PressedPinState); + } + #pragma endregion + +}; + +#pragma endregion + +//END CController +#pragma endregion + + +#pragma region +Functions + +#pragma region InstallVarsAndCmds + /* Install variables + * @note: The following variables are installed: + * - SWITCH_STATE + * - SWITCH_STATE=name + * Gets the current state of the switch + * - CSwitchService -> bool + * - CStatelessSwitchService -> [0,1,2] (see HOMEKIT_PROGRAMMABLE_SWITCH_EVENT) + * - SWITCH_STATE=name|state + * Sets the state of the switch + * + * - SWITCHES + * Gets the names and types of all switches. + * Format: "name;type|name;type|..." + * name: see CSwitchService::Name, CStatelessSwitchService::Name + * type: 1=CSwitchService, 2=CStatelessSwitchService */ void InstallVarsAndCmds(CController& c); +#pragma endregion + +#pragma region Switch_JavaScript /* +* @note functions / variables: +* - var SwitchNames = [] +* All switch names are stored in the array. +* see CSwitchService::Name, CStatelessSwitchService::Name + +* - var SwitchTypes = [] +* All switch types are stored in the array. +* 1=CSwitchService, 2=CStatelessSwitchService +* +* - var AddUIElement = function(name, type, state) {}; +* Adds a switch to the UI. +* name: The name of the switch (see CSwitchService::Name, CStatelessSwitchService::Name) +* type: 1=CSwitchService, 2=CStatelessSwitchService +* +* - BuildSwitches() +* Builds the switch names and types. +* The result is stored in SwitchNames and SwitchTypes. +* auto. Called on-load +* +* - UpdateUI() +* Updates the switch states every 1s. +* Installed on load. +* +* - SetSwitch(name, state) +* Sets the state of the switch. +* name: The name of the switch (see CSwitchService::Name) +* state: (bool) +* +* - SetStatelessSwitch(name, state) +* Sets the state of the stateless switch. +* name: The name of the switch (see CStatelessSwitchService::Name) +* state: [0,1,2] (see HOMEKIT_PROGRAMMABLE_SWITCH_EVENT) +* +* - AddCheckbox(cell, name) +* Adds a checkbox to the cell. +* cell: The cell to add the checkbox to. +* name: The name of the switch (see CSwitchService::Name) +* +* - AddButton(cell, name, text, type) +* Adds a button to the cell. +* cell: The cell to add the button to. +* name: The name of the switch (CStatelessSwitchService::Name) +* text: The text of the button +* type: 0=Single, 1=Double, 2=Long +* +* - AddEmptyRow(table) +* Adds an empty row to the table. +* table: The table to add the row to. +* */ CTextEmitter Switch_JavaScript(); #pragma endregion +#pragma region Switch_CreateUI_JavaScript +/* Installs a function to add a switch to the UI (see AddUIElement) +* Requires a table with the id "SwitchTable". +* used: AddCheckbox, AddButton, AddEmptyRow +* @note Switch_JavaScript must be called before. +*/ +CTextEmitter Switch_CreateUI_JavaScript(); + +#pragma endregion + +#pragma region Switch_BodyHtml +/* Adds a table to the body of the page +*
+* @see Switch_CreateUI_JavaScript +*/ +CTextEmitter Switch_BodyHtml(); + +#pragma endregion + +#pragma region AddMenuItems +/* @brief Adds the Start menu item to the controller. +* @note InstallActionUI is required. +*/ +void AddMenuItems(CController& c); + +#pragma endregion + + + +//END Functions +#pragma endregion + #pragma region Epilog } // namespace Switch diff --git a/src/hb_homekit.h b/src/hb_homekit.h index 377fd4b..8fd6080 100644 --- a/src/hb_homekit.h +++ b/src/hb_homekit.h @@ -4303,11 +4303,22 @@ std::vector Split(const String& text, char sep); * installed enumerator. * * @see CWiFiConnection::SetPasswordEnumerator +* @see SaveWiFiLogin */ CController& AddWiFiLoginMenu(CController&, bool alwaysUsePeristentLogin = true); #pragma endregion +#pragma region +/* Save the WiFi login data to EEPROM. +* @param ssid The SSID of the WiFi network. +* @param password The password of the WiFi network. +* @see AddWiFiLoginMenu +*/ +void SaveWiFiLogin(String ssid, String password); + +#pragma endregion + #pragma region AddDeviceMenu /* Add a info menu item to the web site "/device". * The Device item is created on the right side of the menu bar. diff --git a/src/hb_wifi_menu.cpp b/src/hb_wifi_menu.cpp index 158243d..f8a8b0d 100644 --- a/src/hb_wifi_menu.cpp +++ b/src/hb_wifi_menu.cpp @@ -3,7 +3,7 @@ $CRT 17 Sep 2024 : hb $AUT Holger Burkarth -$DAT >>hb_wifi_menu.cpp<< 02 Okt 2024 07:41:03 - (c) proDAD +$DAT >>hb_wifi_menu.cpp<< 11 Dez 2024 15:45:54 - (c) proDAD *******************************************************************/ #pragma endregion #pragma region Spelling @@ -264,6 +264,14 @@ struct CWifiLoginContext : IPasswordEnumerator } // namespace +void SaveWiFiLogin(String ssid, String password) +{ + auto Ctx = std::make_shared(); + Ctx->SetFirstSSID(std::move(ssid)); + Ctx->SetFirstPassword(std::move(password)); + Ctx->SaveToEEPROM(); +} + CController& AddWiFiLoginMenu(CController& c, bool alwaysUsePeristentLogin) { auto& WifiConnection = CHomeKit::Singleton->WifiConnection;