iOS15 und Webinterface

Hi,

ich habe gerade festgestellt, dass der ESPuino rebootet, wenn man mit iOS15 (Apple) auf das Webinterface zugreift.
Da ich gerade viel am Webinterface ändere, bin ich davon ausgegangen, dass es an meinen Änderungen liegt.
Der Fehler tritt aber auch bei einer älteren ESPuino - Version auf.
Es hängt mit der Websocket-Verbindung zusammen. Hier gibt es einige Hinweise:
https://developer.apple.com/forums/thread/685403?page=2
Auch der dort beschriebene Workaround hilft bei mir.
Setting safari =>Advanced=>Experimental Features=>NSURLSession WebSocket to off solves the issue

Ui, danke für den Hinweis.

Was treibst denn so? :slight_smile:

Hallo Christian,

Danke für Deinen Fehlerbericht! Ich habe exakt das gleiche Problem:
Habe auch an der Weboberfläche rumgechraubt, teste gerade das Feature „Album cover image“, also die Bildanzeige des aktuellen Tracks in der Websteuerung. Soll so ähnlich werden wie die Spotify Steuerung. Hat bislang auch schon gut funktioniert. Jetzt bekomme ich auch immer einen Crash beim Aufrufen der Webseite vom iPhone. Ich dachte der Fehler wurde durch eine gecachte Webseite ausgelöst…Werde Deinen Workarround testen! @biologist wenn’s fertig ist gibt es einen PR wenn Du möchtest…

Schöne Grüße
Dirk

@tueddy Ja sehr gerne. Ich habe kein iPhone, so dass ich den Fehler nicht nachstellen kann

Der Workaround klappt auch bei mir.

1 „Gefällt mir“

bei mir gehts jetzt auch

1 „Gefällt mir“

Websocket

  • Ich habe mir noch einmal die Websocket-Verbindung für den Filebrowser angesehen. Das läuft nun recht zuverlässig. Es wird jetzt nicht mehr pro Eintrag/File eine Websocket-Nachricht verschickt, sondern Blöcke mit mehreren Einträgen.
  • Andere Verbindungen ebenfalls auf Websocket (ohne JSON) umstellen z.B. auch die Presets für Laustärke usw. Da hängt halt leider einiges dran…
  • Regelmäßiges Update für das Webinterface. Lautstärke, aktuelle Position im Titel

Steuerung

  • Cover Titel @tueddy Wie hast du das genau gemacht? Ich lese im Callback der Audio-Lib das MP3 Cover Image aus und schreibe es direkt auf die SD-Karte. Das funktionierte bei 2 von 3 MP3 sehr gut. Hab da aber erst einmal nicht mehr weitergemacht. Mein Code erscheint mir irgendwie recht experimentiell :smiley:
  • LED Helligkeit
  • Sleep-Timer (unfertig)
  • Wiederholung (unfertig)
  • Taskensperre (unfertig)
  • Favoriten (unfertig). Hier soll man beliebige Titel direkt abspielen können

Offline - Done

  • Alle notwendigen JS-Libs (JQuery / jstree) werden in die HTML-Seite „inline“ integriert . Es ist damit keine Internet-Verbindung mehr notwendig. Da muss man mal sehen, wie man mit der Größe hinkommt. Das Skript optimiert ziemlich viel (purify_css, uglify_js, htmlmin) und ich habe die Libs verschlankt (nicht alle JQuery / Bootstrap Module, eigener Font für die Icons). Anschliessend wird die Datei per gzip gepackt und als Byte-Array in ein Header-File gepackt. Es wird also eine gzip html Datei an den Browser geliefert. Die gzip-html-Datei hat zur Zeit 110 KB. → Schon deshalb konnte ich die Template-Funktion vom async-webserver nicht mehr nutzen.

Kosmetik - Done

  • Slider verändert
  • Nav-Menue kombiniert
  • Wifi-RSSI Wert als Grafik anzeigen
  • Übertragunsrate (Upload) anzeigen
  • Einstellungen unter „Allgemein“ in einem Accordion

