Update: 2023-01-09

Esp8266 Button als Arduino Tab.

Button.ino

// ****************************************************************
// Sketch Esp8266 Individual Button Modular(Tab)
// created: Jens Fleischer, 2020-04-10
// last mod: Jens Fleischer, 2020-06-18
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Device an D0 = GPIO 16
// Device an D1 = GPIO  5
// Device an D2 = GPIO  4
// Device an D3 = GPIO  0
// Device an D4 = GPIO  2
// Device an D5 = GPIO 14
// Device an D6 = GPIO 12
// Device an D7 = GPIO 13
// Device an D8 = GPIO 15
// Software: Esp8266 Arduino Core 2.4.2 - 3.1.0
// Getestet auf: Nodemcu, Wemos D1 Mini Pro
/******************************************************************
  Copyright (c) 2020 Jens Fleischer. All rights reserved.

  This file is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  This file is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Individual Button sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> muss im Haupttab aufgerufen werden.
// Die Funktionalität des ESP8266 Webservers ist erforderlich.
// Die Funktion "individualButton();" muss im Setup aufgerufen werden.
// Die Anzahl der Namen und Pin müssen übereinstimmen.
/**************************************************************************************/

const char* const PROGMEM NAME[] {"Beleuchtung", "Pumpe", "Filter", "Lüftung"};
const auto level = HIGH;                                  // LOW für LOW aktive Relais/Led oder HIGH für HIGH aktive (zB. SSR, Mosfet) einstellen
const uint8_t buttonPin[] {D2, D6, D7, D8};               // Pin's eintragen die du verwenden möchtest

void individualButton() {                                 // Die Funktion "individualButton();" muss im Setup aufgerufen werden.
  for (; sizeof(buttonPin) != sizeof(NAME) / sizeof(NAME[0]); Serial.println("Anzahl Pin und Namen der Button stimmen nicht überein."), delay(2e3));
  for (auto& pin : buttonPin) {
    digitalWrite(pin, !level);
    pinMode(pin, OUTPUT);
  }
  // Button Namen senden wenn diese nicht aus dem LocalStorage des Browsers geladen werden können.
  server.on("/press", HTTP_GET, []() {
    String temp = "[";
    for (auto& el : NAME) {
      if (temp != "[") temp += ',';
      temp += (String)"\"" + el + "\"";
    }
    temp += "]";
    server.send(200, "application/json", temp);
  });
  server.on("/press", HTTP_POST, []() {
    if (server.args()) {
      // Pin umschalten
      digitalWrite(buttonPin[server.argName(0).toInt()], !digitalRead(buttonPin[server.argName(0).toInt()]));
      // Serielle Ausgabe zur Kontrolle
      Serial.printf("Button %ld ist %s\n", 1 + server.argName(0).toInt(), digitalRead(buttonPin[server.argName(0).toInt()]) == level ? "AN" : "AUS");
    }
    // Http Antwort
    String temp = "\"";
    for (byte i = 0; i < sizeof(buttonPin); i++) temp += digitalRead(buttonPin[i]) == level;
    temp += "\"";
    server.send(200, "application/json", temp);
  });
}

Die Webseite zum Esp8266 Button.

button.html

<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="style.css">
	<title>Individual Button</title>
	<style>
	  button:active {
        transition: all .5s ease;
        transform: translate(2%, 15%);
        box-shadow: none;
      }
	  button {
        transition: all .5s ease;
      }
	</style>
    <script>
	  window.addEventListener('DOMContentLoaded', () => {
	    var name = JSON.parse(localStorage.getItem('button'));
		if (!name) {
          fetch('/press', {
          }).then(resp => {
            return resp.json();
          }).then(arr => {
	        localStorage.setItem('button', JSON.stringify(arr));
			dom(arr);
	      });
        }
		else {
		  dom(name);
		}
	  });
	  function dom(names) {
	  	var buf = '';
	    names.forEach(el => {
		  length = Math.max(length, el.length);
		  buf += '<button class="button">' + el + '<span></span></button>';
        });
	    document.querySelector('body').insertAdjacentHTML('beforeend', buf);
		send(names);
		document.querySelectorAll('button').forEach((el, i) => {
          el.addEventListener('click', () => {
            let formData = new FormData();
            formData.append(i, 0);
            send(names,formData);
          });
        });
	  }
      function send(arr, arg) {
        fetch('/press', {
          method: 'post',
          body: arg
        }).then(resp => {
          return resp.json();
        }).then(str => {
		  if (str.length != arr.length) localStorage.clear();
          document.querySelectorAll('button').forEach((el, i) => {
		    el.style.minWidth = 0.8 * length + 'em';
		    el.style.backgroundColor = str[i] != 0 ? '#3f0' : '#6c5';
		    el.lastElementChild.innerHTML = str[i] != 0 ? ' AN' : ' AUS';
		  });
        });
      } 
    </script>
  </head>
  <body>
    <h1>SmartHome</h1>
  </body>
</html>