iPhone - incoming call / Apple Notification Center Service (ANCS)

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 305

    #1

    iPhone - incoming call / Apple Notification Center Service (ANCS)

    Hallo zusammen,

    Hintergrund: Ich würde gerne bei eingehenden Anrufen z.B. Musik auf "Pause" schalten. Nach Beendigung des Anrufs auf "Weiter".

    Bekanntlich erlaubt Apple aktuell keinen offiziellen Weg um auf einfache Art und Weise Automatisierungen basierend auf der phone app auszuführen (siehe hier). Android hingegen lässt dies scheinbar zu, siehe hier.
    Eine Alternative beim iOS schein ein Widget zu sein welches immer im Hintergrund läuft, siehe hier. Das erscheint mir jedoch nicht besonders sinnvoll und jedenfalls nicht ressourcenschonend. Auch bringt das scheinbar eine Latenz mit sich.

    Zuletzt bin ich auf die Möglichkeit gestoßen iOS notifications mit einem ESP32 abzufangen, siehe hier. Das geht wohl über den Apple Notification Center Service (ANCS). Laut Spezifikationen gibt es dabei wohl auch infos zu incoming calls, siehe hier.

    Hat schon mal jemand in der Richtung probiert?

    Viele Grüße!
  • t_heinrich
    Lox Guru
    • 07.01.2016
    • 2136

    #2
    Oh wenn sowas klappt wäre echt klasse, mein iPhone ist fast immer nur auf Vibration eingestellt und wenn ich zu Hause bin, verpasse ich dann immer mal wieder Anrufe, weil ich es nicht in der Hosentasche habe.

    Kommentar

    • MarkusCosi
      LoxBus Spammer
      • 28.09.2023
      • 305

      #3
      Folgenden Code habe ich verwendet (minus die Hardware Button Geschichte): https://github.com/Smartphone-Compan...connection.ino

      ...leider scheint die ESP32 library für ANCS nicht mehr zu funktionieren, siehe hier. Entsprechend bekam ich eine Reihe kryptischer Fehlermeldungen.

      Aber: Im Arduino IDE half ein downgrade des ESP32 boards auf 1.0.6. (siehe hier): Tools → Board → Boards Manager → dann links suchen nach ESP32, dann die Version aus der drop-down liste auswählen.
      Dann musste ich noch eine Fehlermeldung beheben die damit zu tun hat, dass python = Python 2 wohl erwartet wurde, ich aber Python 3 installiert habe was per python3 aufgerufen wird. Folgender Befehl behob das und ändert die alte ESP32-Version:

      sed -i -e 's/=python /=python3 /g' ~/Library/Arduino15/packages/esp32/hardware/esp32/*/platform.txt

      Danach konnte ich das Skript mit baudrate 115200 und Partition auf Large kompilieren und hochladen auf den ESP32. Nachdem ich mich mit dem iPhone bei Bluetooth in das tool eingewählt hatte, und die Anfrage auf Kopplung und Erlaubnis Notifications zu senden bejaht hatte, bekam ich im Serial Monitor alle Anruf Infos eines Testanrufs!

      21:05:30.536 -> Got notification: Anrufer Name (aus dem Telefonbuch?)
      21:05:30.575 -> Eingehender Anruf
      21:05:30.575 -> com.apple.mobilephone
      21:05:30.602 -> incoming call

      Mit einer schlanken MQTT library (https://www.emqx.com/en/blog/esp32-c...ic-mqtt-broker) sollte das dann vom ESP32 an den Miniserver gehen können, bzw. ggf. direkt auf einen digitalen Eingang per http?
      Zuletzt geändert von MarkusCosi; 17.09.2024, 21:32.

      Kommentar

      • t_heinrich
        Lox Guru
        • 07.01.2016
        • 2136

        #4
        MarkusCosi es wäre soviel leichter, wenn Apple als Kurzbefehl "Eingehender Anruf" zur Verfügung stellen würde.

        Aber mal eine ganz andere Idee: man nimmt eine Dual-Sim und ein Android Handy, was dauerhaft zu Hause, mit Strom versorgt und lautlos ist. Wäre zumindest einen Versuch wert.

        Kommentar

        • MarkusCosi
          LoxBus Spammer
          • 28.09.2023
          • 305

          #5
          Ja, das wäre in der Tat einfacher.
          Die Idee mit Android und dual sim ist vllt. nicht schlecht, aber ich fürchte, dass das IFTTT bei Android mittlerweile auch nur gegen Aufpreis dieses Feature erlaubt (siehe hier). Und es wäre ja auch ein zweites Gerät, so wie der ESP32 als "incoming-call-detector" eben auch...

          Wie dem auch sei, folgendes funktioniert. Das BLE-Koppeln funktioniert fortan übrigens automatisch und muss nicht erneut doppelt bestätigt werden. Wifi-Aufbau, BLE-connection zum iPhone, MQTT einwahl: alles automatisch. Jeder Anruf triggert ein MQTT publish: "iPhone: incoming call (1)" unter dem topic "eps32".

          Das kann man nun in Loxone verwenden... Das Minimalziel wäre also erreicht...

          Schön wäre auch ein direktes setzen eines Tasters / Schalters in Loxone wie hier, aber das habe ich auch noch nicht hinbekommen..,

          Leider habe ich es auch noch nicht geschafft die Infos vom Call an MQTT zu publishen.. ich scheitere an dieser blöden char-array vs. string Geschichte... für einen Programmierer ist das sicher einfach, aber ich bekomme es einfach nicht hin. (vgl. hier).

          Arduino Code: (tested!)
          (Edit: siehe neuen Code unten)
          Zuletzt geändert von MarkusCosi; 17.09.2024, 19:46.

          Kommentar

          • MarkusCosi
            LoxBus Spammer
            • 28.09.2023
            • 305

            #6
            ich habe noch folgendes beim void onNotificationRemoved hinzgefügt um das Ende eines Anrufs (entweder nicht angenommen oder beendet) per MQTT weiterzugeben:

            //filter action: only incoming calls (cancelled)
            if (notification->category == CategoryIDIncomingCall) {
            Serial.println("Removed Call");
            //post MQTT message
            Serial.println("MQTT publish generic String for Call cancelled");
            client.publish(topicCall, "iPhone: incoming call (0)");
            Serial.println("---");
            }
            if (notification->type == "com.apple.mobilephone") {
            if (notification->message == "Aktiver Anruf") {
            Serial.println("Finished Call");
            //post MQTT message
            Serial.println("MQTT publish generic String for Call end");
            client.publish(topicCall, "iPhone: incoming call (0)");
            Serial.println("---");
            }
            }

            Leider scheitere ich an dieser MQTT Geschichte... es funktioniert einmal, und dann nicht mehr. Ich habe das Gefühl, dass es an dieser retain-Sache liegt, bzw. dem QoS=0 und dass gleiche Dinge nicht mehr als einmal geschickt werden. Kann das sein?

            Kommentar

            • t_heinrich
              Lox Guru
              • 07.01.2016
              • 2136

              #7
              MarkusCosi bleib da bitte dran.
              Für mich sind das leider alles böhmische Dörfer, aber ich glaube das interessant schon paar mehr Leute hier.

              Kommentar

              • MarkusCosi
                LoxBus Spammer
                • 28.09.2023
                • 305

                #8
                Folgender Code für einen ESP32 Mikrocontroller funktioniert nun per direkter Eingabe an den Miniserver per Loxone Webservice / API.
                Im Endergebnis hat man damit einen ESP32 mit USB-C Stromversorgung, der automatisch sich ins WLAN einwählt, sich automatisch (nach erstmaligem Pairing mit Bestätigung am iPhone) per BLE (Bluetooth) mit dem iPhone verbindet, und bei eingehenden Anrufen einen Schalter in Loxone währen des Klingelns bzw. des Telefonats auf EIN setzt, und bei Beendigung den Schalter auf AUS setzt. Der Schalter geht auch auf ein bei einem ausgehenden Anruf sobald dieser Aktiv (also angenommen) wird!

                Dazu muss man in der Loxone Config einen Baustein vom Typ "Schalter" mit dem Namen "iPhone" anlegen. Dazu einen Benutzer anlegen mit dem Namen "ESP32User" und dem Passwort "ESP32UserPW" (sollte man natürlich anpassen). Am besten erstellt man dann noch einen virtuellen Status Baustein und hängt diesen an den Ausgang des Schalters (den man selbst nicht visualisiert). So sieht man dann eben nur den Status und kann mit dem Schalter an sonsten frei weiter programmieren in der Loxone Config.

                Anzupassen sind fernerhin die Miniserver IP, die LoxberryIP + MQTT credentials (kein muss, kann auch gestrichen werden da aktuell ohnehin keine zuverlässige Funktion, siehe hier). Beim MQTT Broker des Loxberry wäre dann noch das topic "ESP32/#" zu subscriben, und ggf. der Input per advanced settings im incoming monitor auf "disable cache".

                Der Code ist dann auf den ESP zu laden per Arduino IDE und ESP (Version beachten, siehe oben!).. Die LED auf dem Chip zeigt auch den Anruf-Status...

                ------------------------

                Edit: aktueller Code: siehe unten!
                Zuletzt geändert von MarkusCosi; 05.10.2024, 20:14.

                Kommentar

                • MarkusCosi
                  LoxBus Spammer
                  • 28.09.2023
                  • 305

                  #9
                  ...was jetzt noch fehlt wäre die Möglichkeit mit mehreren iPhones / Geräten sich mit dem ESP32 zu verbinden... Hat jemand eine Idee / Ahnung ob das überhaupt technisch funktioniert? Oder ist das BLE pairing immer, so wie der Name suggeriert, zwischen zwei Parteien only?

                  Kommentar

                  • MarkusCosi
                    LoxBus Spammer
                    • 28.09.2023
                    • 305

                    #10
                    ...nach etwas rumstöbern im Netz und ausprobieren habe ich gesehen, dass auch rausgehende Anrufe mit dieser Methode abfangbar sind. Zwar nicht beim Wählen, aber zumindest sobald der Anruf von der Gegenseite angenommen wird und damit aktiv wird. Siehe hier. Es scheint ein feature zu sein, welches von Apple mit iOS 13 hinzugekommen ist und einige gar geärgert hat, siehe hier. Das ganze hat zwar keine Kategorie ID (siehe auch hier), aber man kann die notification über die issuing app (type="com.apple.mobilephone") und die Message = "Aktiver Anruf" eben doch eindeutig identifizieren und so als trigger nutzen.

                    Jedenfalls ermöglicht dies nun insgesamt eine Umsetzung, bei der sowohl eingehende Anrufe als auch ausgehende Anrufe einen Status beim Miniserver setzen, siehe den aktualisierten Code oben.

                    Alternativ könnte man bei rausgehenden Anrufen noch bei iOS über Shortcuts → automation gehen, siehe hier. Dh ein Webhook trigger sobald man die phone app öffnet. Das ist aber natürlich nicht sonderlich praktisch…

                    Was mich noch beschäftigt ist die Frage, ob mehrere iPhones als clients an den BLE server (=ESP32) angeschlossen werden können. Grundsätzlich scheint jedenfalls nichts dagegen zu sprechen, da mehrere notification provider (NP = iPhone) an einen notification client (NC, =ESP32) notifications senden können sollte, und bluetooth mehrere Verbindungen grundsätzlich zulässt, siehe hier, hier, hier, hier. Jedoch scheinen die libraries und die darin definierten BLE Funktionen (in esp32notifications.cpp?)

                    Leider komme ich hier programmiertechnisch an meine Grenzen. Wenn hier jemand mehr ESP32 Programmiererfahrung hätte, insbes. im Bereich BLE / bluetooth, wäre ich für Hilfe dankbar!

                    P.S.: ggf. wäre für reconnects zum Bluetooth folgender Trick noch anzuwenden: ESP.restart(); bei BLE disconnects..
                    Zuletzt geändert von MarkusCosi; 19.09.2024, 22:45.

                    Kommentar

                    • MarkusCosi
                      LoxBus Spammer
                      • 28.09.2023
                      • 305

                      #11
                      den iPhone connected Status mit BLE habe ich noch als weiteren Schalter + virt. Status Baustein eingebunden, wobei ich den Zustand in der Funktion "onBLEStateChanged" setze, sowie auf EIN bei jedem Erhalt einer Nachricht. Zusätzlich setze ich den BLE Status auf 0 wenn der Ping Baustein mir sagt, dass der ESP32 offline bzw. aus ist (der ESP hat eine feste IP über DHCP bekommen).

                      Außerdem habe ich noch per oder die Abfragen "notification->type == "com.apple.mobilephone" auf folgendes geändert, um so selbiges bei Face-Time Anrufen zu erreichen:
                      ((notification->type == "com.apple.mobilephone") || (notification->type == "com.apple.facetime"))

                      und folgendes funktioniert nun zum MQTT publishen des Anrufer Namens:
                      String MQTTMessage = "Anrufer: ";
                      MQTTMessage += notification->title;
                      char* MQTTmessageCString = strdup(MQTTMessage.c_str());
                      client.publish(topicCallName, MQTTmessageCString);


                      ...so schaltet nun bei jedem Anruf die Musik auf Pause und geht nach Ende des Anrufs weiter. Flankenerkennung geht an einen EIB-Schalter der zwischen Play/Pause per Yamaha MusicCast API schaltet...
                      Klicke auf die Grafik für eine vergrößerte Ansicht  Name: iphone1.jpg Ansichten: 0 Größe: 111,9 KB ID: 441781Klicke auf die Grafik für eine vergrößerte Ansicht  Name: iphone2.jpg Ansichten: 0 Größe: 100,8 KB ID: 441782
                      Zuletzt geändert von MarkusCosi; 21.09.2024, 12:45.

                      Kommentar

                      • MarkusCosi
                        LoxBus Spammer
                        • 28.09.2023
                        • 305

                        #12
                        ...nach etwas Probieren habe ich den code noch etwas angepasst und in der Loxone Config eingestellt, dass der ESP einen Fern-Restart bekommt, wenn 1) der ESP online ist + 2) das iPhone im Access Point eingewählt ist wo auch der ESP Empfang haben sollte (nutze das neue Wifi Presence Unifi Plugin für den Loxberry) + 3) BLE keine Verbindung hat. Das schaltet einen Impuls-Geber frei (Off-Eingang geht Aus, Impuls alle 30min) der wiederum einen http-Befehl <<IP>>/R schaltet um den ESP neu zu starten. Das hilft wenn das Telefon mal außer Reichweite war um einen reconnect zu ermöglichen...

                        Anbei der Arduino-Code (grüne Einträge sind anzupassen!):

                        neu: siehe unten
                        Zuletzt geändert von MarkusCosi; vor einer Woche.

                        Kommentar

                        • MarkusCosi
                          LoxBus Spammer
                          • 28.09.2023
                          • 305

                          #13
                          Hallo zusammen,

                          ich hatte mich zuletzt mal wieder diesem Projekt gewidmet. Der Anruf-Detektor funktioniert eigentlich wundervoll und macht genau das was er soll (Anruf triggert Schalter in Loxone, Anrufer Name / Nummer in Loxone etc..).
                          Allerdings "überlebt" das automatische Neu-Verbinden mit dem iPhone nach einmaliger Kopplung nur ein paar Dissoziationen und automatische Re-connects (e.g. iPhone außer Reichweite des ESP32), und danach nicht mehr. Danach muss ich das device zunächst wieder "igorieren" um es dann über eine BLEScanner-App manuell zu koppeln (über das Bluetooth iPhone Menü geht es dann nicht mehr). Danach funktioniert es wieder normal für ein paar mal...

                          Das Problem scheint mir diesem hier ähnlich zu sein: https://github.com/espressif/esp-idf/issues/14977. In einem Post (post 13.12.2024) scheint die Lösung für eine BLE Bibliothek gefunden worden zu sein. Leider gab es keinen Übertrag auf die BLEDevice-Klasse (unbeantwortete Anfragen hier: here / here2 / here3) die ich in diesem Projekt verwende und die mit der ANCS Bibliothek verknüpft ist...

                          Hat vllt. von euch jemand noch eine Idee wie man dieses Problem in den Griff bekommt?

                          Danke!

                          -------------------------------------
                          Anbei der aktuelle Code. (grüne Einträge sind anzupassen):

                          // iPhone: incoming calls to Loxone
                          // -----------------------------------------
                          #include "esp32notifications.h" // Apple ANCS support, see https://www.github.com/Smartphone-Co...ifications.git
                          #include <WiFi.h> // Wifi support
                          #include <WiFiClient.h>
                          #include <HTTPClient.h> // Wifi server support
                          #include <PubSubClient.h> // MQTT
                          #include "esp_bt.h" // for BLE range definition

                          // handle time
                          #include <time.h>
                          time_t now; // this is the epoch
                          tm myTimeInfo; // the structure tm holds time information in a more convient way
                          const char* ntpServer = "pool.ntp.org";
                          const long gmtOffset_sec = 3600; //UTC+1h for Germany
                          const int daylightOffset_sec = 0;

                          // On-chip LED GPIO (depends on used ESP32 board)
                          int LED_BUILTIN = 2;

                          // Wifi SSID and password
                          const char* ssid = "YourSSID";
                          const char* password = "YourSSID_PWD";
                          unsigned long previousMillis = 0;
                          unsigned long interval = 30000; // Define rescan waiting time for Wifi connection outage
                          unsigned long intervalInit = 30000; // Define waiting time for Wifi connection outage
                          unsigned long previousMillisS = 0;
                          const long timeoutTimeS = 1000; // Define timeout for Server connection
                          WiFiServer server(80);

                          // Loxone Miniserver + credentials for user authorized on switch(es)
                          String serverName = "http://MiniserverIP";
                          const char* MiniserverUser = "ESP32UserName";
                          const char* MiniserverPW = "ESP32UserPassword";
                          String LoxoneCallSwitchName = "iPhone";
                          String LoxoneBLESwitchName = "iPhoneBLE";
                          // MQTT Broker: Loxberry
                          const char *mqtt_broker = "LoxberryIP";
                          const char *topicESP32 = "esp32";
                          const char *topicCallName = "esp32/CallName";
                          const char *topicCallTime = "esp32/CallTime";
                          const char *mqtt_username = "YourLoxberryUserName";
                          const char *mqtt_password = "YourMQTTPWD";
                          const int mqtt_port = 1883;
                          unsigned long previousMillisMQTT = 0;
                          unsigned long MQTT_KeepAliveInterval = 10000;

                          // Create an interface to the BLE notification library. May require BLE Scanner App to connect for the first time!
                          BLENotifications notifications;
                          unsigned long previousMillisB = 0;
                          const long intervalB = 30000; // Define interval for BLE-is-ON cmd to Loxone
                          bool BLEConnected = false; // for Web interface only
                          bool CallActive = false; // for Web interface only
                          //esp_ble_gap_read_rssi

                          // Serial monitor for the webpage: 20 lines only
                          String WebSerial[20] = {"","","","","","","","","","","","","","",""," "," ","","",""};
                          const int iLines = 20;
                          WiFiClient espClient;
                          PubSubClient MQTTclient(espClient);

                          // Method to serial print & update array to print to web-serial monitor
                          void Wprint(String printStr, int time01=0) {
                          Serial.print(printStr);
                          if (time01==1) {
                          //add time info to output string
                          time(&now); // read the current time
                          localtime_r(&now, &myTimeInfo); // update the structure tm with the current time
                          printStr = String(myTimeInfo.tm_mday) + "." + String(myTimeInfo.tm_hour) + ":" + String(myTimeInfo.tm_min) + ":" + String(myTimeInfo.tm_sec) + " | " + printStr;
                          }
                          WebSerial[0]+=printStr;
                          }

                          // Method to serial print a line & update array to print a line to web-serial monitor
                          void Wprintln(String printStr, int time01=0) {
                          Serial.println(printStr);
                          if (time01==1) {
                          //add time info to output string
                          time(&now); // read the current time
                          localtime_r(&now, &myTimeInfo); // update the structure tm with the current time
                          printStr = String(myTimeInfo.tm_mday) + "." + String(myTimeInfo.tm_hour) + ":" + String(myTimeInfo.tm_min) + ":" + String(myTimeInfo.tm_sec) + " | " + printStr;
                          }
                          WebSerial[0]+=printStr;
                          for (int i=iLines-1; i>=1; i--) {
                          WebSerial[i]=WebSerial[i-1];
                          }
                          WebSerial[0]="";
                          }

                          // This callback will be called when a Bluetooth LE connection is made or broken.
                          void onBLEStateChanged(BLENotifications::State state) {
                          switch(state) {
                          case BLENotifications::StateConnected:
                          Wprintln("StateConnected - connected to iPhone",1);
                          BLEConnected = true;
                          LoxoneSwitch(1, LoxoneBLESwitchName);
                          break;
                          case BLENotifications::StateDisconnected:
                          Wprintln("StateDisconnected - disconnected from iPhone",1);
                          BLEConnected = false;
                          notifications.startAdvertising(); ///* We need to startAdvertising on disconnection, otherwise the ESP 32 will now be invisible.
                          LoxoneSwitch(0, LoxoneBLESwitchName);
                          Wprintln("Restart ESP32 in 5 sec because of BLE disconnect",1);
                          delay(5000); // possibly resolves ANCS reconnect "5-strikes and out" gray list rule?
                          ESP.restart(); // solves reconnect issue | alternative: trigger restart via http command <<IP>>/R
                          break;
                          }
                          }

                          // Blink LED on Chip
                          void BlinkBlueLED() {
                          digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
                          delay(100);
                          digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
                          delay(50);
                          }

                          // Call (initiated & active) or incoming
                          // A notification arrived from the mobile device, i.e. a social media notification or incoming call.
                          // parameters: notification: an Arduino-friendly structure containing notification information.
                          void onNotificationArrived(const ArduinoNotification * notification, const Notification * rawNotificationData) {
                          // Blink Blue On-Chip LED
                          BlinkBlueLED();
                          // filter out buffered / old notifications
                          if((notification->eventFlags & ANCS::EventFlags::EventFlagPreExisting) != 0) {
                          Wprint("Received PRE-EXISTING notification from app: ",1);
                          Wprint(notification->type,0);
                          Wprintln(" -> discarded",0);
                          } else {
                          // serial print any new incoming notification
                          Wprint("Got NEW notification: title: ",1);
                          Wprint(notification->title,0); // The title, ie name of who sent the message
                          Wprint(", message: ",0);
                          Wprint(notification->message,0); // The detail, ie "be home for dinner at 7".
                          Wprint(", type/app: ",0);
                          Wprint(notification->type,0); // Which app sent it
                          Wprint(", category: ",0);
                          Wprint(notifications.getNotificationCategoryDescri ption(notification->category),0); // ie "social media"
                          Wprint(", time: ",0);
                          Wprint(String(notification->time),0);
                          Wprint(", eventFlags: ",0);
                          Wprintln(String(notification->eventFlags),0);
                          // Set (once per intervalB) Loxone BLE switch to on, i.e. if Notification arrives this necessarily also means: BLE connected!
                          unsigned long currentMillisB = millis(); //works for ca. 50 days before reset to 0
                          if (currentMillisB - previousMillisB >= intervalB) {
                          LoxoneSwitch(1, LoxoneBLESwitchName);
                          previousMillisB = currentMillisB;
                          }
                          if (currentMillisB - previousMillisB < 0) {
                          previousMillisB = currentMillisB;
                          }
                          // filter for action: incoming call
                          if (notification->category == CategoryIDIncomingCall) {
                          Wprintln("||| Incoming Call |||",1);
                          if (MQTTclient.connected()) {
                          // post MQTT message with name/number from caller (depends on phone book)
                          Wprintln("publish Caller & Time to MQTT",1);
                          String MQTTMessage = "Anrufer: ";
                          MQTTMessage += notification->title;
                          char* MQTTmessageCString = strdup(MQTTMessage.c_str());
                          MQTTclient.publish(topicCallName, MQTTmessageCString);
                          // post MQTT message with time of call
                          MQTTMessage = "Time: ";
                          MQTTMessage += notification->time;
                          MQTTmessageCString = strdup(MQTTMessage.c_str());
                          MQTTclient.publish(topicCallTime, MQTTmessageCString);
                          }
                          // set Call-Switch in Loxone: call is "active" / ongoing!
                          CallActive = true;
                          LoxoneSwitch(1, LoxoneCallSwitchName);
                          // LED is ON during incoming call
                          digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
                          }
                          // filter for : outgoing call / face-time becomes active
                          if ((notification->type == "com.apple.mobilephone") || (notification->type == "com.apple.facetime")) {
                          if (notification->message == "Aktiver Anruf") {
                          Wprintln("||| Started Call |||",1);
                          CallActive = true;
                          // set Call Switch in Loxone
                          LoxoneSwitch(1, LoxoneCallSwitchName);
                          // LED is ON during call
                          digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
                          }
                          }
                          }
                          }

                          // A notification was cleared
                          void onNotificationRemoved(const ArduinoNotification * notification, const Notification * rawNotificationData) {
                          Wprint("Removed notification: title: ",1);
                          Wprint(notification->title,0);
                          Wprint(", message: ",0);
                          Wprint(notification->message,0);
                          Wprint(", type: ",0);
                          Wprint(notification->type,0);
                          Wprint(", category: ",0);
                          Wprint(notifications.getNotificationCategoryDescri ption(notification->category),0); // ie "social media"
                          Wprint(", time: ",0);
                          Wprintln(String(notification->time),0);
                          // filter for: incoming call cancelled
                          if (notification->category == CategoryIDIncomingCall) {
                          Wprintln("||| Removed/Rejected/Cancelled Call |||",1);
                          CallActive = false;
                          // Set Call Swith in Loxone
                          LoxoneSwitch(0, LoxoneCallSwitchName);
                          // LED Off
                          digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
                          }
                          // filter for: call ended
                          if ((notification->type == "com.apple.mobilephone") || (notification->type == "com.apple.facetime")) {
                          if (notification->message == "Aktiver Anruf") {
                          Wprintln("||| Finished Call |||",1);
                          CallActive = false;
                          // Set Call Swith in Loxone
                          LoxoneSwitch(0, LoxoneCallSwitchName);
                          // turn LED OFF
                          digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
                          }
                          }
                          }

                          //function to set Switch in Loxone
                          void LoxoneSwitch(int OnOff, String SwitchStr) {
                          HTTPClient http;
                          String serverPath = serverName + "/dev/sps/io/";
                          serverPath = serverPath + SwitchStr + "/";
                          if (OnOff == 0) {
                          serverPath = serverPath + "Aus";
                          Wprintln("> sending GET (Aus) to Miniserver Switch: " + SwitchStr,1);
                          } else {
                          serverPath = serverPath + "Ein";
                          Wprintln("> sending GET (Ein) to Miniserver Switch: " + SwitchStr,1);
                          }
                          http.setAuthorization(MiniserverUser, MiniserverPW);
                          http.begin(serverPath.c_str());
                          int httpResponseCode = http.GET();
                          if (httpResponseCode>0) {
                          Wprint("HTTP Response code: ",1);
                          Wprint(String(httpResponseCode),0);
                          Wprint("; HTTP Payload: ",0);
                          String payload = http.getString();
                          payload.replace(String(char(10)), ""); //Remove /n
                          payload.replace(String(char(13)), ""); //Remove /r
                          Wprintln(payload,0);
                          } else {
                          Wprint("Error code: ",1);
                          Wprintln(String(httpResponseCode),0);
                          }
                          // Free resources
                          http.end();
                          }

                          // Standard Arduino function which is called once when the device first starts up
                          void setup() {
                          // Setup for Serial Monitor (when connected to Arduino IDE: Tools -> Serial Monitor)
                          Serial.begin(9600);
                          while(!Serial) {
                          delay(10);
                          }
                          // Initialize digital pin LED_BUILTIN as an output.
                          pinMode(LED_BUILTIN, OUTPUT);
                          // Connect to WiFi
                          Wprintln("\nConnecting to WiFi",1);
                          WiFi.mode(WIFI_STA);
                          WiFi.begin(ssid, password);
                          Wprint(".",1);
                          while((WiFi.status() != WL_CONNECTED) && (millis() < intervalInit)){
                          Wprint(".",0);
                          delay(500);
                          }
                          if (millis() > intervalInit) {
                          Wprintln("Restart ESP32 because of WiFi couldn't connect.",1);
                          ESP.restart(); //sometimes this may help
                          }
                          Wprintln(".",0);
                          Wprint("\nConnected to the WiFi network: ",1);
                          Wprint(String(ssid),0);
                          Wprint(" | local ESP32 IP: ",0);
                          Wprint(String(WiFi.localIP()),0);
                          Wprint(" | RSSI: ",0);
                          Wprintln(String(WiFi.RSSI()),0);
                          // get time
                          configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
                          // start server to allow control via http commands
                          Wprintln("Starting server / web GUI",1);
                          server.begin();
                          // Setup connection to MQTT
                          MQTTclient.setServer(mqtt_broker, mqtt_port);
                          MQTTclient.setCallback(callback);
                          while (!MQTTclient.connected()) {
                          String client_id = "esp32-client-";
                          client_id += String(WiFi.macAddress());
                          Wprint("The client connects to the public MQTT broker\n",1);
                          Wprint(client_id.c_str(),0);
                          if (MQTTclient.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
                          Wprintln("Loxberry MQTT broker connected. Test publishing next.",1);
                          // Test-Publish via MQTT and subscribe
                          MQTTclient.publish(topicESP32, "ESP32 now publishing to MQTT");
                          MQTTclient.subscribe(topicESP32);
                          } else {
                          Wprint("failed with state ",1);
                          Wprintln(String(MQTTclient.state()),0);
                          delay(2000);
                          }
                          }
                          // initialize Loxone Call active switches to Off
                          LoxoneSwitch(0, LoxoneCallSwitchName);
                          LoxoneSwitch(0, LoxoneBLESwitchName);
                          // Set up the BLENotification library
                          notifications.begin("ESP32 - iPhone Call Detector"); //Batch#2: notifications.begin("ESP32 - iPhone Call Detector2");
                          notifications.setConnectionStateChangedCallback(on BLEStateChanged);
                          notifications.setNotificationCallback(onNotificati onArrived);
                          notifications.setRemovedCallback(onNotificationRem oved);
                          // Blink LED on Chip once on Start
                          BlinkBlueLED();
                          // Increase BLE range
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
                          // ESP_PWR_LVL_N12:-12; .. N9: -9; N6: -6; N3: -3; ESP_PWR_LVL_N0: 0; ESP_PWR_LVL_P9: 9; P6: 6; P3: 3;
                          // By default all three are set to ESP_PWR_LVL_P3, but ESP_PWR_LVL_P9 is max possible level.
                          Wprintln("Setup routine finished",1);
                          }

                          // MQTT receive messages; currently not used
                          void callback(char *topic, byte *payload, unsigned int length) {
                          Wprint("Message arrived in topic: ",1);
                          Wprintln(topic,0);
                          Wprint("Message:",1);
                          for (int i = 0; i < length; i++) {
                          Wprint(String((char) payload[i]),0);
                          }
                          Wprintln("",0);
                          Wprintln("-----------------------",0);
                          }

                          // Standard Arduino function that is called in an endless loop after setup
                          void loop() {
                          unsigned long currentMillis = millis(); //works for ca. 50 days before reset to 0
                          unsigned long currentMillisS = millis(); //works for ca. 50 days before reset to 0
                          unsigned long currentMillisMQTT = millis(); //works for ca. 50 days before reset to 0
                          // if WiFi is down, try reconnecting every interval seconds
                          if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >= interval)) {
                          Wprint(String(millis()),1);
                          Wprintln("Reconnecting to WiFi...",0);
                          WiFi.disconnect();
                          WiFi.reconnect();
                          previousMillis = currentMillis;
                          }
                          // get commands from external http command to server
                          WiFiClient Webclient = server.available();
                          if (Webclient) {
                          previousMillisS = currentMillisS;
                          Serial.println("New (Web-)Client");
                          String currentLine = "";
                          while (Webclient.connected() && (currentMillisS - previousMillisS <= timeoutTimeS)) {
                          currentMillisS = millis();
                          if (Webclient.available()) {
                          char c = Webclient.read();
                          Serial.write(c);
                          if (c == '\n') {
                          if (currentLine.length() == 0) {
                          // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
                          // and a content-type so the client knows what's coming, then a blank line:
                          Webclient.println("HTTP/1.1 200 OK");
                          Webclient.println("Content-type:text/html");
                          // client.println("Connection: close");
                          Webclient.println();
                          // create the buttons of the web interface
                          Webclient.print("ESP32 iPhone Call Detector: <br><br>");
                          Webclient.print("<a href="/R">ESP32 Module: Restart</a><br>");
                          Webclient.print("<a href="/B">ESP32 On-Chip LED: Blink!</a><br>");
                          Webclient.print("<a href="/P9">BLE-Range: P9 (Max)</a><br>");
                          Webclient.print("<a href="/P3">BLE-Range: P3 (default)</a><br><br>");
                          Webclient.print("Call active: " + String(CallActive) + "<br>");
                          Webclient.print("BLE-State: " + String(BLEConnected) + "<br>");
                          Webclient.print("WiFi-RSSI: " + String(WiFi.RSSI()) + "<br><br>");
                          //print Serial Monitor Output to webpage
                          Webclient.print("<span style=font-family:courier;font-size:50%>Serial Monitor:<br>");
                          //Webclient.print("Serial Monitor:<br>");
                          Webclient.print("--------------------------------------<br>");
                          for (int i=0; i<=iLines-1; i++) {
                          Webclient.print(WebSerial[i] + "<br>");
                          }
                          Webclient.print("--------------------------------------");
                          // The HTTP response ends with another blank line:
                          Webclient.println();
                          Webclient.print("</span>");
                          // break out of the while loop:
                          break;
                          } else {
                          currentLine = "";
                          }
                          } else if (c != '\r') {
                          currentLine += c;
                          }
                          // Execute GET commands / buttons pressed
                          if (currentLine.endsWith("GET /R")) {
                          Wprintln(" command received to restart ESP32",1);
                          LoxoneSwitch(0, LoxoneBLESwitchName);
                          ESP.restart();
                          }
                          if (currentLine.endsWith("GET /B")) {
                          Wprintln(" command received to blink on-chip LED on ESP32",1);
                          BlinkBlueLED(); BlinkBlueLED(); BlinkBlueLED();
                          }
                          if (currentLine.endsWith("GET /P9")) {
                          Wprintln(" command received to set BLE strength to P9 (max range)",1);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
                          }
                          if (currentLine.endsWith("GET /P3")) {
                          Wprintln(" command received to set BLE strength to P3 (default)",1);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P3);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P3);
                          esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P3);
                          }
                          }
                          }
                          Webclient.stop();
                          Serial.println("(Web-)Client disconnected");
                          }
                          // MQTT keep alive
                          if (currentMillisMQTT - previousMillisMQTT > MQTT_KeepAliveInterval) {
                          previousMillisMQTT = currentMillisMQTT;
                          //Wprintln("MQTT loop to keep alive");
                          MQTTclient.loop();
                          }
                          // handle the reset of long variables to 0 every 50 days or so...
                          if (currentMillis - previousMillis < 0) {
                          previousMillis = currentMillis;
                          previousMillisS = currentMillis;
                          previousMillisMQTT = currentMillis;
                          }
                          }​

                          Kommentar

                          Lädt...