Ist aber insgesamt noch einiges zu tun… :smiley:

4 „Gefällt mir“

Holla die Waldfee! Bin schon gespannt!

@Christian, genauso hab ich’s gemacht:

  • Das MP3 Bild über die Metadaten-Callback auf SD-Karte gespeichert. Vor dem Abspielen diese Datei gelöscht da nicht alle MP3 auch ein Coverbild haben.
  • Dateiname .coverimg, mit Punkt damit die Bilddatei im Filebrowser nicht angezeigt wird.
  • Eine Webmethode /coverimg die diese Daten bereitstellt. Das Bildformat is egal, der Browser macht das schon egal ob PNG/JPG. Bei fehlender Datei (kein Cover verfügbar) ein Dummy-SVG bereitgestellt.
  • Über Websocket das Bild in Echtzeit aktualisiert, das ist ja bereits beispielhaft verfügbar ( /getTrack)

Finde es auch charmant wenn sich die Buttons Play/Pause/Next undVolume usw. in Echtzeit aktualisieren, hatte dazu auch schon einen Test gemacht bis diese üblen Abstürze kamen und ich den Fehler in meinem Code gesucht habe…

@Christian Wie hast du die Integration der JS-Libs eigentlich gemacht? Mario hatte mal für sowas Webpack vorgeschlagen. Ich habe mir nie wirklich Zeit genommen, um es mal ernsthaft umzusetzen. Habe mich jedoch gefragt, ob es für diese Zwecke ggf. etwas Overkill ist.

Was er ja damals auch noch vorgeschlagen hat ist as hier: GitHub - wikimedia/jquery.i18n: 🌐jQuery based internationalization library. Aber waren halt irgendwie immer andere Sachen wichtiger :woman_shrugging:. Ich erwähne das nur deswegen, weil vielleicht kann man das gleich in den „JS-Brocken“ mit reinnehmen, auch wenn man es jetzt noch nicht benötigt. Er hatte das exemplarisch auch mal gestartet. Achtung: Der Stand ist fast ein Jahr alt. Geht also nur darum, es mal zu zeigen.

