Skip to content
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Logo
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. [Vorlage] Spotify Skript

NEWS

  • Wartung am 15.11. – Forum ab 22:00 Uhr nicht erreichbar
    BluefoxB
    Bluefox
    11
    2
    198

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    7.9k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    1.7k

[Vorlage] Spotify Skript

[Vorlage] Spotify Skript

Scheduled Pinned Locked Moved Skripten / Logik
javascript
745 Posts 95 Posters 264.4k Views 41 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    L Offline
    Lucky
    wrote on last edited by Bluefox
    #1

    Moin,

    ich habe mal ein Skript begonnen mit dem Spotify gesteuert werden kann,

    Damit das Skript genutzt werden kann ist folgendes vorher zutun

    • Es muß das NPM Modul querystring im Javascript Adapter hinzugefügt werden !
    • Es wird ein Premium Account benötigt, mit Spotify Family sollte es auch laufen (gem. Angaben von Leuten die es getestet haben)
    • Sonos Geräte werden NICHT zu 100% unterstützt, was durch die API bedingt ist !

    1.Registriere dich auch https://developer.spotify.com

    2.Erstelle einen Application, du erhällst einen Client ID und eine Client Secret

    3.trage in den App Settings deiner Application bei Redirect URIs 'http://localhost' ein

    4.trage hier in diesem Skript deine Cliend ID und Client Secret ein

    5.Starte dieses Skript

    6.wechsle zum Tap Objekte und klicke unter javascript.0.Spotify.Authorization.Authorized auf den Button Get_Authorization

    7.Kopiere die unter javascript.0.Spotify.Authorization.Authorization_URL angezeigte URL in einen Webbrowser und rufe sie auf.

    8.Der Browser wird die Verbindung ablehnen und in der Adresszeile eine URL zurückgeben

    9.kopiere jetzt wider diese URL und füge sie im State 'javascript.0.Spotify.Authorization.Authorization_Return_URI' ein

    ab jetzt gibte es die neueste Version nur noch hier !

    change log

    0.5.3

    -Stabilität verbessert

    -Tracks zusätzlich als Listenstring

    Version 0.5.3 getestet mit JS 3.6.3 (edited by Bluefox)

    /*Version 0.5.3
    letzte änderung 11.02.2018 19:25
    Read Me !!!!!!!
    wie bekomme ich dieses Skript zum laufen ? 
    Es muß das NPM Modul querystring im Javascript Adapter hinzugefügt werden !
    1.Registriere dich auch https://developer.spotify.com
    2.Erstelle einen application, du erhällst einen Client ID und eine Client Secret
    3.trage in den App Settings deiner application bei Redirect URIs 'http://localhost' ein
    4.trage hier in diesem Skript deine Cliend ID und Client Secret ein
    5.Starte dieses Skript
    6.wechsle zum Tap Objekte und klicke unter 'javascript.0.Spotify.Authorization.Authorized' auf den Button Get_Authorization
    7.Kopiere die unter 'javascript.0.Spotify.Authorization.Authorization_URL' angezeigte URL in einen  Webbrowser und rufe sie auf.
    8.Der Browser wird die Verbindung ablehnen und in der Adresszeile eine URL zurückgeben
    9.kopiere jetzt wider diese URL und füge sie im State 'javascript.0.Spotify.Authorization.Authorization_Return_URI' ein
    10.wenn alles funktioniert hat wechselt 'javascript.0.Spotify.Authorization.Authorized' auf true 
    */
    const request = require('request');
    const querystring = require('querystring');
    const fs = require('fs');
    
    createState('javascript.0.Spotify.Player.Play', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Pause', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Skip_Plus', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Skip_Minus', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Repeat_Track', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Repeat_Context', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Repeat_off', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Player.Volume', 0, {type: 'number', role: 'Volume %'});
    createState('javascript.0.Spotify.Player.TrackId', '', {type: 'string', role: 'Track Id to Play'});
    createState('javascript.0.Spotify.Player.Playlist_ID', '', {type: 'string', role: 'Playlist Id to Play'});
    createState('javascript.0.Spotify.Player.Seek', 0, {type: 'number', role: 'Seek To Position (s)'});
    createState('javascript.0.Spotify.Player.Shuffle', false, {type: 'boolean', role: 'Shuffle'});
    createState('javascript.0.Spotify.Devices.Get_Devices', false, {type: 'boolean', role: 'button'});
    //createState('javascript.0.Spotify.Authorization.Login', false,{type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Authorization.Get_Authorization', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.Authorization.Authorization_URL', '', {
        type: 'string',
        role: 'Authorization_URL',
        write: false
    });
    createState('javascript.0.Spotify.Authorization.Authorization_Return_URI', '', {
        type: 'string',
        role: 'Authorization_Return_URI'
    });
    createState('javascript.0.Spotify.Authorization.User_ID', '', {type: 'string', role: 'User ID', write: false});
    createState('javascript.0.Spotify.Authorization.Authorized', false, {
        type: 'boolean',
        role: 'Authorized',
        write: false
    });
    createState('javascript.0.Spotify.Get_User_Playlists', false, {type: 'boolean', role: 'button'});
    createState('javascript.0.Spotify.PlaybackInfo.Track_Id', '', {type: 'string', role: 'Track Id', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Artist_Name', '', {type: 'string', role: 'Artist Name', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Type', '', {type: 'string', role: 'Type', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Album', '', {type: 'string', role: 'Album', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.timestamp', 0, {type: 'number', role: 'Timestamp', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.progress_ms', 0, {type: 'number', role: 'progress_ms', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.progress', 0, {type: 'string', role: 'progress', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.is_playing', false, {type: 'boolean', role: 'is_playing', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.image_url', '', {type: 'string', role: 'Image URL', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Track_Name', '', {type: 'string', role: 'Track_Name', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.duration_ms', 0, {type: 'number', role: 'Duration ms', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.duration', 0, {type: 'string', role: 'duration', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Playlist', '', {type: 'string', role: 'Playlist', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Device.id', '', {type: 'string', role: 'id', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Device.is_active', false, {
        type: 'boolean',
        role: 'is active',
        write: false
    });
    createState('javascript.0.Spotify.PlaybackInfo.Device.is_restricted', false, {
        type: 'boolean',
        role: 'is restricted',
        write: false
    });
    createState('javascript.0.Spotify.PlaybackInfo.Device.name', '', {type: 'string', role: 'Name', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Device.type', '', {type: 'string', role: 'Type', write: false});
    createState('javascript.0.Spotify.PlaybackInfo.Device.volume_percent', 0, {
        type: 'number',
        role: 'volume_percent',
        write: false
    });
    //createState('javascript.0.Spotify.Playlist_Names','',{type: 'string', role: 'String of Playlist Names',write:false});
    //createState('javascript.0.Spotify.Playlist_Index',0 ,{type: 'number', role: 'Playlist_Index'});
    const application = {
        User_ID: '',//Nichts eintragen !!
        BaseURL: 'https://api.spotify.com',
        Client_ID: 'HIER DEINE CLIENT ID !!',
        Client_Secret: 'HIER DEIN CLIENT SECRET',
        redirect_uri: 'http://localhost',   // in älteren Versionen wird 'https://example.com/callback/' verwendet, 'http://localhost' ist eine Sichere Variante
        Token: '', //Nichts eintragen !!
        refresh_token: '',//Nichts eintragen !!
        code: '',//Nichts eintragen !!
        State: '',//Nichts eintragen !!
        TokenFilePath: '/opt/Spotify.token'
    };
    
    const deviceData = {
        last_active_device_id: '',
        last_select_device_id: '',
    };
    
    let interval;
    
    //############### Initial ##########
    readTokenFiles((err, Token) => { //23.01.2018 Funktion überarbeitet 
        if (!err) {
            application.Token = Token.AccessToken;
            application.refresh_token = Token.RefreshToken;
            sendRequest('/v1/me', 'GET', '', (err, data) => {
                if (!err) {
                    GetUserInformation(data);
                    setState('javascript.0.Spotify.Authorization.Authorized', true, true);
    
                    sendRequest('/v1/me/player/devices', 'GET', '', (err, data) => !err && createDevices(data));
                } else {
                    setState('javascript.0.Spotify.Authorization.Authorized', false, true);
                    console.error('sendRequest in readTokenFiles ' + err);
                }
            });
        } else {
            setState('javascript.0.Spotify.Authorization.Authorized', false, true);
            console.warn(err);
        }
    });
    
    //#################################
    function readTokenFiles(callback) {
        fs.readFile(application.TokenFilePath, 'utf8', (err, data) => {
            if (!err) { //wenn keine Fehler
    
                const Token = JSON.parse(data);
                const ATF = 'undefined' !== typeof  Token.AccessToken && (Token.AccessToken !== '');
                const RTF = 'undefined' !== typeof  Token.RefreshToken && (Token.RefreshToken !== '');
    
                if (ATF && RTF) {
                    console.log('Spotify Token aus Datei gelesen !');
                    return callback(null, Token);
                } else {
                    return callback('Keine Token in Datei gefunden !', null)
                }
            } else {
                console.log(err);
                return callback('keine Token-Datei gefunden !, wird erstellt nach Autorisierung  ', null);
            }
        });
    }// End of Function readTokenFiles 
    
    //###################################################################################### FUNCTION SEND REQUEST ###################################################################################
    function sendRequest(endPoint, method, body, callback) {
        method = method || 'PUT';
        body = body || '';
        
        const options = {
            url: application.BaseURL + endPoint,
            method: method,
            headers: {Authorization: 'Bearer ' + application.Token},
            form: body
        };
        // console.log(options.form);
        // console.log('Spotify API Call...'+ endPoint);
        request(options, (error, response, body) => {
            if (!error) {
                switch (response.statusCode) {
                    case 200: // OK
                        return callback && callback(null, JSON.parse(body));
    
                    case 202: //Accepted, processing has not been completed.
                        return callback && callback(response.statusCode, null);
                    case 204: // OK, No Content
                        return callback && callback(null, null);
    
                    case 400: //Bad Request, message body will contain more information
                    case 500: //Server Error
                    case 503: //Service Unavailable
                    case 404: //Not Found
                    case 502: //Bad Gateway
                        return callback && callback(response.statusCode, null);
    
                    case 401: //Unauthorized 
                        if (JSON.parse(body).error.message === 'The access token expired') {
                            console.log('Access Token Abgelaufen!!');
                            setState('javascript.0.Spotify.Authorization.Authorized', false, true);  // neu 05.02.2018
    
                            refreshToken(err => {
                                if (!err) {
                                    setState('javascript.0.Spotify.Authorization.Authorized', true, true);
    
                                    sendRequest(endPoint, method, body, (err, data) => { // dieser Request holt die Daten die zuvor mit altem Token gefordert wurden
                                        if (!err) {
                                            console.log('Daten mit neuem Token');
                                            return callback && callback(null, data);
                                        } else if (err == 202) {
                                            console.log(err + ' Anfrage akzeptiert, keine Daten in Antwort, veruch es nochnal ;-)');
                                            return callback && callback(err, null);
                                        } else {
                                            console.error('FEHLER BEIM ERNEUTEN DATEN ANFORDERN ! ' + err);
                                            return callback && callback(err, null);
                                        }
                                    });
    
                                } else {  //05.02.2018 19:43
                                    console.error(err);
                                    return callback && callback(err, null);
                                }
                            });
                        } else { //wenn anderer Fehler mit Code 401
                            setState('javascript.0.Spotify.Authorization.Authorized', false, true);  // neu 05.01.2018
                            console.error(JSON.parse(body).error.message);
                            return callback && callback(response.statusCode, null);
                        }
                        break;
    
                    default:
                        console.warn('HTTP Request Fehler wird nicht behandelt, bitte Debuggen !!');
                        return callback && callback(response.statusCode, null);
                }
            } else {
                console.error('erron in Request');
                return callback && callback(0, null);
            }
        });//end Request
    }//End of Function sendRequest
    //###################################################################################### END OF FUNCTION SEND REQUEST ###################################################################################
    
    function createPlaybackInfo(pBody) {
    
        //console.log(JSON.stringify(pBody))
    
        if (pBody.hasOwnProperty('device')) {
            deviceData.last_active_device_id = pBody.device.id;
            setState('javascript.0.Spotify.PlaybackInfo.Device.id', pBody.device.id, true);
        }
    
        if (pBody.hasOwnProperty('is_playing')) {
            setState('javascript.0.Spotify.PlaybackInfo.is_playing', pBody.is_playing, true);
            if (pBody.is_playing === true) {
    
                setState('javascript.0.Spotify.PlaybackInfo.Track_Id', pBody.item.id, true);
                setState('javascript.0.Spotify.PlaybackInfo.Artist_Name', pBody.item.artists[0].name, true);
                if (pBody.context !== null) {
    
                    setState('javascript.0.Spotify.PlaybackInfo.Type', pBody.context.type, true);
    
                    if (pBody.context.type === 'playlist') {
                        const IndexOfUser = pBody.context.uri.indexOf('user:') + 5;
                        const EndIndexOfUser = pBody.context.uri.indexOf(':', IndexOfUser);
    
                        const IndexOfPlaylistID = pBody.context.uri.indexOf('playlist:') + 9;
    
                        const query = {
                            fields: 'name',
                        };
    
                        sendRequest('/v1/users/' + pBody.context.uri.substring(IndexOfUser, EndIndexOfUser) + '/playlists/' + pBody.context.uri.slice(IndexOfPlaylistID) + '?' + querystring.stringify(query), 'GET', '', (err, pBody) => {
                            if (!err && pBody.hasOwnProperty('name')) {
                                setState('javascript.0.Spotify.PlaybackInfo.Playlist', pBody.name, true);
                                //console.log(JSON.stringify(pBody))
                            } else {
                                console.warn(err + ' function createPlaybackInfo')
                            }
                        });
                    }
                    else {
                        setState('javascript.0.Spotify.PlaybackInfo.Playlist', '', true)
                    }
    
                }
                else {
                    setState('javascript.0.Spotify.PlaybackInfo.Type', pBody.item.type, true);
                    setState('javascript.0.Spotify.PlaybackInfo.Playlist', '', true);
                }
    
                setState('javascript.0.Spotify.PlaybackInfo.Album', pBody.item.album.name, true);
                setState('javascript.0.Spotify.PlaybackInfo.timestamp', pBody.timestamp, true);
                setState('javascript.0.Spotify.PlaybackInfo.progress_ms', pBody.progress_ms, true);
                setState('javascript.0.Spotify.PlaybackInfo.image_url', pBody.item.album.images[0].url, true);
                setState('javascript.0.Spotify.PlaybackInfo.Track_Name', pBody.item.name, true);
                setState('javascript.0.Spotify.PlaybackInfo.duration_ms', pBody.item.duration_ms, true);
    
                setState('javascript.0.Spotify.PlaybackInfo.duration', digiClock(pBody.item.duration_ms), true);
                setState('javascript.0.Spotify.PlaybackInfo.progress', digiClock(pBody.progress_ms), true);
    
                setState('javascript.0.Spotify.PlaybackInfo.Device.is_active', pBody.device.is_active, true);
                setState('javascript.0.Spotify.PlaybackInfo.Device.is_restricted', pBody.device.is_restricted, true);
                setState('javascript.0.Spotify.PlaybackInfo.Device.name', pBody.device.name, true);
                setState('javascript.0.Spotify.PlaybackInfo.Device.type', pBody.device.type, true);
                setState('javascript.0.Spotify.PlaybackInfo.Device.volume_percent', pBody.device.volume_percent, true);
    
            }
        }
    }//End of Function createPlaybackInfo
    
    function digiClock(ms) {
        // Milisekunden zu Digitaluhr, Beispiel 3:59=238759
        let Min = Math.floor(ms / 60000);
        let Sec = Math.floor(((ms % 360000) % 60000) / 1000);
        if (Min < 10) {
            Min = '0' + Min
        }
        if (Sec < 10) {
            Sec = '0' + Sec
        }
        return Min + ':' + Sec;
    }//End Function digiClock
    
    function GetUserInformation(pBody) {
        application.User_ID = pBody.id;
        setState('javascript.0.Spotify.Authorization.User_ID', pBody.id, true);
    
    }//End of Function GetUserInformation
    
    function GetUsersPlaylist(offset) {
    
        let PlaylistString;
    
        if (application.User_ID !== '') {
            const query = {
                limit: 30,
                offset: offset
            };
    
            sendRequest('/v1/users/' + application.User_ID + '/playlists?' + querystring.stringify(query), 'GET', '', (err, pBody) => {
                if (!err) {
                    for (let i = 0; i < pBody.items.length; i++) {
                        const Pfad = 'javascript.0.Spotify.Playlists.' + pBody.items[i].name.replace(/\s+/g, '');
                        PlaylistString = pBody.items[i].name + ';' + PlaylistString;
    
                        if (getObject(Pfad + '.id') === null) {
                            createState(Pfad + '.Play_this_List', false, {type: 'boolean', role: 'button'});
                            createState(Pfad + '.id', pBody.items[i].id, {type: 'string', role: 'id', write: false});
                            createState(Pfad + '.owner', pBody.items[i].owner.id, {
                                type: 'string',
                                role: 'owner',
                                write: false
                            });
                            createState(Pfad + '.name', pBody.items[i].name, {type: 'string', role: 'Name', write: false});
                            createState(Pfad + '.tracks_total', pBody.items[i].tracks.total, {
                                type: 'number',
                                role: 'tracks_total',
                                write: false
                            });
    
                        } else {
                            setState(Pfad + '.id', pBody.items[i].id, true);
                            setState(Pfad + '.owner', pBody.items[i].owner.id, true);
                            setState(Pfad + '.name', pBody.items[i].name, true);
                            setState(Pfad + '.tracks_total', pBody.items[i].tracks.total, true);
                        }
                        getPlaylistTracks(pBody.items[i].owner.id, pBody.items[i].id, Pfad);
                    }
                    if (pBody.items.length !== 0 && (pBody['next'] !== null)) {
                        GetUsersPlaylist(pBody.offset + pBody.limit)
                    }
    
                    //setState('javascript.0.Spotify.Playlist_Names',PlaylistString);
                }
    
            });
        }
    } // End of Function GetUsersPlaylist
    function deviceHandel(deviceData) {
        if (deviceData.last_select_device_id === '') {
            return deviceData.last_active_device_id;
        } else {
            return deviceData.last_select_device_id;
        }
    }
    
    function getPlaylistTracks(owner, id, Pfad) {   //NEU
        const reg_param = owner + '/playlists/' + id + '/tracks';
        const query = {
            fields: 'items.track.name,items.track.id,items.track.artists.name,total,offset',
            limit: 100,
            offset: 0
        };
    
        sendRequest('/v1/users/' + reg_param + '?' + querystring.stringify(query), 'GET', '', (err, data) => {
            if (!err) {
                let StateString = '';
                let ListString = '';
                let Track_ID_String = '';
    
                for (let i = 0; i < data.items.length; i++) {
    
                    StateString = StateString + i.toString() + ':' + data.items[i].track.name + '-' + data.items[i].track.artists[0].name + ';';
                    ListString = ListString + data.items[i].track.name + '-' + data.items[i].track.artists[0].name + ';';
                    Track_ID_String = Track_ID_String + i.toString() + ':' + data.items[i].track.id + ';';
                }
                if (getObject(Pfad + '.Track_List') === null) {
                    createState(Pfad + '.Track_List', -1, {
                        type: 'number',
                        role: 'Tracks',
                        states: StateString,
                        Track_ID: Track_ID_String
                    });
                } else {
                    //setState(Pfad+'.Track_List',StateString,Track_ID=Track_ID_String,akt=true);
                }
    
                if (getObject(Pfad + '.Track_List_String') === null) {
                    createState(Pfad + '.Track_List_String', ListString, {type: 'string', role: 'Tracks List String'});
                } else {
                    setState(Pfad + '.Track_List_String', ListString, true);
                }
    
            }
    
        });
    }//End of Function getPlaylistTracks
    function createDevices(pBody) {
    
        for (i = 0; i < pBody.devices.length; i++) {
    
            for (const ObjName in pBody.devices[i]) {
    
                if (!getObject('javascript.0.Spotify.Devices.' + pBody.devices[i].name.replace(/\s+/g, '') + '.' + ObjName)) {
    
                    createState('javascript.0.Spotify.Devices.' + pBody.devices[i].name.replace(/\s+/g, '') + '.' + ObjName, pBody.devices[i][ObjName], {
                        type: typeof pBody.devices[i][ObjName],
                        role: ObjName
                    });
                    createState('javascript.0.Spotify.Devices.' + pBody.devices[i].name.replace(/\s+/g, '') + '.' + 'Use_for_Playback', false, {
                        type: 'boolean',
                        role: 'button'
                    });
                } else {
                    setState('javascript.0.Spotify.Devices.' + pBody.devices[i].name.replace(/\s+/g, '') + '.' + ObjName, pBody.devices[i][ObjName], true);
                }
            }
        }
    }//End of Function createDevices 
    function generateRandomString(length) {
        let text = '';
        const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        for (let i = 0; i < length; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }
        return text;
    }
    
    function requestAuthorization() {
        application.State = generateRandomString(20);
    
        const query = {
            client_id: application.Client_ID,
            response_type: 'code',
            redirect_uri: application.redirect_uri,
            state: application.State,
            scope: 'user-modify-playback-state user-read-playback-state user-read-currently-playing playlist-read-private'
        };
    
        const options = {
            url: 'https://accounts.spotify.com/de/authorize/?' + querystring.stringify(query),
            method: 'GET',
            followAllRedirects: true,
        };
        setState('javascript.0.Spotify.Authorization.Authorization_URL', options.url);
        const debug = false;
        if (debug) {
            request(options, (error, response, body, formData) => {
                // console.log(options.url);
                console.log('STATUS_CODE ' + response.statusCode);
                //console.log('RESPONSE*************'+JSON.stringify(response));
                //console.log('BODY*****'+body);
                //console.log('ERROR'+error);
                //console.log('FORM'+request.form);
                //console.log('HEADERS   *****'+JSON.stringify(response.headers));
                //console.log('HTML   *****'+JSON.stringify(response.html));
            });
        }
    }// End of Function requestAuthorization
    
    function getToken() {
        const options = {
            url: 'https://accounts.spotify.com/api/token',
            method: 'POST',
            headers: {Authorization: 'Basic ' + Buffer.from(application.Client_ID + ':' + application.Client_Secret).toString('base64')},
            form: {grant_type: 'authorization_code', code: application.code, redirect_uri: application.redirect_uri}
        };
        request(options, function (error, response, body) {
    
            saveToken(JSON.parse(body), function (err, Token) {
                if (!err) {
                    setState('javascript.0.Spotify.Authorization.Authorization_URL', '', true);
                    setState('javascript.0.Spotify.Authorization.Authorization_Return_URI', '', true);
                    setState('javascript.0.Spotify.Authorization.Authorized', true, true);
    
                    application.Token = Token.AccessToken;
                    application.refresh_token = Token.RefreshToken;
                }
                else {
                    console.log(err)
                }
            });
        });
    }//End of Function getToken
    
    function refreshToken(callback) {
        console.log('Token wird erneut angefordert ! ');
    
        const options = {
            url: 'https://accounts.spotify.com/api/token',
            method: 'POST',
            headers: {Authorization: 'Basic ' + Buffer.from(application.Client_ID + ':' + application.Client_Secret).toString('base64')},
            form: {grant_type: 'refresh_token', refresh_token: application.refresh_token}
        };
    
        if (application.refresh_token !== '') {
            request(options, (error, response, body) => { // dieser Request holt den neuen Token
                if (response.statusCode === 200) {
                    console.log('neuer Token eingetroffen');
                    //console.log(body);
                    const pBody = JSON.parse(body);
                    if (!pBody.hasOwnProperty('refresh_token')) {
                        pBody.refresh_token = application.refresh_token
                    }
                    //console.log(JSON.stringify(pBody))
    
                    saveToken(pBody, (err, Token) => {
                        if (!err) {
                            application.Token = Token.AccessToken;
                            return callback(null);
                            //application.refresh_token=Token.refresh_token;
                        } else {
                            console.log(err);
                            return callback(err);
                        }
                    });
                } else {
                    return callback(response.statusCode)
                }    //05.02.2018 19:37
            });
        }// end if   
    }//End of Function refreshToken
    
    function saveToken(pBody, callback) {
        //const ParsedBody=JSON.parse(Body);
        //console.log(ParsedBody.hasOwnProperty('access_token'))
        if (pBody.access_token && pBody.refresh_token) {
            const Token = {
                AccessToken: pBody.access_token,
                RefreshToken: pBody.refresh_token
            };
            fs.writeFile(application.TokenFilePath, JSON.stringify(Token), 'utf8', err => {
                if (!err) {
                    console.log('Token Saved!');
                    return callback(null, Token);
                } else {
                    return callback('Fehler beim Token Speichern', null)
                }
            });
        } else {
            return callback('keine Token in Serverantwort gefunden ! ', null)
        }
    
    }//End of Function saveToken
    on({id: 'javascript.0.Spotify.Authorization.Authorization_Return_URI', change: 'any'}, obj => {
        if (!obj.state.ack) {
            const return_uri = querystring.parse(obj.state.val.slice(obj.state.val.search('[?]') + 1, obj.state.val.length));
            if (return_uri.state == application.State) {
                application.code = return_uri.code;
                getToken();
            }
        }
    
    });
    on({id: 'javascript.0.Spotify.Authorization.Get_Authorization', val: true}, obj => {
        requestAuthorization();
        setState('javascript.0.Spotify.Authorization.Authorized', false, true);
    });
    
    on({id: /\.Use_for_Playback$/, val: true}, obj => {
        deviceData.last_select_device_id = getState(obj.id.slice(0, obj.id.lastIndexOf('.')) + '.id').val;
    
        const send = {
            device_ids: [deviceData.last_select_device_id],  //Divice IDs als Array !
            //play:false  //True = Wiedergabe startet sofort auf diesem Gerät, FALSE = Wiedergabe anhängig von Playback State
        };
        sendRequest('/v1/me/player', 'PUT', JSON.stringify(send), (err, data) => {
            //if(!err){deviceData.last_select_device_id=getState(obj.id.slice(0,obj.id.lastIndexOf('.'))+'.id').val}
        });
    });
    
    on({id: /\.Track_List$/, valGe: 0, valNe: null, ack: false}, obj => { //eine bestimmten Track aus Playliste  sofort abspielen
    
        const StateName = obj.common.Track_ID.split(';');
        const StateArr = [];
        for (let i = 0; i < StateName.length; i++) {
            const ele = StateName[i].split(':');
            StateArr[ele[0]] = ele[1];
        }
        if (StateArr[obj.state.val] !== '' && (StateArr[obj.state.val] !== null)) {
            const send = {
                uris: ['spotify:track:' + StateArr[obj.state.val]],
                offset: {
                    position: 0
                }
            };
    
            sendRequest('/v1/me/player/play', 'PUT', JSON.stringify(send), err =>
                !err && setState(obj.id, obj.state.val, true));
        }
    });
    
    on({id: /\.Play_this_List$/, val: true}, obj => { //eine bestimmte Playlist sofort abspielen
        const send = {
            context_uri: 'spotify:user:' + getState(obj.id.slice(0, obj.id.lastIndexOf('.')) + '.owner').val + ':playlist:' + getState(obj.id.slice(0, obj.id.lastIndexOf('.')) + '.id').val,
            offset: {
                position: 1
            }
        };
    
        const query = {device_id: deviceHandel(deviceData)};
        sendRequest('/v1/me/player/play?' + querystring.stringify(query), 'PUT', JSON.stringify(send), () => 
            sendRequest('/v1/me/player', 'GET', '', (err, data) => !err && createPlaybackInfo(data)));
    });
    
    on({id: 'javascript.0.Spotify.Player.Play', val: true}, obj => {
        const query = {device_id: deviceHandel(deviceData)};
        console.log(deviceHandel(deviceData));
        sendRequest('/v1/me/player/play?' + querystring.stringify(query));
    });
    on({id: 'javascript.0.Spotify.Player.Pause', val: true}, obj => {
        const query = {device_id: deviceHandel(deviceData)};
        sendRequest('/v1/me/player/pause?' + querystring.stringify(query));
    });
    
    on({id: 'javascript.0.Spotify.Player.Skip_Plus', val: true}, obj => {
        const query = {device_id: deviceHandel(deviceData)};
        sendRequest('/v1/me/player/next?' + querystring.stringify(query), 'POST');
    });
    
    on({id: 'javascript.0.Spotify.Player.Skip_Minus', val: true}, obj => {
        const query = {device_id: deviceHandel(deviceData)};
        sendRequest('/v1/me/player/previous?' + querystring.stringify(query), 'POST');
    });
    
    on({id: 'javascript.0.Spotify.Player.Repeat_Track', val: true}, obj => 
        sendRequest('/v1/me/player/repeat?state=track'));
    
    on({id: 'javascript.0.Spotify.Player.Repeat_Context', val: true}, obj => 
        sendRequest('/v1/me/player/repeat?state=context'));
    
    on({id: 'javascript.0.Spotify.Player.Repeat_off', val: true}, obj => 
        sendRequest('/v1/me/player/repeat?state=off'));
    
    on({id: 'javascript.0.Spotify.Player.Volume'}, obj => {
        sendRequest('/v1/me/player/volume?volume_percent=' + obj.state.val, 'PUT', '', err => {
            // !err && setState('javascript.0.Spotify.Player.Volume', true/*ack*/)
        });
    });
    
    on({id: 'javascript.0.Spotify.Player.Seek'}, obj => 
        sendRequest('/v1/me/player/seek?position_ms=' + obj.state.val * 1000));
    
    on({id: 'javascript.0.Spotify.Player.Shuffle'}, obj => {
        if (obj.state.val === true) {
            sendRequest('/v1/me/player/shuffle?state=true');
        } else {
            sendRequest('/v1/me/player/shuffle?state=false');
        }
    });
    
    on({id: 'javascript.0.Spotify.Player.TrackId'}, obj => {
        const send = {
            uris: ['spotify:track:' + obj.state.val],
            offset: {
                position: 0
            }
        };
    
        sendRequest('/v1/me/player/play', 'PUT', JSON.stringify(send));
    });
    
    on({id: 'javascript.0.Spotify.Player.Playlist_ID'}, obj => {
        const send = {
            context_uri: 'spotify:user:' + application.User_ID + ':playlist:' + obj.state.val,
            offset: {
                position: 1
            }
        };
    
        sendRequest('/v1/me/player/play', 'PUT', JSON.stringify(send));
    });
    
    on({id: 'javascript.0.Spotify.Get_User_Playlists'}, () => GetUsersPlaylist(0));
    
    on({id: 'javascript.0.Spotify.Devices.Get_Devices'}, () => 
        sendRequest('/v1/me/player/devices', 'GET', '', (err, data) => !err && createDevices(data)));
    
    on({id: 'javascript.0.Spotify.Get_Playback_Info'}, () => 
        sendRequest('/v1/me/player', 'GET', '', (err, data) => !err && createPlaybackInfo(data)));
    
    on({id: 'javascript.0.Spotify.Authorization.Authorized'}, obj => {
        if (obj.state.val === true) {
            interval = setInterval(() => {
                sendRequest('/v1/me/player', 'GET', '', (err, data) => {
                    //console.log('interval'+err)
    
                    if (!err) {
                        createPlaybackInfo(data)
                    } else if (err == 202 || (err == 502) || (err == 401)) {  //202,401 und 502 lassen den Interval  weiter laufen
                        DummyBody = {is_playing: false}; // tritt ein wenn kein Player geöffnet ist
                        createPlaybackInfo(DummyBody)
                    } else {  // andere Fehler stoppen den interval
                        clearInterval(interval);
                        console.warn('Spotify interval gestoppt !');
                    }
    
                });
    
            }, 5000);
        } else {
            interval && clearInterval(interval);
            interval = null;
        }
    });
    
    // on({id:'javascript.0.Spotify.Authorization.Login'}, function (obj){});
    
    onStop(() => {
        setState('javascript.0.Spotify.Authorization.Authorization_URL', '', true);
        setState('javascript.0.Spotify.Authorization.Authorization_Return_URI', '', true);
        setState('javascript.0.Spotify.Player.TrackId', '', true);
        setState('javascript.0.Spotify.Player.Playlist_ID', '', true);
        setState('javascript.0.Spotify.Authorization.User_ID', '', true);
        setState('javascript.0.Spotify.Authorization.Authorized', false, true);
        interval && clearInterval(interval);
        interval = null;
    }, 1000 /*ms*/);
    
    Scholl-MaeuselS 1 Reply Last reply
    1
    • lobomauL Offline
      lobomauL Offline
      lobomau
      wrote on last edited by
      #2

      Coole Idee!

      In der Beschreibung hast du vergessen, dass man auch das zusätzliche npm Paket installieren muss "querystring".

      Das habe ich soweit hinbekommen bis zum Code der Seite:

      Diese Website ist nicht erreichbar
      
      Die Webseite unter https://example.com/callback/?code=AQB2......kd09 ist möglicherweise vorübergehend nicht verfügbar oder wurde dauerhaft an eine neue Webadresse verschoben.
      ERR_INVALID_RESPONSE
      

      Also Code habe ich genommen "AQB2…...kd09". Korrekt?

      Den habe ich auch gleich eingetragen Authorization_Code. Habe es dreimal probiert, recht schnell, also unter 30 sec.

      Aber die Werte bleiben rot und paar Knöpfe habe ich ausprobiert wie Get_Devices, Play, aber es steht nur im log:

      javascript.0	2017-09-24 00:36:21.964	info	}
      javascript.0	2017-09-24 00:36:21.964	info	}
      javascript.0	2017-09-24 00:36:21.964	info	"message": "Only valid bearer authentication supported"
      javascript.0	2017-09-24 00:36:21.964	info	"status": 400,
      javascript.0	2017-09-24 00:36:21.964	info	"error": {
      javascript.0	2017-09-24 00:36:21.964	info	script.js.spotify: Body: {
      javascript.0	2017-09-24 00:36:21.964	info	script.js.spotify: https://api.spotify.com/v1/me/player/play
      javascript.0	2017-09-24 00:35:46.653	info	}
      javascript.0	2017-09-24 00:35:46.653	info	}
      javascript.0	2017-09-24 00:35:46.653	info	"message": "Only valid bearer authentication supported"
      javascript.0	2017-09-24 00:35:46.653	info	"status": 400,
      javascript.0	2017-09-24 00:35:46.653	info	"error": {
      javascript.0	2017-09-24 00:35:46.653	info	script.js.spotify: Body: {
      javascript.0	2017-09-24 00:35:46.653	info	script.js.spotify: https://api.spotify.com/v1/me/player/pause
      javascript.0	2017-09-24 00:35:17.710	info	}
      javascript.0	2017-09-24 00:35:17.710	info	}
      javascript.0	2017-09-24 00:35:17.710	info	"message": "Only valid bearer authentication supported"
      javascript.0	2017-09-24 00:35:17.710	info	"status": 400,
      javascript.0	2017-09-24 00:35:17.710	info	"error": {
      javascript.0	2017-09-24 00:35:17.710	info	script.js.spotify: Body: {
      javascript.0	2017-09-24 00:35:17.709	info	script.js.spotify: https://api.spotify.com/v1/me/player/devices
      

      Host: NUC8i3 mit Proxmox:

      • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
      • Slave: Pi4
      1 Reply Last reply
      0
      • L Offline
        L Offline
        Lucky
        wrote on last edited by
        #3

        du hast recht, es muss natürlich noch das Paket querystring installiert werden !

        Die Antwort solle im Browser ungefähr so aussehen..

        https://example.com/callback/?code=abcdefghijk-xyxyxyxyxyxyxyxyxyxyx-xyxyxyxyxyxy-hsgatebskvz&state=34fFs29kd09

        du darfst nur den rot markierten teil in den Authorization_Code State kopieren, wenn alles geklappt hat sollte im log

        10:11:00.037	[info]	javascript.0 script.js.Spotify.Spotify: SpotifyRefreshToken Saved!
        10:11:00.039	[info]	javascript.0 script.js.Spotify.Spotify: SpotifyAccessToken Saved!
        

        stehen !

        1 Reply Last reply
        0
        • lobomauL Offline
          lobomauL Offline
          lobomau
          wrote on last edited by Jey Cee
          #4

          @Lucky:

          du hast recht, es muss natürlich noch das Paket querystring installiert werden !

          Die Antwort solle im Browser ungefähr so aussehen.

          https://example.com/callback/?code=****abcdefghijk-xyxyxyxyxyxyxyxyxyxyx-xyxyxyxyxyxy-hsgatebskvz****&state=34fFs29kd09
          

          du darfst nur den rot markierten teil in den Authorization_Code State kopieren, wenn alles geklappt hat sollte im log

          10:11:00.037	[info]	javascript.0 script.js.Spotify.Spotify: SpotifyRefreshToken Saved!
          10:11:00.039	[info]	javascript.0 script.js.Spotify.Spotify: SpotifyAccessToken Saved!
          

          stehen !
          Joh, jetzt habe ich den richtigen Teil des Codes genommen. Es scheint zu funktionieren.

          Ich habe nichts von

          [info]	javascript.0 script.js.Spotify.Spotify: SpotifyAccessToken Saved!
          

          gelesen.

          Aber mit Get_Devices werden meine zwei Echos angezeigt, Google Home aber nicht.

          Kann ich auch innerhalb des Device Ordners etwas schalten? Volume_percent macht hier nichts.

          Es funktioniert aber play, pause, skip, volume. Schon nicht schlecht für den Anfang! 🙂

          Host: NUC8i3 mit Proxmox:

          • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
          • Slave: Pi4
          1 Reply Last reply
          0
          • L Offline
            L Offline
            Lucky
            wrote on last edited by
            #5

            Momentan geht nur das, aber ich bin dabei weiteres einzubauen ! in der nächsten Version wird auch die Autorisierung etwas einfacher sei 😉

            1 Reply Last reply
            0
            • L Offline
              L Offline
              Lucky
              wrote on last edited by
              #6

              so, ich habe das "etwas" erweitert 😉

              hier nochmal die Anleitung:

              1-Spotify Account muss vorhanden sein (habe Premium, ob es auch mit der Free Version geht weis ich nicht)

              2-unter https://developer.spotify.com muss eine Application registriert werden

              3-die dann erhaltene Client ID und Client Secret im Skript eintragen

              4-als Redirect URI https://example.com/callback/ oder wenn vorhanden eigen Webseite eintragen

              5-gleiche Redirect URI im Skript eintragen

              6-wenn alles eingetragen ist, Skript starten

              7-javascript.0.Spotify.Get_Authorization drücken

              8-in javascript.0.Spotify.Authorization_URL wird eine URL angezeigt, diese in einem Browser öffnen, ihr werdet dann von Spotify gefragt ob ihr die App zulassen wollt, dem zustimmen.

              9-wenn https://example.com/callback/ verwendet wurde, kommt eine Fehlermeldung vom Browser zurück, jetzt bitte die Redirect URL aus der Adresszeile des Browsers komplett kopieren !

              10- die kopierte URL innerhalb von 30 Sekunden in javascript.0.Spotify.Authorization.Authorization_Return_URI eintragen, Token wird dann erzeugt und gespeichert

              was neu ist:

              -es werden alle mit Spotify verbundenen Geräte ausgelistet, zwischen diesen kann direkt gewechselt werden

              -Informationen über die aktuelle Wiedergabe werden abgerufen

              -Playlist des Users werden aufgelistet, diese können direkt angewählt werden

              -es können Tracks und Playlist per ID gestartet werden

              ! ```
              `//Version 0.2.0
              //letzte änderung 04.11.2017 23:32
              ! createState('javascript.0.Spotify.Player.Play', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Pause', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Skip_Plus', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Skip_Minus', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Repeat_Track', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Repeat_Context', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Repeat_off', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Player.Volume', 0,{type: "number", role: "Volume %"});
              createState('javascript.0.Spotify.Player.TrackId','' ,{type: "string", role: "Track Id to Play"});
              createState('javascript.0.Spotify.Player.Playlist_ID','' ,{type: "string", role: "Playlist Id to Play"});
              createState('javascript.0.Spotify.Player.Seek', 0,{type: "number", role: "Seek To Position (s)"});
              ! createState('javascript.0.Spotify.Devices.Get_Devices', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Authorization.Get_Authorization', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Authorization.Authorization_URL','',{type: "string", role: "Authorization_URL",write:false});
              createState('javascript.0.Spotify.Authorization.Authorization_Return_URI','',{type: "string", role: "Authorization_Return_URI"});
              createState('javascript.0.Spotify.Authorization.User_ID','',{type: "string", role: "User ID",write:false});
              createState('javascript.0.Spotify.Authorization.Authorized',false,{type: "boolean", role: "Authorized",write:false});
              //createState('javascript.0.Spotify.Get_Playback_Info', false,{type: "boolean", role: "button"});
              createState('javascript.0.Spotify.Get_User_Playlists', false,{type: "boolean", role: "button"});
              ! createState('javascript.0.Spotify.PlaybackInfo.Track_Id','' ,{type: "string", role: "Track Id",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Artist_Name','' ,{type: "string", role: "Artist Name",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Type','' ,{type: "string", role: "Type",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Album','' ,{type: "string", role: "Album",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.timestamp',0 ,{type: "number", role: "Timestamp",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.progress_ms',0 ,{type: "number", role: "progress_ms",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.is_playing',false ,{type: "boolean", role: "is_playing",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.image_url','' ,{type: "string", role: "Image URL",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Track_Name','' ,{type: "string", role: "Track_Name",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.duration_ms',0 ,{type: "number", role: "Duration ms",write:false});
              ! createState('javascript.0.Spotify.PlaybackInfo.Device.id','',{type: "string", role: "id",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Device.is_active',false,{type: "boolean", role: "is active",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Device.is_restricted',false,{type: "boolean", role: "is restricted",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Device.name','',{type: "string", role: "Name",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Device.type','',{type: "string", role: "Type",write:false});
              createState('javascript.0.Spotify.PlaybackInfo.Device.volume_percent',0,{type: "number", role: "volume_percent",write:false});
              ! var request = require('request');
              var querystring = require('querystring');
              var fs = require('fs');
              ! var Application = {
              User_ID:'',//Nichts eintragen !!
              BaseURL:'https://api.spotify.com',
              Client_ID:'hier deine client ID',
              Client_Secret:'hier dein client secret',
              redirect_uri:'https://example.com/callback/',
              Token:'', //Nichts eintragen !!
              refresh_token:'',//Nichts eintragen !!
              code:'',//Nichts eintragen !!
              State:''//Nichts eintragen !!
              };
              ! //############### Initial ##########
              ReadTokenFiles(function(err){
              ! if(!err) {
              SendRequest('/v1/me','GET','',function(err,data){
              if(!err) {
              GetUserInformation(data);
              setState('javascript.0.Spotify.Authorization.Authorized',val=true,akt=true);

                                   SendRequest('/v1/me/player/devices','GET','',function(err,data){
                                          if(!err){CreateDevices(data)}
                                   });
                        }
                        else{setState('javascript.0.Spotify.Authorization.Authorized',val=false,akt=true)}
                });
              

              ! }
              });
              ! //#################################
              ! function ReadTokenFiles(callback){
              ! fs.readFile('SpotifyAccessToken.txt', function(err, data) {
              if(!err){ //wenn keine Fehler
              console.log('SpotifyAccessToken gelesen');
              Application.Token=data;

                      fs.readFile('SpotifyRefreshToken.txt', function(err, data) {
                        if(!err){
                          console.log('SpotifyRefreshToken gelesen');
                          Application.refresh_token=data;
              
                          return callback(false);
                         }
                          else{ //wenn Fehler
                              console.log(err);
                              return callback(true);
              
                          }
                    }); 
              }
              else{ //wenn Fehler
                  console.log (err);
                  return callback(true);
              }
              

              });

              !

              }// End of Function ReadTokenFiles

              ! //###################################################################################### FUNCTION SEND REQUEST ###################################################################################
              function SendRequest(Endpoint,Method,Send_Body,callback){
              ! var options = {
              url: Application.BaseURL+Endpoint,
              method: Method,
              headers: {Authorization: 'Bearer '+Application.Token},
              form:Send_Body
              };
              ! //console.log(options.form);
              //console.log('Spotify API Call...'+ Endpoint);
              ! request(options,function (error, response, body){

              var ParsetBody ;

              ! //console.log(response.statusCode);

                  switch (response.statusCode){
              
                      case 200: // OK
                          return callback(false,JSON.parse(body)); 
              
                      case 202: //Accepted, processing has not been completed.
                      DummyBody={
                          is_playing:false                //tritt ein wenn kein Player geöffnet ist
                      };
                          return callback(false,DummyBody);
              

              ! case 204: // OK, No Content
              return callback(false,null);

                      case 400: //Bad Request, message body will contain more information
                          return callback(true,null);
              
                      case 401: //Unauthorized 
                          if(JSON.parse(body).error.message=='The access token expired'){
              
                                       Refresh_Token(Endpoint,Method,Send_Body,function(err,NewData){
              
                                           return callback(err,NewData); //neue Daten
                                       });
              
                          }
                                   else{ //wenn anderer fehler
                                       console.warn(JSON.parse(body).error.message);
                                       return callback(true,null);
                                   }
              
                          break;
              
                  default:
                          console.warn( 'Code '+response.statusCode+' wird nicht verarbeitet ! ');
                          return callback(true,null);
                  }
              

              ! });

              }//End of Function SendRequest
              //###################################################################################### END OF FUNCTION SEND REQUEST ###################################################################################

              ! function CreatePlaybackInfo(P_Body){
              setState('javascript.0.Spotify.PlaybackInfo.is_playing',val=P_Body.is_playing,akt=true);

              if(P_Body.is_playing===true){

              setState('javascript.0.Spotify.PlaybackInfo.Track_Id',val=P_Body.item.id,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.Artist_Name',val=P_Body.item.artists[0].name,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.Type',val=P_Body.item.type,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.Album',val=P_Body.item.album.name,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.timestamp',val=P_Body.timestamp,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.progress_ms',val=P_Body.progress_ms,akt=true);
              setState('javascript.0.Spotify.PlaybackInfo.image_url',val=P_Body.item.album.images[0].url,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Track_Name',val=P_Body.item.name,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.duration_ms',val=P_Body.item.duration_ms,akt=true);

              setState( 'javascript.0.Spotify.PlaybackInfo.Device.id',val=P_Body.device.id,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Device.is_active',val=P_Body.device.is_active,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Device.is_restricted',val=P_Body.device.is_restricted,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Device.name',val=P_Body.device.name,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Device.type',val=P_Body.device.type,akt=true);
              setState( 'javascript.0.Spotify.PlaybackInfo.Device.volume_percent',val=P_Body.device.volume_percent,akt=true);

              ! }
              }//End of Function CreatePlaybackInfo
              ! function GetUserInformation(P_Body){
              ! Application.User_ID=P_Body.id;
              setState('javascript.0.Spotify.Authorization.User_ID',val=P_Body.id,akt=true);

              }//End of Function GetUserInformation

              ! function GetUsersPlaylist(P_Body){
              ! for (i = 0; i < P_Body.items.length; i++) {
              //console.log(P_Body.items[i].name)
              if (!getObject('javascript.0.Spotify.Playlists.'+P_Body.items[i].name.replace(/\s+/g, ''))) {
              createState('javascript.0.Spotify.Playlists.'+P_Body.items[i].name.replace(/\s+/g, '')+'.'+'Play_this_List',false,{type:'boolean', role:'button'});
              createState('javascript.0.Spotify.Playlists.'+P_Body.items[i].name.replace(/\s+/g, '')+'.'+'id',P_Body.items[i].id,{type:'string', role:'id'});
              createState('javascript.0.Spotify.Playlists.'+P_Body.items[i].name.replace(/\s+/g, '')+'.'+'owner',P_Body.items[i].owner.id,{type:'string', role:'owner'});

                  }
              }
              

              }

              ! function CreateDevices(P_Body){

              for (i = 0; i < P_Body.devices.length; i++) {
              
                           for (var ObjName in P_Body.devices[i]) {
              
                               if (!getObject('javascript.0.Spotify.Devices.'+P_Body.devices[i].name.replace(/\s+/g, '')+'.'+ObjName)){
              
                                   createState('javascript.0.Spotify.Devices.'+P_Body.devices[i].name.replace(/\s+/g, '')+'.'+ObjName,P_Body.devices[i][ObjName],{type: typeof P_Body.devices[i][ObjName], role: ObjName});
                                   createState('javascript.0.Spotify.Devices.'+P_Body.devices[i].name.replace(/\s+/g, '')+'.'+'Use_for_Playback',false,{type:'boolean', role:'button'});
                               }
              
                               else{setState('javascript.0.Spotify.Devices.'+P_Body.devices[i].name.replace(/\s+/g, '')+'.'+ObjName,P_Body.devices[i][ObjName],akt=true)}
                           }
              }
              

              }//End of Function CreateDevices

              ! function generateRandomString (length) {
              var text = '';
              var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
              ! for (var i = 0; i < length; i++) {
              text += possible.charAt(Math.floor(Math.random() * possible.length));
              }
              return text;
              }
              ! function request_authorization(){

              Application.State=generateRandomString (20);
              
              var query ={
                  client_id:Application.Client_ID,
                  response_type: 'code',
                  redirect_uri :Application.redirect_uri,
                  state:Application.State,
                  scope:'user-modify-playback-state user-read-playback-state user-read-currently-playing playlist-read-private'
              };
              

              !
              var options = {
              url:'https://accounts.spotify.com/de/authorize/?'+querystring.stringify(query),
              method: 'GET',
              followAllRedirects: true,
              };
              ! setState('javascript.0.Spotify.Authorization.Authorization_URL',val=options.url);
              ! // request(options, function (error, response,body){

               //   console.log(options.url);
                 // console.log('RESPONSE*************'+JSON.stringify(response));
                  //console.log('BODY*****'+body);
                  //console.log('ERROR'+error);
                  //console.log('FORM'+form);
                  //console.log('HEADERS   *****'+JSON.stringify(response.headers));
                  //console.log('HTML   *****'+JSON.stringify(response.html));
              

              // });

              ! }// End of Function request_authorization
              ! function GetToken(){
              var options = {
              url: 'https://accounts.spotify.com/api/token',
              method: 'POST',
              headers: {Authorization: 'Basic '+Buffer.from(Application.Client_ID + ':' + Application.Client_Secret).toString('base64')},
              form: {grant_type:'authorization_code',code:Application.code,redirect_uri:Application.redirect_uri}
              };
              ! request(options, function (error, response, body){
              if(SaveToken(body)===true){
              setState('javascript.0.Spotify.Authorization.Authorization_URL',val='',akt=true);
              setState('javascript.0.Spotify.Authorization.Authorization_Return_URI',val='',akt=true);
              setState('javascript.0.Spotify.Authorization.Authorized',val=true,akt=true);
              }

              });
              

              ! }//End of Function GetToken
              ! function Refresh_Token(Endpoint,Method,Send_Body,callback){
              console.log('Token wird erneut angefordert ! ');

              var options = {
              url: 'https://accounts.spotify.com/api/token',
              method: 'POST',
              headers: {Authorization: 'Basic '+Buffer.from(Application.Client_ID + ':' + Application.Client_Secret).toString('base64')},
              form: {grant_type:'refresh_token',refresh_token:Application.refresh_token}
              

              };

              ! if(Application.refresh_token!==''){
              ! request(options, function (error, response, body){
              if(error){console.log(error)}

                  if (SaveToken(body)===true){  // wenn Token erfolgreich gespreichert wurde
                  console.log('neuer Token eingetroffen');
                        SendRequest(Endpoint,Method,Send_Body,function(err,data){ 
                           if (!err){console.log('Daten mit neuem Token')}
                           else{console.error('FEHLER BEIM ERNEUTEN DATEN ANFORDERN !')}
                      callback(err,data);  
                      });
              
                  }
              
              else {callback(true,null)}
              
              });
              

              }
              else{
              console.warn('kein Refresh Token gefunden !!');
              callback(true,null);
              }

              }//End of Function Refresh_Token

              ! function SaveToken(Body){

               var ParsedBody=JSON.parse(Body);
              
                   if ("undefined" !== typeof  ParsedBody.access_token){  
              
                        Application.Token=ParsedBody.access_token;
              
                              fs.writeFile('SpotifyAccessToken.txt', Application.Token, function (err) {
                              if (err) {throw err}
                              else{
                              console.log('SpotifyAccessToken Saved!');
              
                              }
              
                           });
                   }    
              
                  if ("undefined" !== typeof  ParsedBody.refresh_token){  
              
                      Application.refresh_token=ParsedBody.refresh_token;
              
                          fs.writeFile('SpotifyRefreshToken.txt', Application.refresh_token, function (err) {
                              if (err) {throw err}
                              else{
                              console.log('SpotifyRefreshToken Saved!');
              
                              }
                         });
                  }
                  return true;
              

              }//End of Function SaveToken

              ! on({id:'javascript.0.Spotify.Authorization.Authorization_Return_URI',change:"any"}, function (obj){
              if (!obj.state.ack) {

                   var return_uri=querystring.parse(obj.state.val.slice(obj.state.val.search('[?]')+1, obj.state.val.length));
                   if(return_uri.state==Application.State){
                       Application.code=return_uri.code;
                         GetToken();
                   }
               }
              

              });

              ! on({id:'javascript.0.Spotify.Authorization.Get_Authorization',val:true}, function (obj){
              request_authorization();
              setState('javascript.0.Spotify.Authorization.Authorized',val=false,akt=true);
              });
              ! on({id: /.Use_for_Playback$/, val:true},function (obj){

              var send={
              device_ids:[getState(obj.id.slice(0,obj.id.lastIndexOf("."))+'.id').val]
              };
              SendRequest('/v1/me/player','PUT',JSON.stringify(send),function(){});
              });

              on({id: /.Play_this_List$/, val:true},function (obj){

              ! var send ={
              context_uri:'spotify:user:'+getState(obj.id.slice(0,obj.id.lastIndexOf("."))+'.owner').val+':playlist:'+getState(obj.id.slice(0,obj.id.lastIndexOf("."))+'.id').val,
              offset:{
              position:1
              }
              };

                   SendRequest('/v1/me/player/play','PUT',JSON.stringify(send),function(){
                       SendRequest('/v1/me/player','GET','',function(err,data){
                      if(!err) {CreatePlaybackInfo(data)}
                          });
                   });
              

              });

              ! on({id:'javascript.0.Spotify.Player.Play',val:true}, function (obj){
              SendRequest('/v1/me/player/play','PUT','',function(){});
              });
              ! on({id:'javascript.0.Spotify.Player.Pause',val:true}, function (obj){
              SendRequest('/v1/me/player/pause','PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Skip_Plus',val:true}, function (obj){
              SendRequest('/v1/me/player/next','POST','',function(err,data){

                   });
              

              });

              on({id:'javascript.0.Spotify.Player.Skip_Minus',val:true}, function (obj){
              SendRequest('/v1/me/player/previous','POST','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Repeat_Track',val:true}, function (obj){ //aaaaaaaaaaaa
              SendRequest('/v1/me/player/repeat?state=track','PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Repeat_Context',val:true}, function (obj){ //aaaaaaaaaaaa
              SendRequest('/v1/me/player/repeat?state=context','PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Repeat_off',val:true}, function (obj){ //aaaaaaaaaaaa
              SendRequest('/v1/me/player/repeat?state=off','PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Volume'}, function (obj){
              SendRequest('/v1/me/player/volume?volume_percent='+obj.state.val,'PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.Seek'}, function (obj){
              SendRequest('/v1/me/player/seek?position_ms='+obj.state.val*1000,'PUT','',function(){});
              });

              on({id:'javascript.0.Spotify.Player.TrackId'}, function (obj){

                   var send ={
                       uris:['spotify:track:'+obj.state.val],
                       offset:{
                           position:0
                       }
                   };
              
                   SendRequest('/v1/me/player/play','PUT',JSON.stringify(send),function(){});
              

              });

              on({id:'javascript.0.Spotify.Player.Playlist_ID'}, function (obj){

                   var send ={
                       context_uri:'spotify:user:'+Application.User_ID+':playlist:'+obj.state.val,   //spotify:user:user:playlist:
                       offset:{
                           position:1
                       }
                   };
              
                   SendRequest('/v1/me/player/play','PUT',JSON.stringify(send),function(){});
              

              });

              on({id:'javascript.0.Spotify.Get_User_Playlists'}, function (obj){
              if(Application.User_ID!==''){
              var query ={
              limit:40,
              offest:0
              };

                   SendRequest('/v1/users/'+Application.User_ID+'/playlists?'+querystring.stringify(query),'GET','',function(err,data){
                       if(!err) {GetUsersPlaylist(data)}
                   });
                 }
              

              });

              on({id:'javascript.0.Spotify.Devices.Get_Devices'}, function (obj){

                  SendRequest('/v1/me/player/devices','GET','',function(err,data){
                      if(!err){CreateDevices(data)}
                  });
              

              });

              on({id:'javascript.0.Spotify.Get_Playback_Info'}, function (obj){

                   SendRequest('/v1/me/player','GET','',function(err,data){
                      if(!err) {CreatePlaybackInfo(data)}
                   });
              

              });

              on({id:'javascript.0.Spotify.Authorization.Authorized'}, function (obj){

                if(obj.state.val===true){
              
                       Intervall = setInterval(function () {
              
                           SendRequest('/v1/me/player','GET','',function(err,data){
                              if(!err) {CreatePlaybackInfo(data)}
                              else{clearInterval(Intervall)}
                           });
              
                       },5000); 
                }
                else{
                    if ("undefined" !== typeof  Intervall){clearInterval(Intervall)}
                }
              

              ! });

              onStop(function () {
              setState('javascript.0.Spotify.Authorization.Authorization_URL',val='',akt=true);
              setState('javascript.0.Spotify.Authorization.Authorization_Return_URI',val='',akt=true);
              setState('javascript.0.Spotify.Player.TrackId',val='',akt=true);
              setState('javascript.0.Spotify.Player.Playlist_ID',val='',akt=true);
              setState('javascript.0.Spotify.Authorization.User_ID',val='',akt=true);
              setState('javascript.0.Spotify.Authorization.Authorized',val=false,akt=true);
              if ("undefined" !== typeof Intervall){clearInterval(Intervall)}
              }, 1000 /ms/);` [/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i]

              1 Reply Last reply
              0
              • M Offline
                M Offline
                Marci_Marc85
                wrote on last edited by
                #7

                Hi . kannst du evtl kurz ab Punkt 6 erklären, was ich genau machen muss? Bin neu in iobroker und was programmieren angeht, bin ich leider auch nur Laie. Ich hab das Script in den Scripte reiter in iobroker geladen und die ID's, die mir die developer Seite gegeben hat dort eingesetzt. Wie soll ich jetzt das Script starten? Hättest du da ne kurze und anfängerfreundliche Anleitung für mich?

                1 Reply Last reply
                0
                • T Offline
                  T Offline
                  tempestas
                  wrote on last edited by
                  #8

                  starke Sache, schaue ich mir heute abend definitv mal an.

                  <size="85">ioBroker | 21 Adapter | Ubuntu Server | intel NUC | Homematic CCU2 | Hue | Osram Lightify| Sonos | 2x Instar Cam | Samsung Tab A 2016 im Holzrahmen| 3x Echo dot | 1x Echo | Neato Botvac D5</size>

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    Marci_Marc85
                    wrote on last edited by
                    #9

                    ???

                    Wenn ich das Script starte, kommt immer folgende Fehlermeldung:

                    14:01:16.530 [info] javascript.0 Start javascript script.js.Spotify_Get_Authorization

                    14:01:16.532 [info] javascript.0 script.js.Spotify_Get_Authorization: registered 19 subscriptions and 0 schedules

                    14:01:16.533 [info] javascript.0 script.js.Spotify_Get_Authorization: Error: ENOENT: no such file or directory, open 'SpotifyAccessToken.txt'

                    4129_spotify_iobroker.png

                    1 Reply Last reply
                    0
                    • T Offline
                      T Offline
                      tempestas
                      wrote on last edited by
                      #10

                      bist du sicher, dass du uns deine keys und secret so mitteilen willst im Screenshot?

                      Hast du diesen Teil beachtet?:

                      ` > 9-wenn https://example.com/callback/ verwendet wurde, kommt eine Fehlermeldung vom Browser zurück, jetzt bitte die Redirect URL aus der Adresszeile des Browsers komplett kopieren !

                      10- die kopierte URL innerhalb von 30 Sekunden in javascript.0.Spotify.Authorization.Authorization_Return_URI eintragen, Token wird dann erzeugt und gespeichert `

                      <size="85">ioBroker | 21 Adapter | Ubuntu Server | intel NUC | Homematic CCU2 | Hue | Osram Lightify| Sonos | 2x Instar Cam | Samsung Tab A 2016 im Holzrahmen| 3x Echo dot | 1x Echo | Neato Botvac D5</size>

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        Marci_Marc85
                        wrote on last edited by
                        #11

                        @tempestas:

                        bist du sicher, dass du uns deine keys und secret so mitteilen willst im Screenshot?

                        Hast du diesen Teil beachtet?:

                        ` > 9-wenn https://example.com/callback/ verwendet wurde, kommt eine Fehlermeldung vom Browser zurück, jetzt bitte die Redirect URL aus der Adresszeile des Browsers komplett kopieren !

                        10- die kopierte URL innerhalb von 30 Sekunden in javascript.0.Spotify.Authorization.Authorization_Return_URI eintragen, Token wird dann erzeugt und gespeichert

                        Ich bin ja gerade einmal bei Punkt 6 und da kommt dann ja schon die Meldung. Also direkt nach dem Starten des Scripts

                        EDIT:

                        Wie gesagt. Ich bin da ganz neu in der Materie. Hab jetzt aber gerade gesehen, das im Reiter Objekte die jeweiligen einzelenen Scripte angelegt wurden, jedoch steht im "Authorizaion_URL" keine URL drinm, die ich im Browser kopieren und aufrufen könnte

                        4129_spotify_iobroker2.png

                        EDIT2:

                        Hab alles nochmal gemacht. Die URL kam diesmal, hab dann die anderen schritte abgearbeitet , die Redirect URL kopiert und eingefügt und bei "Authorized" steht jetzt "true" Schein also jetzt funktioniert zu haben. Wie kann ich das ganze jetzt in einem Widget darstellen?

                        1 Reply Last reply
                        0
                        • K Offline
                          K Offline
                          Karim
                          wrote on last edited by
                          #12

                          Ich klinke mich mal mit ein.

                          Skript läuft und der Token wurde akzeptiert.

                          Wie kann ich das Ganze denn nun visualisieren?

                          Lieben Gruß, Karim.

                          1 Reply Last reply
                          0
                          • lobomauL Offline
                            lobomauL Offline
                            lobomau
                            wrote on last edited by
                            #13

                            Nun habe ich das Script-Update vom 4.11 eingespielt. Das scheint auch zu laufen.

                            "Leider" habe ich inzwischen auf Spotify Family geupdatet. Dann werde ich wohl nur die Geräte sehen können, wo mein Spotify gerade drauf läuft und nicht das von meiner Frau. Ich glaube nicht, dass die Mitglieder bei Family gesondert Client ID und Secret ID erhalten werden. :roll:

                            Host: NUC8i3 mit Proxmox:

                            • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
                            • Slave: Pi4
                            1 Reply Last reply
                            0
                            • L Offline
                              L Offline
                              Lucky
                              wrote on last edited by
                              #14

                              das weis ich ehelich gesagt gar nicht… hat denn jeder bei Spotify Family einen eigenen Account oder läuft das über einen ?

                              Werden die Geräte der anderen Nutzer aufgelistet ?

                              1 Reply Last reply
                              0
                              • lobomauL Offline
                                lobomauL Offline
                                lobomau
                                wrote on last edited by
                                #15

                                @Lucky:

                                das weis ich ehelich gesagt gar nicht… hat denn jeder bei Spotify Family einen eigenen Account oder läuft das über einen ?

                                Werden die Geräte der anderen Nutzer aufgelistet ? `
                                Das ist das Problem. Es ist nur ein Account. Die anderen bekommen eine Einladung und werden nicht aufgelistet.

                                Host: NUC8i3 mit Proxmox:

                                • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
                                • Slave: Pi4
                                1 Reply Last reply
                                0
                                • C Offline
                                  C Offline
                                  Conquest
                                  wrote on last edited by
                                  #16

                                  Hallo würde auch gerne dein Spotify Script nutzen leider hänge ich schon ganz am Anfang. Ich hab keine Ahnung wo man die Client ID bekommt. Was muss ich auf der Developer Seite anklicken? Eine Anleitung mit Screenshots wäre super gewesen dann würden viele Fragen vielleicht gar nicht auftauchen.

                                  Würde mich freuen wenn mir jemand weiterhelfen könnte.

                                  Gruß Torsten
                                  470_bildschirmfoto_vom_2017-12-06_14-14-58.png

                                  1 Reply Last reply
                                  0
                                  • lobomauL Offline
                                    lobomauL Offline
                                    lobomau
                                    wrote on last edited by
                                    #17

                                    @spoerl.torsten:

                                    Hallo würde auch gerne dein Spotify Script nutzen leider hänge ich schon ganz am Anfang. Ich hab keine Ahnung wo man die Client ID bekommt. Was muss ich auf der Developer Seite anklicken? Eine Anleitung mit Screenshots wäre super gewesen dann würden viele Fragen vielleicht gar nicht auftauchen.

                                    Würde mich freuen wenn mir jemand weiterhelfen könnte.

                                    Gruß Torsten `
                                    Oben auf My Apps, musst registriert sein, dann create an app.

                                    Gibst der app einen Namen und eine Beschreibung und im nächsten Fenster hast du schon die Client ID

                                    Host: NUC8i3 mit Proxmox:

                                    • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
                                    • Slave: Pi4
                                    1 Reply Last reply
                                    0
                                    • C Offline
                                      C Offline
                                      Conquest
                                      wrote on last edited by
                                      #18

                                      Super hat funktioniert. Danke lobomau.

                                      Hab aber noch eine Frage und zwar wenn ich auf den Button "Get_User_Playlists" drücke passiert nichts ist das normal? Hatte gedacht das darüber meine in Spotify angelegte Playlist angezeigt wird.

                                      LG Torsten

                                      1 Reply Last reply
                                      0
                                      • lobomauL Offline
                                        lobomauL Offline
                                        lobomau
                                        wrote on last edited by
                                        #19

                                        @spoerl.torsten:

                                        Super hat funktioniert. Danke lobomau.

                                        Hab aber noch eine Frage und zwar wenn ich auf den Button "Get_User_Playlists" drücke passiert nichts ist das normal? Hatte gedacht das darüber meine in Spotify angelegte Playlist angezeigt wird.

                                        LG Torsten `
                                        Gerne.

                                        Hab es gerade ausprobiert. Bei Klick auf Get User Playlists werden darunter die Playlist Ordner angelegt.

                                        Host: NUC8i3 mit Proxmox:

                                        • ioBroker CT Debian 13, npm 10.9.3, nodejs 22.20.0
                                        • Slave: Pi4
                                        1 Reply Last reply
                                        0
                                        • ruhr70R Offline
                                          ruhr70R Offline
                                          ruhr70
                                          wrote on last edited by
                                          #20

                                          @Lucky:

                                          so, ich habe das "etwas" erweitert 😉 `

                                          Stark, was hier alles gezaubert wird.

                                          Meinst Du, es macht Sinn, wenn die aktualisierte Beschreibung und das aktuelle Skript im ersten Post gepflegt wird?

                                          Dann braucht man nicht immer im Thread suchen 🙂

                                          Adapter: Fritzbox, Unify Circuit
                                          Skripte: dynamic hue, Bluetooth Scan, Multi-Ereignisliste

                                          1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          170

                                          Online

                                          32.4k

                                          Users

                                          81.3k

                                          Topics

                                          1.3m

                                          Posts
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Recent
                                          • Tags
                                          • Unread 0
                                          • Categories
                                          • Unreplied
                                          • Popular
                                          • GitHub
                                          • Docu
                                          • Hilfe