Ich habe das auch als Overkill eingeschätzt.
Ich habe folgendes gemacht:

  • HTML, CSS und JS in eigene Dateien (u.a. Voraussetzung um effektiv komprimieren zu können)
  • Alle notwendigen Libs „lokal“ verfügbar gemacht (Voraussetzung für die Optimierungs-Skripts)
  • Libs / CSS versucht zu verschlanken
    • nicht alle Bootstrap-, JQuery, JSTree Module verwenden
    • Slider Lib entfernt (und durch vorhandene Komponenten ersetzt)
    • Notwendige Icons aus Font Awesome extrahiert (https://icomoon.io/)
  • Dann kommt das Skript
    • Libs / CSS / Images in das HTML integrieren „inlinen“. Es entsteht eine HTML-Datei die alle Komponenten enthält. Dabei werden JS, CSS und HTML „minimiert / uglify“
    • HTML-Datei gzippen
    • gzip → byte array erstellen

Das hier war meine Inspiration : Embed Your Website in Your ESP8266 Firmware Image - Tinkerman
Hab das ergänzt / angepasst and die aktuelle GULP-Version:

/*

ESP8266 file system builder

Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>;

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

// -----------------------------------------------------------------------------
// File system builder
// -----------------------------------------------------------------------------

const fs = require('fs');
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
const cleancss = require('gulp-clean-css');
const uglify = require('gulp-uglify');
const gzip = require('gulp-gzip');
const del = require('del');
const inline = require('gulp-inline');
const inlineImages = require('gulp-css-base64');
const favicon = require('gulp-base64-favicon');
const { series } = require('gulp');
const purify = require('gulp-purify-css');
const { watch } = require('gulp');
 

const dataFolder = 'test/';

async function clean() {
    del([ dataFolder + '*']);
    
}

function buildfs_embeded(done) {

    var source = dataFolder + 'management_DE.html.gz';
    var destination = '../src/management_DE.html.gz.h';

    var wstream = fs.createWriteStream(destination);
    wstream.on('error', function (err) {
        console.log(err);
    });

    var data = fs.readFileSync(source);

    wstream.write('#define index_html_gz_len ' + data.length + '\n');
    wstream.write('const uint8_t index_html_gz[] PROGMEM = {')

    for (i=0; i<data.length; i++) {
        if (i % 1000 == 0) wstream.write("\n");
        wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
        if (i<data.length-1) wstream.write(',');
    }

    wstream.write('\n};')
    wstream.end();

    //del([source]);
	done();

}

 

function buildfs_inline() {
    return gulp.src('../html/DE/*.html')
        .pipe(favicon())
        .pipe(inline({
            base: '../html/',
            js: uglify,
            css: [cleancss, inlineImages],
            disabledTypes: ['svg', 'img']
        }))
        .pipe(htmlmin({
            collapseWhitespace: true,
            removecomments: true,
            aside: true,
            minifyCSS: true,
            minifyJS: true
        }))
        .pipe(gzip())
        .pipe(gulp.dest(dataFolder));
}

function purify_css() {
    return gulp.src('../html/DE/css/*.css')
        .pipe(purify(['../html/DE/*.js', '../html/DE/*.html','../html/DE/js/*.js']))
        .pipe(gulp.dest('../html/DE/css_purify/'));
}

watch(['../html/DE/css/*.css'], purify_css );
exports.default = series(clean,purify_css,buildfs_inline,buildfs_embeded);

Sieht dann aktuell so aus: Da sieht man auch direkt die Größe.

#define index_html_gz_len 110608
const uint8_t index_html_gz[] PROGMEM = {
0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xcc,0xbd,0xe9,0x72,0xdb,0x48,0xb6,0x2e,0xfa,0xff,0x3c,0x85,0x88,0xf6,0x56,0x03,0x66,0x92,0xa6,0x5c,0x5d,0x75,0x76,0x83,0x82,0x19,0x2e,0x0f,0x65,0xd7,0x64,0xb7,0xed,0x9a,0x9a,0x62,0x39,0x20,0x32,0x25,0xa1,0x0c,0x01,0x2c,0x00,0xb4,0xa4,0x12,0xb9,0xdf,0xec,0xc4,0x7d,0xa4,0xfb,0x0a,0x77,0x7d,0x2b,0x07,0x24,0x06,0xaa,0xdc,0xbd,0xf7,0x89,0xb8,0x8e,0xb0,0x88,0x21,0x91,0xe3,0xca,0x95,0x6b,0x5e,0xc7,0x83,0xa7,0xaf,0x9e,0xbc,0xfb,0xe5,0xf5,0xb3,0x83,0x8b,0xea,0x32,0x7d,0x74,0x8c,0xbf,0x07,0x69,0x9c,0x9d,0x47,0xde,0x4a,0x7a,0x74,0x2f,0xe3,0xd5,0xa3,0xe3,0x2a,0xa9,0x52,0xf9,0xe8,0xd9,0xdb,0xd7,0x9b,0x24,0xcb,0x47,0
...

Vielleicht vorweg: Das Skript ist jetzt so aufgebaut, dass zusätzliche Libs sehr einfach ergänzt werden können. Wenn die Webseite lokal „offline“ funktioniert, übersetzt das Skript einfach alles.

Aber: Ich in den letzten Wochen die Mehrsprachlichkeit konsequent ignoriert und wollte das eh mal zur Diskussion stellen. Ich meine, dass eine Sprachversion ausreichend ist und der Aufwand sehr hoch ist.
So viel Text ist es am Ende nicht und Wörter wie „Play“ „Volume“ „Power“ haben sich ja sehr ins Deutsche gedrängt. Wenn man dann z.B. „erfolgreich gespeichert“ durch „okay“ ersetzt, passt es für beide Sprachen. Oder einfach nur Deutsch?!