# $Id$ # ############################################## # # 2022.01.12 v0.2.15 # - FEATURE: Unterstützung A2Z8O30CD35N8F Sonos Arc # Unterstützung A3EVMLQTU6WL1W Fire TV Stick 4K Max # Unterstützung A39Y3UG1XLEJLZ Fitbit Sense # Unterstützung A1XWJRHALS1REP Echo Show 5 2. Generation # # 2021.10.19 v0.2.14 # - BUG: https://forum.fhem.de/index.php/topic,82631.msg1175268.html#msg1175268 (Danke Benutzer JudgeDredd) # - FEATURE: Unterstützung A31DTMEEVDDOIV FireTV Stick # # 2021.09.15 v0.2.13 # - CHANGE: Probleme set tunein (Danke Benutzer adn77) # # 2021.06.24 v0.2.12 # - FEATURE: Unterstützung A15996VY63BQ2D Echo Show 8 Gen2 # Unterstützung A2WFDCBDEXOXR8 Bose Soundbar 700 # # 2021.05.18 v0.2.11 # - FEATURE: Unterstützung A1DL2DVDQVK3Q Fire Tab HD 10 # # 2021.04.26 v0.2.10 # - CHANGE: Logeintrag "[Echodevice] [echodevice_SendCommand] [12] IGNORIERE Command=activities Abfrage in CMD_Queue schon vorhanden!" auf Loglevel 4 geändert # - FEATURE: Unterstützung AIPK7MM90V7TB Echo Show Gen3 # # 2021.02.10 v0.2.9 # - BUG: Probleme wenn getbehavior keine Antwort liefert. # - CHANGE: CMD_Queue check # ($hash->{model} eq "Reverb" || $hash->{model} eq "Sonos One" || $hash->{model} eq "Sonos Beam" Unterscheidung entfernt # - FEATURE: Geräte Kennung THIRD_PARTY_AVS_SONOS_BOOTLEG hinzugefügt # # 2021.02.07 v0.2.8 # - BUG: Sonso aktualisiert keine voice Readings # # 2021.01.28 v0.2.7 # - BUG: Routinen werden nicht mehr angezeigt # # 2021.01.18 v0.2.6 # - BUG: item_shopping_delete,item_shopping_add,item_task_delete,item_task.add # - FEATURE: Unterstützung A23FPV4BT7FH68 Yamaha YAS-209 Soundbar # Untersützung A265XOI9586NML Fire TV Stick 4K # # 2020.12.11 v0.2.5 # - FEATURE: Text Kommando an Amazon schicken "set textcommand" # Unterstützung A2WN1FJ2HG09UN Ultimate Alexa # # 2020.11.19 v0.2.4 # - CHANGE: get custom-history-records Dialog angepasst # - FEATURE: Unterstützung A2U21SRK4QGSE1 Echo Dot Gen4 # # 2020.11.18 v0.2.3 # - FEATURE: get custom-history-records # Unterstützung A2H4LV5GIZ1JFT Echo Dot Gen4 with Clock # Unterstützung AVE5HX13UR5NO Zero Touch (Logitech) # Unterstützung A3GZUE7F9MEB4U Sony WH-100XM3 # Unterstützung A2J0R2SD7G9LPA Lenovo P10 # Unterstützung A1J16TEDOYCZTN Amazon Tablet # Unterstützung A38EHHIB10L47V Fire HD 8 Tablet # Unterstützung A112LJ20W14H95 Media Display # Unterstützung A1H0CMF1XM0ZP4 Bose Soundtouch # Unterstützung AAMFMBBEW2960 Garmin DriveSmart 65 with Amazon Alexa # Unterstützung A2IVLV5VM2W81 Mobile Voice iOS";} # Unterstützung A2TF17PFR55MTB Mobile Voice Android # Unterstützung A3V3VA38K169FO Fire Tablet # Unterstützung AVD3HM0HOJAAL Sonos One # Unterstützung A1C66CX2XD756O Fire HD 8 Tablet # Unterstützung A17LGWINFBUTZZ Anker Roav Car Charger # Unterstützung A2XPGY5LRKB9BE FitBit watch # Unterstützung A2Y04QPFCANLPQ Bose QC35 II # Unterstützung A3BW5ZVFHRCQPO Alexa Car # Unterstützung A303PJF6ISQ7IC Echo Auto # Unterstützung A1ZB65LA390I4K Fire HD 10 Tablet # Unterstützung AVU7CPPF2ZRAS Fire HD 8 Plus (2020) # Unterstützung A24Z7PEXY4MDTK Sony WF-1000X # Unterstützung ABN8JEI7OQF61 Sony WF-1000XM3 # Unterstützung A7S41FQ5TWBC9 Sony WH-1000XM4 # # 2020.11.06 v0.2.2 # - FEATURE: Unterstützung A3RMGO6LYLH7YN Echo Dot Gen4 # # 2020.10.07 v0.2.1 # - BUG: Not a HASH reference at ./FHEM/37_echodevice.pm line 2975 # # 2020.10.05 v0.2.0 # - BUG: Not a HASH reference at ./FHEM/37_echodevice.pm line 3532 # # 2020.09.25 v0.1.9 # - BUG: Not a HASH reference at ./FHEM/37_echodevice.pm line 2687 # # 2020.05.06 v0.1.8 # - BUG: Zu viele Loginfos bei set "NPM_login refresh" # # 2020.04.27 v0.1.7 # - FEATURE: Unterstützung A1WAR447VT003J Yamaha MusicCast 20 # - BUG: set "NPM_login refresh" # - CHANGE: get status erweitert # # 2020.04.22 v0.1.5 # - CHANGE: Mehr Loginfos bei set "NPM_login refresh" # # 2020.04.20 v0.1.4 # - CHANGE: Keepalive aktiviert (cookielogin6) # # 2020.04.14 v0.1.3 # - CHANGE: Mehr Loginfos bei set "NPM_login new" # # 2020.04.12 v0.1.2 # - CHANGE: Mehr Loginfos bei set "NPM_login new" # # 2020.04.08 v0.1.1 # - CHANGE: Keepalive aktiviert # - BUG: set "NPM_login new" # - FEATURE: Unterstützung A3RBAYBE7VM004 ECHO Studio # Unterstützung A3SSG6GR8UU7SN ECHO SUB # Unterstützung A1HNT9YTOBE735 Telekom Smart Speaker # set sounds: (Sounds gemäß Routine-Übersicht) # # 2019.12.24 v0.1.0 # - FEATURE: Unterstützung A1Z88NGR2BK6A2 ECHO Show 8 # Unterstützung A2JKHJ0PX4J3L3 ECHO FireTv Cube 4K # # 2019.12.22 v0.0.60 # - FEATURE: Unterstützung A3VRME03NAXFUB ECHO Flex # Unterstützung AKOAGQTKAS9YB ECHO Connect # Unterstützung A3NTO4JLV9QWRB Gigaset L800HX # # 2019.11.05 v0.0.59 # - FEATURE: Nachricht an Handy App schicken "set mobilmessage" # - CHANGE: Hilfetexte erweitert # # 2019.10.27 v0.0.58 # - FEATURE: Unterstützung A30YDR2MK8HMRV ECHO Gen 3 # # 2019.10.17 v0.0.57 # - FEATURE: Unterstützung A3FX4UWTP28V1P ECHO Gen 3 # # 2019.10.09 v0.0.56 # - FEATURE: Hintergrundbild ECHO SHOW ändern "set homescreen" # # 2019.09.20 v0.0.55 # - CHANGE: speak_volume Auswertung Account-Device/Echo-Device # DEF xxx@xxx.de xxx = NPM Login Modus # - BUGFIX: presence # # 2019.07.22 v0.0.54 # - FEATURE: Unterstützung A1RABVCI4QCIKC ECHO dot 3 # # 2019.02.19 v0.0.53 # - FEATURE: Unterstützung A4ZP7ZC4PI6TO ECHO 5 # Unterstützung A1RTAM01W29CUP Alexa App for PC # Unterstützung A21Z3CGI8UIP0F HEOS # # 2019.02.19 v0.0.52 # - FEATURE: Alarme "_originalDate" als Reading # - BUGFIX: Readings *_count Wert # # 2019.02.18 v0.0.51z # - BUGFIX: NPM Proxy IP Adresse / Port usw. # set routine_play - Unterstützung Smart Home Geräte # set speak - Sonderzeichen " entfernen # get conversations https://forum.fhem.de/index.php/topic,82631.msg903955.html#msg903955 # Bluetooth Geräte bereinigen # - FEATURE: Unterstützung AppRegisterLogin per NPM # Unterstützung A10L5JEZTKKCZ8 VOBOT # Unterstützung A1JJ0KFC4ZPNJ3 ECHO Input # Unterstützung AKPGW064GI9HE Fire TV Stick 4K # Unterstützung A37SHHQ3NUL7B5 Bose Home Speaker 500 # Unterstützung AVN2TMX8MU2YM Bose Home Speaker 500 # set speak_ssml https://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html # https://developer.amazon.com/de/docs/custom-skills/speech-synthesis-markup-language-ssml-reference.html # get status - Statusinformationen zum Modul # Attribut "ignorevoicecommand" https://forum.fhem.de/index.php/topic,82631.msg906424.html#msg906424 # Alarme "_recurringPattern" als Reading # - CHANGE: https://forum.fhem.de/index.php/topic,82631.msg869460.html#msg869460 # # 2018.12.02 v0.0.50 # - FEATURE: Unterstützung A32DDESGESSHZA Echo Dot Gen3 # # 2018.11.13 v0.0.49 # - BUGFIX: reading voice # Sonos Beam A3NPD82ABCPIDP # - FEATURE: reading voice_timestamp # # 2018.10.30 v0.0.48i # - CHANGE: Attribut browser_useragent_random (Standard=0) # Neuer Status "connected but loginerror" # - BUGFIX: https://forum.fhem.de/index.php/topic,82631.msg850171.html#msg850171 # CMD_QUEUE leeren wenn loginerror # set loginwithcaptcha # - FEATURE: Unterstützung A3R9S4ZZECZ6YL Fire Tab HD 10 # Unterstützung A3L0T0VL9A921N Fire Tab HD 8 # Unterstützung A2M4YX06LWP8WI Fire Tab 7 # Unterstützung A2E0SNTXJVT7WK Fire TV V1 # Unterstützung A2GFL5ZMWNE0PX Fire TV # Unterstützung A12GXV8XMS007S Fire TV # Unterstützung A3HF4YRA2L7XGC Fire TV Cube # Unterstützung ADVBD696BHNV5 Fire TV Stick V1 # Unterstützung A2LWARUGJLBYEW Fire TV Stick V2 # Unterstützung AP1F6KUH00XPV ECHO Stereopaar # # 2018.10.25 v0.0.47 # - FEATURE: Unterstützung neuer Sonos Beam A15ERDAKK5HQQG # - BUGFIX: browser_language default = de,en-US;q=0.7,en;q=0.3 # browser_useragent default = Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0 # # 2018.10.17 v0.0.46b # - BUGFIX: Attribut intervalvoice 0=realtime bis 100 Sekunden # - FEATURE: Reading "bluetooth_MAC-Adresse" zeigt Connected Status an (ECHO Device) # Readings "config_address_from","config_address_to" und "config_address_between" (Account Device) # Unterstützung neuer ECHO Show Gen2 AWZZ5CVHX2CD # Unterstützung neuer ECHO Dot Gen3 A32DOYMUN6DTXA # # 2018.10.15 v0.0.45 # - FEATURE: Attribut intervalvoice 0=realtime bis 100 Sekunden # # 2018.10.10 v0.0.44 # - FEATURE: set alarm_off und alarm_on (Wecker und Musikwecker) # set alarm_normal und alarm_repeat # set routine_play (Abspielen und ausführen von Routinen) # set info (Beliebig_Auf_Wiedersehen,Beliebig_Bestaetigung,Beliebig_Geburtstag,Beliebig_Guten_Morgen,Beliebig_Gute_Nacht,Beliebig_Ich_Bin_Zuhause,Beliebig_Kompliment,Erzaehle_Geschichte,Erzaehle_Was_Neues,Erzaehle_Witz,Kalender_Heute,Kalender_Morgen,Kalender_Naechstes_Ereignis,Nachrichten,Singe_Song,Verkehr,Wetter) # set config_address_from config_address_to config_address_between (Einstellungen Verkehr) # get address (Hier kann die Adresse gesucht werden für die set Befehle config_address_from config_address_to config_address_between) # Unterstützung neuer ECHO Plus 2 A18O6U1UQFJ0XK # Lautstärke sollte jetzt immer regelbar sein! # - BUGFIX: Reading "currentTuneInID" # # 2018.10.08 v0.0.43a # - CHANGE: set speak von Multiroom Geräten entfernt # - BUGFIX: set Account Device # get conversations # # 2018.09.03 v0.0.42 # - BUGFIX: Login # - CHANGE: readingsBulkUpdateIfChanged to readingsBulkUpdate # # 2018.08.23 v0.0.41 # - BUGFIX: Login # # 2018.08.22 v0.0.40 # - FEATURE: set speak Natives TTS # "set loginwithcaptcha" # - CHANGE: browser_language default = de-DE # browser_useragent default = Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0 # Attribut "browser_save_data" wird jetzt auch am ECHO angezeigt # - BUGFIX: 2FACode Authentifizierung # "get actions" mit leeren Gerätennamen. Fehler im LOG # Attribut disable # Login # # 2018.06.08 v0.0.39 # - BUGFIX: get html_results # # 2018.06.07 v0.0.38 # - FEATURE: Anzeigen der Amazon Login Ergebnisse (get html_results) # Attribut "browser_save_data" # # 2018.05.30 v0.0.37 # - BUGFIX: ReLogin bei "COOKIE ERROR" # - FEATURE: Neues Attribut "browser_language" # # 2018.05.17 v0.0.36 # - CHANGE: Accept-Language: de,en-US # # 2018.05.17 v0.0.35 # - BUGFIX: Attribut "cookie" # # 2018.05.07 v0.0.34 # - BUGFIX: Attribut "intervalsettings" # ReLogin bei "COOKIE ERROR" # # 2018.04.09 v0.0.33 # - CHANGE: get "help" zusätzliche Anleitung MP3 Streamserver & IceCast2 # TTS_Nachrichten werden mindestens mit der Laustärke von Reading "volume_alarm" abgespielt. # Zwei Faktor Authentifizierung (set login2FACode) Danke Benutzer JoWiedmann https://forum.fhem.de/index.php/topic,82631.msg787815.html#msg787815 # Verstecken von Helper "CUSTOMER","COMMSID","COOKIE","DIRECTID","PASSWORD","USER","HTTP_CONNECTION" und "SERIAL" # Verstecken von Readings "COOKIE","AWS_Access_Key" und "aws_secret_key" # - FEATURE: Neues Reading "currentTuneInID" # set "tts_translate" Übersetzung von der Webseite http://www.online-translator.com/ Reading="tts_translate_result" # Neues Attribut "TTS_Translate_From" # TTS Translate unterstützt folgende Sprachen:dutch,english,french,german,italian,japanese,korean,portuguese,russian,spanish und turkish # TTS MP3 Länge ermitteln. Reading = "tts_lenght" # TTS Nachrichte abspielen wenn schon ein TuneIn Sender läuft. # - BUGFIX: Log Eintrag bei TTS & Attribut: "TTS_normalize" entfernt # Name Attribut "TTS_Voice" WelshEnglish_Female_Gwyneth # get settings # Verzeichnis "echodevice" wurde nicht automatisch angelegt # # 2018.03.20 v0.0.32 # - FEATURE: Neues Attribut: "TTS_normalize" (only mp3 Outputformat!) # - BUGFIX: remove sleep 0.5 # # 2018-03-19 v0.0.31 # - BUGFIX: Amazone TTS Nachrichten < 3 Zeichen # File "cache/pollyspeech.py", line 4, in # get "devices" https://forum.fhem.de/index.php/topic,82631.msg783487.html#msg783487 # # 2018-03-18 v0.0.30 # - FEATURE: Text2Speech (TTS) inkl. Google und Amazon Stimmen # Musik aus dem eigenen LAN abspielen # Neue Attribute: ALLE "TTS_Voice" und "TTS_IgnorPlay" # Neue Set Befehle: ACCOUNT "AWS_Access_Key","AWS_OutputFormat","AWS_Secret_Key","POM_Filename","POM_IPAddress","POM_TuneIn","TTS_Filename","TTS_IPAddress" und "TTS_TuneIn" # Neue Set Befehle: Nur ECHO "tts", "playownmusic", "playownplaylist", "deleteownplaylist", und "saveownplaylist" # - CHANGE: get "help" # Reihenfolge get settings https://forum.fhem.de/index.php/topic,82631.msg781731.html#msg781731 # # v0.0.29 # - FEATURE: Zwei Faktor Authentifizierung (set login2FACode) Danke Benutzer JoWiedmann https://forum.fhem.de/index.php/topic,82631.msg780848.html#msg780848 # # v0.0.28 # - CHANGE: get "Conversations" auf nonBlocking # get "tunein" auf nonBlocking & move to Echo Device & play link # get "tracks" auf nonBlocking # get "devices" auf nonBlocking # set "autocreat_devices" auf nonBlocking # httpversion = "1.1" # - FEATURE: get "actions" # get "primeplayeigene_albums" # get "primeplayeigene_tracks" # get "primeplayeigene_artists" # get "primeplayeigeneplaylist" # get "help" # Multiroom add get settings & tunein # - BUGFIX: primeplayeigene # # v0.0.27 # - BUGFIX: Not an ARRAY reference at ./FHEM/37_echodevice.pm line 1610 # # v0.0.26 # - BUGFIX: read readings if amazon device is connected # # v0.0.25 # - BUGFIX: set reminder_normal # Attribut disable # no Internet connect # - FEATURE: Attribut browser_useragent_random (Standard=1) # Attribut intervallogin (Standard=60) # # v0.0.24 # - BUGFIX: Timer Readings # # v0.0.23 # - BUGFIX: Nested quantifiers in regex # - CHANGE: Reading version # # v0.0.22 # - FEATURE: Attribut browser_useragent https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Attribute # # v0.0.21 # - CHANGE: Header # # v0.0.20 # - CHANGE: Cookie erstellen auf nonBlocking # Cookie erstellen Timeout 10 sekunden # - BUGFIX: div. # # v0.0.19 # - BUGFIX: Fehlt bei "get" der Punkt "conversations" # Fehlt bei "set" der Punkt "textmessage" # # v0.0.18 # - FEATURE: autocreate Standard Raum "Amazon" # - CHANGE: COOKIE wird nicht mehr erneuert! # # v0.0.17 # - FEATURE: refresh ECHO devices (Attribut autocreate_refresh) # define icon to echo # - CHANGE: Header # # v0.0.16 # - FEATURE: autocreate ECHO Spot # # v0.0.15 # - CHANGE: deletereading auf FHEM Command umgestellt # - BUGFIX: MausicAlarm # # v0.0.14 # - FEATURE: autocreate ECHO Multiroom # autocreate Sonos One # autocreate Reverb # - CHANGE: model im Klartext z.B. Echo Dot # # v0.0.13 # - BUGFIX: Cookie # # v0.0.12 # - FEATURE: Support Musicalarm # # v0.0.11.2 # - FEATURE: neue Readings timer_XX, reminder_X und alarm_xx # neue Readings deviceAddress, timeZoneId # Zeigt den Status für Mikrofon Reading = microphone # Zeigt den Status ob der ECHO online ist. Reading = online # - BUGFIX: Reading voice leer # Div. Logeinträge wenn Variablen leer sind # - CHANGE : Reading active entfernt # # v0.0.10 # - BUGFIX: Einkaufsliste und ToDo Liste (Fehler beim hinzufügen und entfernen von Einträgen) # # v0.0.9 # - BUGFIX: ECHO Devices Readings wurden nicht aktualisiert # # v0.0.8 # - FEATURE: Attribut tuneid_default (Hier kann ein Standard TuneIn Sender angegeben werden) # set notifications_delete (löschen von Erinnerungen, Timer und Wecker) # autocreate ECHO Show Geräte # löschen und hinzufügen von Einkauflisten- und Task Einträgen # # v0.0.7 # - FEATURE: Interval Anpassung beim abspielen eines Songs # - CHANGE: set reminder_normal ohne Datumsangabe (Reminder sofort ausgeführt)) # # v0.0.6 # - CHANGE : Log Einträge reduziert # Reading "voice" zum Echo Device verschoben # - BUGFIX: set reminder_normal Text (Reminder sofort ausgeführt)) # ACCOUNT DEVICE macht jetzt die abfragen für wakeword, volume_alarm, dnd, active, bluetooth # Standard Interval 60 Sekunden # # v0.0.5 # - CHANGE : set reminder_normal (durch weglassen der Uhrzeit wird der Reminder sofort ausgeführt) # - FEATURE: Attribut reminder_delay (wird für reminder_normal benötigt. Standardwert = 10 sekunden) # # v0.0.4 # - CHANGE: set reminder vom ACCOUNT DEVICE entfernt # set reminder zum Echo DEVICE hinzugefügt # - FEATURE: set reminder_normal # set reminder_repeat # # v0.0.3 # - BUGFIX: Anzeige set befehle primeplayeigene,primeplayeigeneplaylist,primeplaylist und primeplaysender # # v0.0.2 # - FEATURE: set primeplayeigene # set primeplayeigeneplaylist # set primeplaylist # set primeplaysender # # v.0.0.1 # - BUGFIX: blocking restart fhem # readings # # Copyright by Michael Winkler # e-mail: michael.winkler at online.de # # This file is part of fhem. # # Fhem is free software: you can redistribute it andor modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem 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 fhem. If not, see . # # https://forum.fhem.de/index.php/topic,82631.0.html # ############################################################################## package main; #use strict; use Time::Local; use Encode; use Encode qw/from_to/; use URI::Escape; use Data::Dumper; use JSON; use utf8; use Date::Parse; use Time::Piece; use lib ('./FHEM/lib', './lib'); use MP3::Info; use MIME::Base64; my $ModulVersion = "0.2.15"; my $AWSPythonVersion = "0.0.3"; my $NPMLoginTyp = "unbekannt"; my $QueueNumber = 0; ############################################################################## sub echodevice_Initialize($) { my ($hash) = @_; my $name = $hash->{NAME}; $hash->{DefFn} = "echodevice_Define"; $hash->{UndefFn} = "echodevice_Undefine"; $hash->{NOTIFYDEV} = "global"; $hash->{NotifyFn} = "echodevice_Notify"; $hash->{GetFn} = "echodevice_Get"; $hash->{SetFn} = "echodevice_Set"; $hash->{AttrFn} = "echodevice_Attr"; $hash->{AttrList} = "disable:0,1 ". "IODev ". "TTS_Voice:AustralianEnglish_Female_Nicole,AustralianEnglish_Male_Russell,BrazilianPortuguese_Female_Vitoria,BrazilianPortuguese_Male_Ricardo,BritishEnglish_Female_Amy,BritishEnglish_Female_Emma,BritishEnglish_Male_Brian,CanadianFrench_Female_Chantal,CastilianSpanish_Female_Conchita,CastilianSpanish_Male_Enrique,Danish_Female_Naja,Danish_Male_Mads,Dutch_Female_Lotte,Dutch_Male_Ruben,French_Female_Celine,French_Male_Mathieu,German_Female_Google,German_Female_Marlene,German_Female_Vicki,German_Male_Hans,Icelandic_Female_Dora,Icelandic_Male_Karl,IndianEnglish_Female_Aditi,IndianEnglish_Female_Raveena,Italian_Female_Carla,Italian_Male_Giorgio,Japanese_Female_Mizuki,Japanese_Male_Takumi,Korean_Female_Seoyeon,Norwegian_Female_Liv,Polish_Female_Ewa,Polish_Female_Maja,Polish_Male_Jacek,Polish_Male_Jan,Portuguese_Female_Ines,Portuguese_Male_Cristiano,Romanian_Female_Carmen,Russian_Female_Tatyana,Russian_Male_Maxim,Swedish_Female_Astrid,Turkish_Female_Filiz,USEnglish_Female_Ivy,USEnglish_Female_Joanna,USEnglish_Female_Kendra,USEnglish_Female_Kimberly,USEnglish_Female_Salli,USEnglish_Male_Joey,USEnglish_Male_Justin,USEnglish_Male_Matthew,USSpanish_Female_Penelope,USSpanish_Male_Miguel,WelshEnglish_Female_Gwyneth,WelshEnglish_Male_Geraint ". "TTS_IgnorPlay:0,1 ". "TTS_normalize:slider,5,1,40 ". "TTS_Translate_From:dutch,english,french,german,italian,japanese,korean,portuguese,russian,spanish,turkish ". "intervalsettings ". "intervallogin ". "intervalvoice:slider,0,1,100 ". "ignorevoicecommand ". "speak_volume:slider,0,1,100 ". "server ". "cookie ". "reminder_delay ". "tunein_default ". "autocreate_refresh:0,1 ". "browser_useragent ". "browser_language ". "browser_save_data:0,1 ". "browser_useragent_random:0,1 ". "npm_proxy_port ". "npm_proxy_ip ". "npm_proxy_listen_ip ". "npm_refresh_intervall ". "npm_bin ". "npm_bin_node ". $readingFnAttributes; } sub echodevice_Define($$$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my ($found, $dummy); return "syntax: define echodevice " if(int(@a) != 4 ); my $name = $hash->{NAME}; $attr{$name}{server} = "layla.amazon.de" if( defined($attr{$name}) && !defined($attr{$name}{server}) ); RemoveInternalTimer($hash); if($a[2] =~ /crypt/ || $a[2] =~ /@/ || $a[2] =~ /^\+/) { $hash->{model} = "ACCOUNT"; my $user = $a[2]; my $pass = $a[3]; if ($user eq 'xxx@xxx.xx' and $pass eq "xxx") { # Amazon NPM Login Modus $hash->{LOGINMODE} = "NPM"; } else { # Amazon normal Login Modus my $username = echodevice_encrypt($user); my $password = echodevice_encrypt($pass); $hash->{DEF} = "$username $password"; $hash->{helper}{".USER"} = $username; $hash->{helper}{".PASSWORD"} = $password; $hash->{LOGINMODE} = "NORMAL"; } $hash->{helper}{TWOFA} = ""; $hash->{helper}{SERVER} = $attr{$name}{server}; $hash->{helper}{SERVER} = "layla.amazon.de" if(!defined($hash->{helper}{SERVER})); $hash->{helper}{RUNLOGIN} = 0; $hash->{helper}{".LOGINERROR"} = 0; $modules{$hash->{TYPE}}{defptr}{"account"} = $hash; $hash->{STATE} = "INITIALIZED"; # set default settings on first define if ($init_done and $attr{$name}{icon} eq "" and $attr{$name}{room} eq "") { $attr{$name}{icon} = 'echo'; $attr{$name}{room} = 'Amazon'; } InternalTimer(gettimeofday() + 5 , "echodevice_FirstStart" , $hash, 0); InternalTimer(gettimeofday() + 10 , "echodevice_GetSettings", $hash, 0); } else { # Attribute my $DeviceTyp = $a[2]; my $DeviceSerial = $a[3]; # Amazon ECHO Device $hash->{STATE} = "INITIALIZED"; $hash->{model} = echodevice_getModel($DeviceTyp);#$a[2]; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "model", $hash->{model}, 1); readingsBulkUpdate($hash, "state", "INITIALIZED", 1); readingsEndUpdate($hash,1); $hash->{helper}{DEVICETYPE} = $DeviceTyp; $hash->{helper}{".SERIAL"} = $DeviceSerial; $hash->{LOGINMODE} = "IODEV"; $modules{$hash->{TYPE}}{defptr}{$DeviceSerial} = $hash; my $account = $modules{$hash->{TYPE}}{defptr}{"account"}; Log3 $name, 0, "[echodevice_Define] load ECHO Devicename=$name Devicetype=" . $DeviceTyp . " Devicemodel=" . $hash->{model}; $hash->{IODev} = $account; $attr{$name}{IODev} = $account->{NAME} if( !defined($attr{$name}{IODev}) && $account); if ($hash->{model} ne "THIRD_PARTY_AVS_MEDIA_DISPLAY") { InternalTimer(gettimeofday() + 1, "echodevice_GetSettings", $hash, 0); } } readingsSingleUpdate ($hash, "COOKIE_MODE", $hash->{LOGINMODE} ,0); Log3 $name, 4, "[$name] [echodevice_Define] Getting auth URL return"; return undef; } sub echodevice_Undefine($$) { my ($hash, $arg) = @_; my $name = $hash->{NAME}; RemoveInternalTimer($hash); delete( $modules{$hash->{TYPE}}{defptr}{"ACCOUNT"} ) if($hash->{model} eq "ACCOUNT"); delete( $modules{$hash->{TYPE}}{defptr}{$hash->{helper}{".SERIAL"}} ) if($hash->{model} ne "ACCOUNT"); return undef; } sub echodevice_Notify($$) { my ($hash,$dev) = @_; my $name = $hash->{NAME}; return if($dev->{NAME} ne "global"); return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}})); Log3 "echodevice", 4, "[$name] [echodevice_Notify] echodevice: notify reload"; return undef; } sub echodevice_Get($@) { my ($hash, @a) = @_; shift @a; my $command = shift @a; my $parameter = join(' ',@a); my $name = $hash->{NAME}; my $usage = "Unknown argument $command, choose one of "; return $usage if ($hash->{model} eq 'unbekannt'); return $usage if ($hash->{model} eq 'Sonos One'); return $usage if ($hash->{model} eq 'Sonos Beam'); if ($hash->{model} eq "Reverb") { $usage .= "help:noArg " ; } elsif ($hash->{model} eq "ACCOUNT") { $usage .= "settings:noArg devices:noArg actions:noArg tracks:noArg help:noArg conversations:noArg html_results:noArg address status:noArg customer-history-records:noArg"; } else { $usage .= "tunein settings:noArg primeplayeigene_albums primeplayeigene_tracks primeplayeigene_artists primeplayeigeneplaylist:noArg help:noArg html_results:noArg "; } #return "no get" if ($hash->{model} eq "Echo Multiroom"); return $usage if $command eq '?'; if ($command ne "help" || $command ne "help_results" || $command ne "status") { } elsif (IsDisabled($name)) { $hash->{STATE} = "disabled"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "disabled", 1); readingsEndUpdate($hash,1); return "$name is disabled. Aborting..."; } else { } my $ConnectState = ""; if($hash->{model} eq "ACCOUNT") {$ConnectState = $hash->{STATE}} else {$ConnectState = $hash->{IODev}->{STATE}} if ($command eq "help") { my $return = ''; $return .= "

Hilfe:

"; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= "
Dokumentation Modul   
"."Beschreibung"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice
"."Readings"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Readings
"."Attribute"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Attribute
"."Set"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Set
"."Get"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Get
"."Medieninformationen ermitteln"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Medieninformationen_ermitteln
"."Cookie_ermitteln"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Cookie_ermitteln
"."Loginmit Captcha"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Login_captcha
"."MP3 Playlisten"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#MP3_Playlisten
"."Amazon Stimmen"."   https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#AWS_Konfiguration
 
Diverse Anleitungen
"."Amazon ECHO TTS/POM"."   https://mwinkler.jimdo.com/smarthome/sonstiges/amazon-echo-tts-mp3s
"."MPD Streamserver"."   https://mwinkler.jimdo.com/smarthome/sonstiges/mpd-streamserver
"."MPD Web Frontend"."   https://mwinkler.jimdo.com/smarthome/sonstiges/mpd-webfrontend
"."IceCast2"."   https://mwinkler.jimdo.com/smarthome/sonstiges/icecast2
"."NPM Login"."   https://mwinkler.jimdo.com/modul-echodevice-npm
 
Forum
"."Forums Thread"."   https://forum.fhem.de/index.php/topic,82631.0.html
 
"; return $return; } if ($command eq "html_results") { my $timestamp ; my $epoch_timestamp ; my $return = ''; $return .= "

Amazon HTML Results:

"; $return .= ""; opendir(DH, $FW_dir . "/echodevice/results"); my @files = readdir(DH); closedir(DH); my @filessorted = sort @files; foreach my $file (@filessorted){ # skip . and .. next if($file =~ /^\.$/); next if($file =~ /^\.\.$/); # Datum $epoch_timestamp = 0 ; $timestamp = 0 ; if ((-e $FW_dir . "/echodevice/results/" . $file)) { $epoch_timestamp = (stat($FW_dir . "/echodevice/results/" . $file))[9]; $timestamp = localtime($epoch_timestamp); } if ($hash->{model} eq "ACCOUNT" || index($file, $name) == 0 ) { # $file is the file used on this iteration of the loop $return .= ""; } } $return .= ""; $return .= "
Datum   HTML Result Dateiname
".$timestamp."   " . $file . "
 
"; return $return; } if ($command eq "status") { my $return = ''; #Allgemeine Informationen $return .= ''; $return .= "

Modul Infos:

"; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; #$return .= ""; # Attribute auslesen while ( my ($key, $value) = each %{$attr{$name}} ) { $return .= ""; } $return .= "
Beschreibung   Bereich   Wert
STATE   Reading" . ReadingsVal( $name, "state", "unbekannt") . "
Version   Reading" . ReadingsVal( $name, "version", "unbekannt") . "
NPM Cookie Version   Reading" . echodevice_NPMCheckVersion($hash,"cache/alexa-cookie/node_modules/alexa-cookie2/package.json","get status") . "
COOKIE_STATE   Reading" . ReadingsVal( $name, "COOKIE_STATE", "unbekannt") . "
COOKIE_TYPE   Reading" . ReadingsVal( $name, "COOKIE_TYPE", "unbekannt") . "
COOKIE_MODE   Reading" . $hash->{LOGINMODE} . "
amazon_refreshtoken   Reading" . ReadingsVal( $name, "amazon_refreshtoken", "unbekannt") . "
.COOKIE   Reading123
" . $key . "   Attribut" . $value . "
  
"; #Allgemeine Cookie Infos $return .= ''; $return .= "

Amazon Cookie:

"; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= ""; $return .= "
Beschreibung   Bereich   Wert
.COOKIE   Reading" . substr(ReadingsVal( $name, ".COOKIE", "unbekannt" ), 0, 20) . "....
COOKIE_STATE   Reading" . ReadingsVal( $name, "COOKIE_STATE", "unbekannt") . "
COOKIE_TYPE   Reading" . ReadingsVal( $name, "COOKIE_TYPE", "unbekannt") . "
amazon_refreshtoken   Reading" . ReadingsVal( $name, "amazon_refreshtoken", "unbekannt") . "
.COOKIE   Helper" . substr($hash->{helper}{".COOKIE"}, 0, 20) . "....
.COMMSID   Helper" . substr($hash->{helper}{".COMMSID"}, 0, 20) . "....
.CSRF   Helper" . substr($hash->{helper}{".CSRF"}, 0, 3) . "....
.DIRECTID   Helper" . substr($hash->{helper}{".DIRECTID"}, 0, 20) . "....
RUNLOGIN   Helper" . $hash->{helper}{RUNLOGIN} . "
RUNNING_REQUEST   Helper" . $hash->{helper}{RUNNING_REQUEST} . "
LOGINERROR   Helper" . $hash->{helper}{".LOGINERROR"} . "
  
"; $return .= ""; return $return; } if ($ConnectState ne "connected") { return "$name is not connected. Aborting..."; } if ($command eq "settings") { echodevice_GetSettings($hash); return "OK" if($hash->{model} ne "ACCOUNT"); } elsif($command eq "actions") { echodevice_SendCommand($hash,"getcards",""); } elsif($command eq "devices") { echodevice_SendCommand($hash,"devices",""); } elsif($command eq "conversations") { echodevice_SendCommand($hash,"conversations",""); } elsif($command eq "tunein") { echodevice_SendCommand($hash,"searchtunein",$parameter); } elsif($command eq "tracks") { echodevice_SendCommand($hash,"searchtracks",$parameter); } elsif($command eq "primeplayeigene_albums") { echodevice_SendCommand($hash,"primeplayeigene_Albums",$parameter); } elsif($command eq "primeplayeigene_tracks") { echodevice_SendCommand($hash,"primeplayeigene_Tracks",$parameter); } elsif($command eq "primeplayeigene_artists") { echodevice_SendCommand($hash,"primeplayeigene_Artists",$parameter); } elsif($command eq "primeplayeigeneplaylist") { echodevice_SendCommand($hash,"getprimeplayeigeneplaylist",""); } elsif($command eq "address") { echodevice_SendCommand($hash,"address",$parameter); } elsif($command eq "customer-history-records") { echodevice_SendCommand($hash,"customer-history-records",$parameter); } return undef; } sub echodevice_Set($@) { my ($hash, @a) = @_; shift @a; my $command = shift @a; my $parameter = join(' ',@a); my $name = $hash->{NAME}; my $ShoppingListe = ReadingsVal($name, "list_SHOPPING_ITEM", ""); my $TaskListe = ReadingsVal($name, "list_TASK", ""); my $tracks = AttrVal($name, 'tracks', AttrVal(AttrVal($name, 'IODev', $name), 'tracks', undef)); my $usage = 'Unknown argument $command, choose one of '; return $usage if ($hash->{model} eq 'unbekannt'); if($hash->{model} eq "ACCOUNT") { $usage .= 'autocreate_devices:noArg item_shopping_add item_task_add '; $usage .= 'AWS_Access_Key AWS_Secret_Key TTS_IPAddress TTS_Filename TTS_TuneIn POM_TuneIn POM_IPAddress POM_Filename AWS_OutputFormat:mp3,ogg_vorbis,pcm textmessage ';# if(defined($hash->{helper}{".COMMSID"})); $usage .= 'config_address_from config_address_to config_address_between mobilmessage '; $usage .= 'login:noArg loginwithcaptcha login2FACode ' if($hash->{LOGINMODE} eq "NORMAL"); $usage .= 'NPM_install:noArg NPM_login:new,refresh ' if($hash->{LOGINMODE} eq "NPM"); # Einkaufsliste my $ShoppingListe = ReadingsVal($name, "list_SHOPPING_ITEM", ""); my $TaskListe = ReadingsVal($name, "list_TASK", ""); $ShoppingListe =~ s/ / /g; $TaskListe =~ s/ / /g; $usage .= ' item_shopping_delete:'.$ShoppingListe; $usage .= ' item_task_delete:'.$TaskListe; } elsif ($hash->{model} eq "Echo Multiroom" || $hash->{model} eq "Sonos Display" || $hash->{model} eq "Echo Stereopaar") { $usage .= 'volume:slider,0,1,100 play:noArg pause:noArg next:noArg previous:noArg forward:noArg rewind:noArg shuffle:on,off repeat:on,off '; $usage .= 'tunein primeplaylist primeplaysender primeplayeigene primeplayeigeneplaylist tts tts_translate:textField-long playownmusic:textField-long saveownplaylist:textField-long '; if(defined($tracks)) { $tracks =~ s/ /_/g; $tracks =~ s/:/,/g; $usage .= 'track:'.$tracks.' '; } else { $usage .= 'track '; } # startownplaylist $usage .= echodevice_GetOwnPlaylist($hash); } else { $usage .= 'volume:slider,0,1,100 play:noArg pause:noArg next:noArg previous:noArg forward:noArg rewind:noArg shuffle:on,off repeat:on,off dnd:on,off volume_alarm:slider,0,1,100 '; $usage .= 'info:Beliebig_Auf_Wiedersehen,Beliebig_Bestaetigung,Beliebig_Geburtstag,Beliebig_Guten_Morgen,Beliebig_Gute_Nacht,Beliebig_Ich_Bin_Zuhause,Beliebig_Kompliment,Erzaehle_Geschichte,Erzaehle_Was_Neues,Erzaehle_Witz,Kalender_Heute,Kalender_Morgen,Kalender_Naechstes_Ereignis,Nachrichten,Singe_Song,Verkehr,Wetter sounds:glocken,kirchenglocke,summer,tuerklingel_1,tuerklingel_2,tuerklingel_3,jubelnde_menschenmenge,publikumsapplaus,flugzeug,katastrophenalarm,motoren_an,schilde_hoch,sirenen,zappen,boing_1,boing_2,kamera,lufthupe,quitschende_tuer,tickende_uhr,trompete,hahn,hundegebell,katzenmauzen,loewengebruell,wolfsgeheul,gruselig_quitschende_tuer,weihnachtsglocken tunein primeplaylist primeplaysender primeplayeigene primeplayeigeneplaylist alarm_normal alarm_repeat reminder_normal reminder_repeat speak speak_ssml tts tts_translate:textField-long playownmusic:textField-long saveownplaylist:textField-long '; $usage .= 'textcommand '; $usage .= 'homescreen ' if ($hash->{model} eq "Echo Show 5" || $hash->{model} eq "Echo Show 8" || $hash->{model} eq "Echo Show" || $hash->{model} eq "Echo Show Gen2" || $hash->{model} eq "Echo Show Gen3"); # startownplaylist $usage .= echodevice_GetOwnPlaylist($hash); if(defined($tracks)) { $tracks =~ s/ /_/g; $tracks =~ s/:/,/g; $usage .= 'track:'.$tracks.' '; } else { $usage .= 'track '; } $usage .= 'bluetooth_connect:'.$hash->{helper}{bluetooth}.' bluetooth_disconnect:'.$hash->{helper}{bluetooth}.' ' if(defined($hash->{helper}{bluetooth})); # Routinen auslesen my $BehaviorName ; my @Behaviors = (); foreach my $BehaviorID (sort keys %{$hash->{IODev}->{helper}{"getbehavior"}}) { #Log3 $name, 3, "[DEBUG] BehaviorID = " . $BehaviorID; $BehaviorName = $hash->{IODev}->{helper}{"getbehavior"}{$BehaviorID}{triggers}[0]{payload}{utterance}; $BehaviorName =~ s/ /_/g; #Log3 $name, 3, "[DEBUG] Name = " . $BehaviorName; push @Behaviors, $BehaviorName . "@" . $BehaviorID; } # Reminder/Alarm auslesen my @ncstrings = (); my @Alarms = (); my $NotifiResult ; my $NotifiType ; foreach my $NotifiID (sort keys %{$hash->{IODev}->{helper}{"notifications"}{$hash->{helper}{".SERIAL"}}}) { if ($hash->{IODev}->{helper}{"notifications"}{$hash->{helper}{".SERIAL"}}{$NotifiID} ne "") { $NotifiResult = $hash->{IODev}->{helper}{"notifications"}{$hash->{helper}{".SERIAL"}}{$NotifiID} ; $NotifiType = (split("_",$NotifiResult))[0]; $NotifiResult =~s/ /_/g; $NotifiResult =~s/,/_/g; $NotifiResult =~s/@/_/g; $NotifiResult .= "@" . $NotifiID ; if (lc($NotifiType) eq "alarm" || lc($NotifiType) eq "musicalarm") { push @Alarms, $NotifiResult; } push @ncstrings, $NotifiResult; } } if (@Alarms) { @Alarms = sort @Alarms; $usage .= 'alarm_off:' . join(",", @Alarms). ' '; $usage .= 'alarm_on:' . join(",", @Alarms). ' '; } if (@ncstrings) { @ncstrings = sort @ncstrings; $usage .= 'notifications_delete:' . join(",", @ncstrings). ' '; } if (@Behaviors) { @Behaviors = sort @Behaviors; $usage .= 'routine_play:' . join(",", @Behaviors). ' '; } } return $usage if $command eq '?'; #return echodevice_Login($hash) if($command eq "login"); return echodevice_SendLoginCommand($hash,"cookielogin1","") if($command eq "login"); if($command eq "NPM_install"){ return echodevice_NPMInstall($hash); } if($command eq "NPM_login"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); return echodevice_NPMLoginNew($hash) if ($a[0] eq "new") ; return echodevice_NPMLoginRefresh($hash) if ($a[0] eq "refresh"); } if($command eq "loginwithcaptcha"){ return echodevice_getHelpText("HTML Result file does exits. Pleas activate the attribut browser_save_data") if ((!-e $FW_dir . "/echodevice/results/". $name . "_cookielogin4.html")); return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); $hash->{helper}{CAPTCHA} = $a[0]; echodevice_SendLoginCommand($hash,"cookielogin4captcha",""); return; } if($command eq "login2FACode"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); $hash->{helper}{TWOFA} = $a[0]; echodevice_SendLoginCommand($hash,"cookielogin4",""); return; } if(IsDisabled($name)) { $hash->{STATE} = "disabled"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "disabled", 1); readingsEndUpdate($hash,1); return "$name is disabled. Aborting..."; } my $ConnectState = ""; if($hash->{model} eq "ACCOUNT") {$ConnectState = $hash->{STATE}} else {$ConnectState = $hash->{IODev}->{STATE}} if ($ConnectState ne "connected" && $command ne "login" && $command ne "login2FACode") { return "$name is not connected. Aborting..."; } # Allgemeine Einstellungen if($command eq "bluetooth_connect"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my @parameters = split("/",$a[0]); $parameters[0] =~ s/-/:/g; my $json = encode_json( { bluetoothDeviceAddress => $parameters[0] } ); echodevice_SendCommand($hash,"bluetooth_connect",$json); } elsif($command eq "autocreate_devices") { readingsSingleUpdate ( $hash, "autocreate_devices", "running", 0 ); echodevice_SendCommand($hash,"autocreate_devices",""); } elsif($command eq "bluetooth_disconnect"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my @parameters = split("/",$a[0]); $parameters[0] =~ s/-/:/g; my $json = encode_json( { bluetoothDeviceAddress => $parameters[0] } ); echodevice_SendCommand($hash,"bluetooth_disconnect",$json); } elsif($command eq "dnd"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my $json = encode_json( { deviceSerialNumber => $hash->{helper}{".SERIAL"}, deviceType => $hash->{helper}{DEVICETYPE}, enabled => ($a[0] eq "on")?"true":"false" } ); $json =~s/\"true\"/true/g; $json =~s/\"false\"/false/g; echodevice_SendCommand($hash,"dnd",$json); } elsif($command eq "volume") { return echodevice_getHelpText("no arg") if ( !defined( $a[0] ) ); # Voluemeangabe prüfen if ($a[0] >= 0 && $a[0] <= 100 ) { readingsBeginUpdate($hash); readingsBulkUpdate($hash, "volume", $a[0], 1); readingsEndUpdate($hash,1); echodevice_SendCommand($hash,"volume",$a[0]); } else { return echodevice_getHelpText("Argument $a[0] does not seem to be a valid integer between 0 and 100"); } } elsif($command eq "volume_alarm") { return echodevice_getHelpText("no arg") if ( !defined( $a[0] ) ); # Voluemeangabe prüfen if ($a[0] >= 0 && $a[0] <= 100 ) { my $json = encode_json( { deviceSerialNumber => $hash->{helper}{".SERIAL"}, deviceType => $hash->{helper}{DEVICETYPE}, softwareVersion => $hash->{helper}{VERSION}, volumeLevel => int($a[0]) } ); readingsBeginUpdate($hash); readingsBulkUpdate($hash, "volume_alarm", $a[0], 1); readingsEndUpdate($hash,1); echodevice_SendCommand($hash,"volume_alarm",$json); } else { return echodevice_getHelpText("Argument $a[0] does not seem to be a valid integer between 0 and 100"); } } elsif($command eq "config_address_from" || $command eq "config_address_to" || $command eq "config_address_between") { echodevice_SendCommand($hash,$command,$parameter); } # Listen elsif($command eq "item_task_delete" ) { return echodevice_getHelpText("no arg") if ( !defined($parameter) ); echodevice_SendCommand($hash,"item_task_delete",$parameter) } elsif($command eq "item_shopping_delete" ) { return echodevice_getHelpText("no arg") if ( !defined($parameter) ); echodevice_SendCommand($hash,"item_shopping_delete",$parameter) } elsif($command eq "item_task_add" ) { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my $json = JSON->new->utf8(1)->encode( { 'createdDateTime' => int(time), 'listId' => $hash->{helper}{TO_DO_LIST_ID}, 'completed' => "false", 'value' => decode_utf8($parameter) } ); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; $parameter =~ s/ /_/g; readingsBeginUpdate($hash); if ($TaskListe eq "") {readingsBulkUpdate($hash, "list_TASK", $parameter , 1);} else {readingsBulkUpdate($hash, "list_TASK", $parameter . "," . $TaskListe , 1); } readingsEndUpdate($hash,1); echodevice_SendCommand($hash,"item_task_add",$json) } elsif($command eq "item_shopping_add" ) { return echodevice_getHelpText("no arg") if ( !defined($parameter) ); my $json = JSON->new->utf8(1)->encode( { 'createdDateTime' => int(time), 'listId' => $hash->{helper}{SHOPPING_LIST_ID}, 'completed' => "false", 'value' => decode_utf8($parameter) } ); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; $parameter =~ s/ /_/g; readingsBeginUpdate($hash); if ($ShoppingListe eq "") {readingsBulkUpdate($hash, "list_SHOPPING_ITEM", $parameter , 1);} else {readingsBulkUpdate($hash, "list_SHOPPING_ITEM", $parameter . "," . $ShoppingListe , 1); } readingsEndUpdate($hash,1); echodevice_SendCommand($hash,"item_shopping_add",$json) } # Erinnerungen / Timer / Wecker elsif($command eq "reminder_normal" || $command eq "alarm_normal") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my $reminder_delay = AttrVal($name, "reminder_delay", 10); my $ReminderText ; my $ReminderDate ; my $Type; $Type = "Reminder" if ($command eq "reminder_normal"); $Type = "Alarm" if ($command eq "alarm_normal"); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, lc($Type) . "_normal", join(' ',@a), 1); readingsEndUpdate($hash,1); my ($Tsec, $Tmin, $Thour, $Tmday, $Tmon, $Tyear, $Twday, $Tyday, $Tisdst) = localtime(); # Prüfen es sich um ein Datum handelt if (index($a[0], "-") != -1){ $ReminderDate = str2time($a[0] . " " . $a[1]); splice @a, 0, 1; splice @a, 0, 1; $ReminderText = join(' ',@a); } elsif (index($a[0], ":") != -1){ $ReminderDate = str2time(sprintf("%04d",$Tyear+1900)."-".sprintf("%02d",$Tmon+1)."-".sprintf("%02d",$Tmday)." ". $a[0]); splice @a, 0, 1; $ReminderText = join(' ',@a); } else { $ReminderText = $parameter; $ReminderDate = time + $reminder_delay; } my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($ReminderDate); my $json = encode_json( { alarmTime => $ReminderDate*1000, createdDate => int(time)*1000, deviceSerialNumber => $hash->{helper}{".SERIAL"}, deviceType => $hash->{helper}{DEVICETYPE}, id => "create".$Type, isRecurring => "false", isSaveInFlight => "true", originalDate => sprintf("%04d",$year+1900)."-".sprintf("%02d",$mon+1)."-".sprintf("%02d",$mday), originalTime => sprintf("%02d",$hour).":".sprintf("%02d",$min).":".sprintf("%02d",$sec).".000", reminderLabel => decode_utf8($ReminderText), status => "ON", type => $Type}); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; echodevice_SendCommand($hash,"reminderitem",$json); } elsif($command eq "reminder_repeat" || $command eq "alarm_repeat") { return echodevice_getHelpText("There are some arguments missing. [Zeitangabe] [Wiederholumgsmode] nachrichtentext ") if ( !defined($a[0]) ); my $Type; $Type = "Reminder" if ($command eq "reminder_repeat"); $Type = "Alarm" if ($command eq "alarm_repeat"); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, lc($Type)."_repeat", join(' ',@a), 1); readingsEndUpdate($hash,1); # Vorbereitungen my @parameters = split(":",$a[0]); my $ReminderRecc = $a[1]; splice @a, 0, 1; splice @a, 0, 1; my $ReminderText = join(' ',@a); my $recurringPattern = ""; if ($ReminderRecc eq "1") {$recurringPattern = "P1D";} elsif ($ReminderRecc eq "2") {$recurringPattern = "XXXX-WD";} elsif ($ReminderRecc eq "3") {$recurringPattern = "XXXX-WE";} elsif ($ReminderRecc eq "4") {$recurringPattern = "XXXX-WXX-1";} elsif ($ReminderRecc eq "5") {$recurringPattern = "XXXX-WXX-2";} elsif ($ReminderRecc eq "6") {$recurringPattern = "XXXX-WXX-3";} elsif ($ReminderRecc eq "7") {$recurringPattern = "XXXX-WXX-4";} elsif ($ReminderRecc eq "8") {$recurringPattern = "XXXX-WXX-5";} elsif ($ReminderRecc eq "9") {$recurringPattern = "XXXX-WXX-6";} elsif ($ReminderRecc eq "10") {$recurringPattern = "XXXX-WXX-7";} else {$recurringPattern = "P1D";} my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); my $json = encode_json( { alarmTime => int(time)*1000, createdDate => int(time)*1000 , deviceSerialNumber => $hash->{helper}{".SERIAL"}, deviceType => $hash->{helper}{DEVICETYPE}, id => "create".$Type, isRecurring => "true", isSaveInFlight => "true", recurringPattern => $recurringPattern, originalDate => sprintf("%04d",$year+1900)."-".sprintf("%02d",$mon+1)."-".sprintf("%02d",$mday), originalTime => sprintf("%02d",$parameters[0]).":".sprintf("%02d",$parameters[1]).":00.000", status => "ON", reminderLabel => decode_utf8($ReminderText), type => $Type}); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; Log3( $name, 5, "[$name] set ".lc($Type)."_repeat $parameters[0]:$parameters[1] $ReminderRecc Message = $ReminderText"); echodevice_SendCommand($hash,"reminderitem",$json); } elsif($command eq "notifications_delete"){ return echodevice_getHelpText("no arg") if ( !defined($a[0])); my @parameters = split("@",$parameter); # Reminder aus dem hash entfernen $hash->{IODev}->{helper}{"notifications"}{$hash->{helper}{".SERIAL"}}{$parameters[1]} = ""; echodevice_SendCommand($hash,"notifications_delete",$parameters[1]); } elsif($command eq "alarm_off"){ return echodevice_getHelpText("no arg") if ( !defined($a[0])); echodevice_SendCommand($hash,"alarm_off",$a[0]); } elsif($command eq "alarm_on"){ return echodevice_getHelpText("no arg") if ( !defined($a[0])); echodevice_SendCommand($hash,"alarm_on",$parameter); } # Routinen elsif($command eq "routine_play"){ return echodevice_getHelpText("no arg") if ( !defined($a[0])); echodevice_SendCommand($hash,"routine_play",$parameter); } # Nachrichten elsif($command eq "textmessage"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); return echodevice_getHelpText("There are some arguments missing. [conversationId] nachrichtentext ") if ( !defined($a[1]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "mobilmessage"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "message_delete"){ #return "No argument given" if ( !defined($a[0])); #my @parameters = split("@",$parameter); # Reminder aus dem hash entfernen #$hash->{IODev}->{helper}{"notifications"}{$hash->{helper}{".SERIAL"}}{$parameters[1]} = ""; echodevice_SendCommand($hash,"message_delete",""); } # Medien elsif($command eq "tunein" || $command eq "ttstunein"){ my $tuneinID ; if ( !defined($a[0]) && AttrVal($name,"tunein_default","none") eq "none" ) { return echodevice_getHelpText("No argument given. You can set attribut tunein_default!"); } elsif (!defined($a[0])) {$tuneinID = AttrVal($name,"tunein_default","none");} else {$tuneinID = $a[0];} # Player aktualisieren readingsBeginUpdate($hash); readingsBulkUpdate($hash, "tunein", $tuneinID, 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); echodevice_SendCommand($hash,"tunein",$tuneinID); InternalTimer( gettimeofday() + 10, "echodevice_GetSettings", $hash, 0); } elsif($command eq "primeplaylist"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, "primeplaylist", $a[0], 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); my $json = encode_json( { asin => $a[0] } ); echodevice_SendCommand($hash,$command,$json); InternalTimer( gettimeofday() + 5, "echodevice_GetSettings", $hash, 0); } elsif($command eq "primeplayeigeneplaylist"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, "primeplayeigeneplaylist", $a[0], 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); my $json = encode_json( { playlistId => $a[0] } ); echodevice_SendCommand($hash,$command,$json); InternalTimer( gettimeofday() + 3, "echodevice_GetSettings", $hash, 0); } elsif($command eq "primeplaysender"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, "primeplaysender", $a[0], 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); my $json = encode_json( { seed => '{"type":"KEY","seedId":"' . $a[0] .'"}' ,stationName => $a[0],seedType => "KEY" } ); echodevice_SendCommand($hash,$command,$json); InternalTimer( gettimeofday() + 3, "echodevice_GetSettings", $hash, 0); } elsif($command eq "primeplayeigene"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, "primeplayeigene", $a[0], 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); my @PlayItem = split (/@/s, $parameter); my $json = encode_json( { albumArtistName => $PlayItem[0],albumName => $PlayItem[1]} ); echodevice_SendCommand($hash,$command,$json); InternalTimer( gettimeofday() + 3, "echodevice_GetSettings", $hash, 0); } elsif($command eq "track"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); # Reading festhalten readingsBeginUpdate($hash); readingsBulkUpdate($hash, "track", $a[0], 1); readingsBulkUpdate($hash, "playStatus", "playing", 1); readingsEndUpdate($hash,1); my $json = encode_json( { trackId => $a[0], playQueuePrime => "false"} ); $json =~s/\"true\"/true/g; $json =~s/\"false\"/false/g; echodevice_SendCommand($hash,$command,$json); InternalTimer( gettimeofday() + 3, "echodevice_GetSettings", $hash, 0); } elsif($command eq "AWS_Access_Key" || $command eq "AWS_Secret_Key" ) { readingsBeginUpdate($hash); readingsBulkUpdate($hash, lc(".$command"), echodevice_encrypt($parameter), 1); readingsEndUpdate($hash,1); } elsif($command eq "TTS_Filename" || $command eq "POM_Filename" || $command eq "TTS_TuneIn" || $command eq "POM_TuneIn" || $command eq "AWS_OutputFormat" || $command eq "TTS_IPAddress" || $command eq "POM_IPAddress" ) { readingsBeginUpdate($hash); readingsBulkUpdate($hash, lc($command), $parameter, 1); readingsEndUpdate($hash,1); } elsif($command eq "tts") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); return echodevice_getHelpText("TTS can not play. The ECHO device $name is playing other media.") if (AttrVal($hash->{IODev}->{NAME},"TTS_IgnorPlay",1) == 0 && ReadingsVal( $name, "playStatus", "stopped") eq "playing" && ReadingsVal($name , "currentTuneInID", "-") eq "-"); return echodevice_getHelpText("TTS can not play. The ECHO device $name is playing other media.") if (AttrVal($name,"TTS_IgnorPlay",1) == 0 && ReadingsVal( $name, "playStatus", "stopped") eq "playing" && ReadingsVal($name , "currentTuneInID", "-") eq "-"); return echodevice_getHelpText("TTS can not play. Please define TTS_IPAdrees at the ECHO ACCOUNT DEVICE " . $hash->{IODev}->{NAME}) if (ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_IPAddress"), "none") eq "none"); my $TTS_Voice = AttrVal($name,"TTS_Voice","German_Female_Google"); if ($TTS_Voice eq "German_Female_Google") { echodevice_Google($hash,$parameter,$command); } else { echodevice_Amazon($hash,$parameter,$command); } } elsif($command eq "tts_translate") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); return echodevice_getHelpText("TTS can not play. The ECHO device $name is playing other media.") if (AttrVal($hash->{IODev}->{NAME},"TTS_IgnorPlay",1) == 0 && ReadingsVal( $name, "playStatus", "stopped") eq "playing" && ReadingsVal($name , "currentTuneInID", "-") eq "-"); return echodevice_getHelpText("TTS can not play. The ECHO device $name is playing other media.") if (AttrVal($name,"TTS_IgnorPlay",1) == 0 && ReadingsVal( $name, "playStatus", "stopped") eq "playing" && ReadingsVal($name , "currentTuneInID", "-") eq "-"); return echodevice_getHelpText("TTS can not play. Please define TTS_IPAdrees at the ECHO ACCOUNT DEVICE " . $hash->{IODev}->{NAME}) if (ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_IPAddress"), "none") eq "none"); my $TTS_Voice = AttrVal($name,"TTS_Voice","German_Female_Google"); my $TTS_Translate_From = AttrVal($name,"TTS_Translate_From","german"); my $TTS_CodeOutput = "en"; my $TTS_CodeInput = "de"; # Output if (index(lc($TTS_Voice), 'english') >= 0) { $TTS_CodeOutput = "en"; } elsif (index(lc($TTS_Voice), 'french') >= 0) { $TTS_CodeOutput = "fr"; } elsif (index(lc($TTS_Voice), 'portuguese') >= 0) { $TTS_CodeOutput = "pt"; } elsif (index(lc($TTS_Voice), 'spanish') >= 0) { $TTS_CodeOutput = "es"; } elsif (index(lc($TTS_Voice), 'dutch') >= 0) { $TTS_CodeOutput = "nl"; } elsif (index(lc($TTS_Voice), 'italian') >= 0) { $TTS_CodeOutput = "it"; } elsif (index(lc($TTS_Voice), 'japanese') >= 0) { $TTS_CodeOutput = "ja"; } elsif (index(lc($TTS_Voice), 'korean') >= 0) { $TTS_CodeOutput = "ko"; } elsif (index(lc($TTS_Voice), 'russian') >= 0) { $TTS_CodeOutput = "ru"; } elsif (index(lc($TTS_Voice), 'turkish') >= 0) { $TTS_CodeOutput = "tr"; } elsif (index(lc($TTS_Voice), 'german') >= 0) { $TTS_CodeOutput = "de"; } else { return "TTS can not play. Please define other TTS_Voice. Language not supported. Supported languages are:dutch,english,french,german,italian,japanese,korean,portuguese,russian,spanish and turkish"; } # Input if ($TTS_Translate_From eq "dutch") {$TTS_CodeInput = "nl"} elsif ($TTS_Translate_From eq "english") {$TTS_CodeInput = "en"} elsif ($TTS_Translate_From eq "french") {$TTS_CodeInput = "fr"} elsif ($TTS_Translate_From eq "german") {$TTS_CodeInput = "de"} elsif ($TTS_Translate_From eq "italian") {$TTS_CodeInput = "it"} elsif ($TTS_Translate_From eq "japanese") {$TTS_CodeInput = "ja"} elsif ($TTS_Translate_From eq "portuguese") {$TTS_CodeInput = "pl"} elsif ($TTS_Translate_From eq "russian") {$TTS_CodeInput = "ru"} elsif ($TTS_Translate_From eq "spanish") {$TTS_CodeInput = "es"} elsif ($TTS_Translate_From eq "turkish") {$TTS_CodeInput = "tr"} if ($TTS_CodeInput eq $TTS_CodeOutput) { return echodevice_getHelpText("TTS can not play. Please define other TTS_Voice or TTS_Translate_From. Input and output languages are the same!"); } my $json = "{ dirCode:'" . $TTS_CodeInput . "-" . $TTS_CodeOutput . "', template:'General', text:'" .urlEncode($parameter) . "', lang:'de', limit:'3000',useAutoDetect:false, key:'123', ts:'MainSite',tid:'', IsMobile:false}"; echodevice_SendCommand($hash,$command,$json); } elsif($command eq "playownmusic") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); return echodevice_getHelpText("POM can not play. Please define POM_IPAdrees at the ECHO ACCOUNT DEVICE ") . $hash->{IODev}->{NAME} if (ReadingsVal($hash->{IODev}->{NAME} , lc("POM_IPAddress"), "none") eq "none"); echodevice_PlayOwnMP3($hash,$parameter); } elsif($command eq "saveownplaylist") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SaveOwnPlaylist($hash,$parameter); } elsif($command eq "playownplaylist") { return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); my $WEBAddress = ReadingsVal($hash->{IODev}->{NAME} , lc("POM_IPAddress"), "none"); return echodevice_getHelpText("POM can not play. Please define POM_IPAdrees at the ECHO ACCOUNT DEVICE ") . $hash->{IODev}->{NAME} if ($WEBAddress eq "none"); echodevice_PlayOwnMP3($hash,"http://" . $WEBAddress . "/playlists/" . $parameter); } elsif($command eq "deleteownplaylist") { return echodevice_getHelpText("no arg")if ( !defined($a[0]) ); # Playliste löschen if ((-e $FW_dir . "/echodevice/playlists/". $parameter)) {unlink $FW_dir . "/echodevice/playlists/".$parameter} } elsif($command eq "speak"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "speak_ssml"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "sounds"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "info"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "homescreen"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } elsif($command eq "textcommand"){ return echodevice_getHelpText("no arg") if ( !defined($a[0]) ); echodevice_SendCommand($hash,$command,join(' ',@a)); } else { echodevice_SendMessage($hash,$command,$parameter); # Player aktualisieren InternalTimer( gettimeofday() + 2, "echodevice_GetSettings", $hash, 0); } return ; } ######################### sub echodevice_SendMessage($$$) { my ($hash,$command,$value) = @_; my $name = $hash->{NAME}; my $json = encode_json( {} ); if($command eq "volume") { $json = encode_json( { type => 'VolumeLevelCommand', volumeLevel => 0+$value, contentFocusClientId => undef } ); } elsif ($command eq "play") { $json = encode_json( { type => 'PlayCommand', contentFocusClientId => undef } ); } elsif ($command eq "pause") { $json = encode_json( { type => 'PauseCommand', contentFocusClientId => undef } ); } elsif ($command eq "next") { $json = encode_json( { type => 'NextCommand', contentFocusClientId => undef } ); } elsif ($command eq "previous") { $json = encode_json( { type => 'PreviousCommand', contentFocusClientId => undef } ); } elsif ($command eq "forward") { $json = encode_json( { type => 'ForwardCommand', contentFocusClientId => undef } ); } elsif ($command eq "rewind") { $json = encode_json( { type => 'RewindCommand', contentFocusClientId => undef } ); } elsif ($command eq "shuffle") { $json = encode_json( { type => 'ShuffleCommand', shuffle => ($value eq "on"?"true":"false"), contentFocusClientId => undef } ); } elsif ($command eq "repeat") { $json = encode_json( { type => 'RepeatCommand', repeat => ($value eq "on"?"true":"false"), contentFocusClientId => undef } ); } else { Log3 ($name, 4, "[$name] [echodevice_SendMessage] Unknown command $command $value"); return ; } Log3 ($name, 4, "[$name] [echodevice_SendMessage] command $command $value"); $json =~s/\"true\"/true/g; $json =~s/\"false\"/false/g; echodevice_SendCommand($hash,"command",$json); } sub echodevice_SendCommand($$$) { my ( $hash, $type, $SendData ) = @_; my $name = $hash->{NAME}; my $SendUrl; my $SendDataL; Log3 $name, 4, "[$name] [echodevice_SendCommand] [$type] START"; if($hash->{model} eq "ACCOUNT") { return undef if(!defined($hash->{helper}{SERVER})); $SendUrl = "https://".$hash->{helper}{SERVER}; } else { return undef if(!defined($hash->{IODev}->{helper}{SERVER})); $SendUrl = "https://".$hash->{IODev}->{helper}{SERVER}; } my $SendParam ; my $SendMetode = "GET" ; # Ohne JSON if ($type eq "bluetoothstate") { $SendUrl .= "/api/bluetooth?cached=true&_=".int(time); } elsif ($type eq "notifications") { $SendUrl .= "/api/notifications?cached=true&_=".int(time); } elsif ($type eq "getdnd") { $SendUrl .= "/api/dnd/device-status-list?_=".int(time); } elsif ($type eq "getbehavior") { $SendUrl .= "/api/behaviors/v2/automations?limit=100"; } elsif ($type eq "getdevicesettings") { $SendUrl .= "/api/device-preferences"; } elsif ($type eq "getisonline") { $SendUrl .= "/api/devices-v2/device?cached=true&_=".int(time); } elsif ($type eq "wakeword") { $SendUrl .= "/api/wake-word?_=".int(time); } elsif ($type eq "alarmvolume") { $SendUrl .= "/api/device-notification-state?_=".int(time); } elsif ($type eq "activities") { if (int(AttrVal($name,"intervalvoice",999999)) != 999999) { $SendUrl .= "/api/activities?startTime=&size=10&offset=1&_=".int(time); } else { $SendUrl .= "/api/activities?startTime=&size=50&offset=1&_=".int(time); } } elsif ($type eq "player") { $SendUrl .= "/api/np/player?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&screenWidth=1392&_=".int(time); } elsif ($type eq "media") { $SendUrl .= "/api/media/state?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&screenWidth=1392&_=".int(time); } elsif ($type eq "reminderitem") { $SendUrl .= "/api/notifications/createReminder"; $SendMetode = "PUT"; } elsif ($type eq "command") { $SendUrl .= "/api/np/command?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}; $SendMetode = "POST"; } elsif ($type eq "tunein" || $type eq "ttstunein" ) { $SendUrl .= "/api/entertainment/v1/player/queue?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}; $SendData = encode_json( { contentToken => 'music:'. encode_base64(encode_base64('["music/tuneIn/stationId","'.$SendData.'"]|{"previousPageId":"TuneIn_SEARCH"}', ''), '') } ); $SendDataL = $SendData ; $SendMetode = "PUT"; } elsif ($type eq "getnotifications" ) { $SendUrl .= "/api/notifications"; $SendData = ""; } elsif ($type eq "notifications_delete" ) { $SendUrl .= "/api/notifications/".$hash->{helper}{DEVICETYPE}."-".$hash->{helper}{".SERIAL"}."-".$SendData; $SendMetode = "DELETE"; $SendData = ""; $SendDataL = $SendData ; } elsif ($type eq "routine_play") { $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; my @parameters = split("@",$SendData); my $sequenceJson = encode_json($hash->{IODev}->{helper}{"getbehavior"}{$parameters[1]}{sequence}); $sequenceJson =~ s/"/\\"/g; #Log3 $name, 3, "[$name] [DEBUG] JSONORG=" . $sequenceJsonTest; #Log3 $name, 3, "[$name] [DEBUG] JSONNEW=" . $sequenceJson; $SendData = '{"behaviorId":"'.$parameters[1].'","sequenceJson":"'.$sequenceJson.'","status":"'.$hash->{IODev}->{helper}{"getbehavior"}{$parameters[1]}{status}.'"}'; my $AlexaType = $hash->{helper}{DEVICETYPE}; my $AlexaDSN = $hash->{helper}{".SERIAL"}; $SendData =~ s/ALEXA_CURRENT_DEVICE_TYPE/$AlexaType/g; $SendData =~ s/ALEXA_CURRENT_DSN/$AlexaDSN/g; $SendDataL = $SendData; } elsif ($type eq "mobilmessage") { $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; my $Messagetext = $SendData; $Messagetext =~ s/"/'/g; $SendData = '{"behaviorId":"PREVIEW","sequenceJson":"{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"operationPayload\":{\"notificationMessage\":\"' . $Messagetext .'\",\"alexaUrl\":\"#v2/behaviors\",\"customerId\":\"' . $hash->{helper}{".CUSTOMER"} .'\",\"title\":\"FHEM\"},\"type\":\"Alexa.Notifications.SendMobilePush\",\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"skillId\":\"amzn1.ask.1p.routines.messaging\",\"name\":null},\"sequenceId\":\"amzn1.alexa.sequence.8f5aa289-c6d4-4a6f-a1b9-5b182e23be1e\"}","status":"ENABLED"}'; $SendDataL = $SendData; } elsif ($type eq "alarm_off" || $type eq "alarm_on" ) { my @parameters = split("@",$SendData); my @AlarmType = split("_",$SendData); my @StateType = split("_",$type); my $SetTo = uc($StateType[1]); $SendUrl = "https://alexa.amazon.de/api/notifications/".$hash->{helper}{DEVICETYPE}."-".$hash->{helper}{".SERIAL"};#."-12".$parameters[1]."12"; $SendMetode = "PUT"; $SendData = '{"id":"'.$hash->{helper}{DEVICETYPE}.'-'.$hash->{helper}{".SERIAL"}.'-'.$parameters[1].'","createdDate":0,"deferredAtTime":null,"deviceSerialNumber":"'.$hash->{helper}{".SERIAL"}.'","deviceType":"'.$hash->{helper}{DEVICETYPE}.'","geoLocationTriggerData":null,"musicAlarmId":'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"musicAlarmId"}.',"musicEntity":'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"musicEntity"}.',"notificationIndex":"'.$parameters[1].'","originalDate":"'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"originalDate"}.'","originalTime":"'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"originalTime"}.'","personProfile":null,"provider":'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"provider"}.',"recurringPattern":'; if ($hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"recurringPattern"} eq "null" || lc($AlarmType[0]) eq "musicalarm") { $SendData .= "null"; } else { $SendData .= '"'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"recurringPattern"}.'","sound":{"cid":"c13489","attributes":{"displayName":"Simple Alarm","folder":null,"id":"system_alerts_melodic_01","providerId":"ECHO","sampleUrl":"https://s3.amazonaws.com/deeappservice.prod.notificationtones/system_alerts_melodic_01.mp3"},"_changing":false,"_previousAttributes":{},"changed":{},"id":"system_alerts_melodic_01","_pending":false,"hasSynced":false,"_isExpired":false,"_listeners":{"l13490":{"displayName":"Simple Alarm","folder":null,"id":"system_alerts_melodic_01","providerId":"ECHO","sampleUrl":"https://s3.amazonaws.com/deeappservice.prod.notificationtones/system_alerts_melodic_01.mp3"}},"_listenerId":"l13490","_events":{"sync":[{"context":{"displayName":"Simple Alarm","folder":null,"id":"system_alerts_melodic_01","providerId":"ECHO","sampleUrl":"https://s3.amazonaws.com/deeappservice.prod.notificationtones/system_alerts_melodic_01.mp3"},"ctx":{"displayName":"Simple Alarm","folder":null,"id":"system_alerts_melodic_01","providerId":"ECHO","sampleUrl":"https://s3.amazonaws.com/deeappservice.prod.notificationtones/system_alerts_melodic_01.mp3"}}]},"providerId":null,"displayName":null,"sampleUrl":null,"folder":null}'; } $SendData .= ',"remainingTime":'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"remainingTime"}.',"reminderLabel":null,"skillInfo":null,"sound":{"displayName":"Simple Alarm","folder":null,"id":"system_alerts_melodic_01","providerId":"ECHO","sampleUrl":"https://s3.amazonaws.com/deeappservice.prod.notificationtones/system_alerts_melodic_01.mp3"},"status":"'.$SetTo.'","targetPersonProfiles":null,"timeZoneId":null,"timerLabel":null,"triggerTime":0,"type":"'.$AlarmType[0].'","version":"16","alarmTime":'.$hash->{IODev}->{helper}{$AlarmType[0]}{$hash->{helper}{".SERIAL"}}{$parameters[1]}{"alarmTime"}.',"alarmIndex":null,"isSaveInFlight":true}'; } elsif ($type eq "message_delete" ) { $SendUrl .= "/api/device-preferences/G090L90964350E96"; $SendMetode = "PUT"; } elsif ($type eq "track" ) { $SendUrl .= "/api/cloudplayer/queue-and-play?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}; $SendMetode = "POST"; } elsif ($type eq "primeplaylist" ) { $SendUrl .= "/api/prime/prime-playlist-queue-and-play?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}; $SendMetode = "POST"; } elsif ($type eq "primeplayeigeneplaylist" ) { $SendUrl .= "/api/cloudplayer/queue-and-play?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}; $SendMetode = "POST"; } elsif ($type eq "primeplaysender" ) { $SendUrl .= "/api/gotham/queue-and-play?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}; $SendMetode = "POST"; } elsif ($type eq "primeplayeigene" ) { $SendUrl .= "/api/cloudplayer/queue-and-play?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}; $SendMetode = "POST"; } elsif ($type eq "textmessage" ) { my @parameters = split(" ",$SendData); my $conversationid = shift @parameters; my $parameter = join(" ",@parameters); $SendUrl = "https://alexa-comms-mobile-service.amazon.com/users/".$hash->{helper}{".COMMSID"}."/conversations/".$conversationid."/messages"; $SendMetode = "POST"; $SendData = JSON->new->pretty(1)->utf8(1)->encode([{ "type" => "message/text", "payload" => {"text" => decode_utf8($parameter)} }] ); $SendData =~s/\//\\\//; } elsif ($type eq "volume_alarm" ) { $SendUrl .= "/api/device-notification-state/".$hash->{helper}{DEVICETYPE}."/".$hash->{helper}{VERSION}."/".$hash->{helper}{".SERIAL"}; $SendMetode = "PUT"; } elsif ($type eq "dnd" ) { $SendUrl .= "/api/dnd/status"; $SendMetode = "PUT"; } elsif ($type eq "bluetooth_connect" ) { $SendUrl .= "/api/bluetooth/pair-sink/".$hash->{helper}{DEVICETYPE}."/".$hash->{helper}{".SERIAL"}; $SendMetode = "POST"; } elsif ($type eq "bluetooth_disconnect" ) { $SendUrl .= "/api/bluetooth/disconnect-sink/".$hash->{helper}{DEVICETYPE}."/".$hash->{helper}{".SERIAL"}; $SendMetode = "POST"; } elsif ($type eq "namedListsIDs") { $SendUrl .= "/api/namedLists"; } elsif ($type eq "listitems_task" || $type eq "listitems_shopping" ) { $SendUrl .= "/api/todos?size=100&startTime=&endTime=&completed=false&type=".$SendData."&deviceSerialNumber=&deviceType=&_=".int(time); $SendDataL = $SendData ; $SendData = ""; } elsif ($type eq "item_shopping_add" ) { $SendUrl .= "/api/namedLists/" . $hash->{helper}{SHOPPING_LIST_ID} . "/item"; $SendMetode = "POST"; $SendDataL = $SendData; } elsif ($type eq "item_task_add" ) { $SendUrl .= "/api/namedLists/" . $hash->{helper}{TO_DO_LIST_ID} . "/item"; $SendMetode = "POST"; $SendDataL = $SendData; } elsif ($type eq "item_task_delete" ) { my $json = JSON->new->utf8(1)->encode( {'value' => $hash->{helper}{"ITEMS"}{TASK}{TEXT}{$SendData}, 'listId' => $hash->{helper}{TO_DO_LIST_ID}, 'id' => $hash->{helper}{"ITEMS"}{TASK}{ID}{$SendData}}); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; $SendUrl .= "/api/namedLists/" . $hash->{helper}{TO_DO_LIST_ID} . "/item/" . $hash->{helper}{"ITEMS"}{TASK}{ID}{$SendData}; $SendMetode = "DELETE"; $SendData = $json; $SendDataL = $SendData; } elsif ($type eq "item_shopping_delete" ) { my $json = JSON->new->utf8(1)->encode( {'value' => $hash->{helper}{"ITEMS"}{SHOPPING_ITEM}{TEXT}{$SendData}, 'listId' => $hash->{helper}{SHOPPING_LIST_ID}, 'id' => $hash->{helper}{"ITEMS"}{SHOPPING_ITEM}{ID}{$SendData}}); $json =~ s/\"true\"/true/g; $json =~ s/\"false\"/false/g; $SendUrl .= "/api/namedLists/" . $hash->{helper}{SHOPPING_LIST_ID} . "/item/" . $hash->{helper}{"ITEMS"}{SHOPPING_ITEM}{ID}{$SendData}; $SendMetode = "DELETE"; $SendData = $json; $SendDataL = $SendData; } elsif ($type eq "account" ) { #Log3 $name, 2, "[$name] [echodevice_SendCommand] [$type] START"; $SendUrl = "https://alexa-comms-mobile-service.amazon.com/accounts"; $SendMetode = "GET"; $SendData = ""; } elsif ($type eq "homegroup" ) { $SendUrl = "https://alexa-comms-mobile-service.amazon.com/users/".$hash->{helper}{".COMMSID"}."/identities?includeUserName=true"; $SendMetode = "GET"; } elsif ($type eq "conversations" ) { $SendUrl = "https://alexa-comms-mobile-service.amazon.com/users/".$hash->{helper}{".COMMSID"}."/conversations?latest=true&includeHomegroup=true&unread=false&modifiedSinceDate=1970-01-01T00:00:00.000Z&includeUserName=true"; } elsif ($type eq "devices" || $type eq "autocreate_devices" || $type eq "devicesstate") { $SendUrl .= "/api/devices-v2/device?cached=true&_=".int(time); } elsif ($type eq "searchtunein" ) { $SendUrl .= "/api/tunein/search?query=".uri_escape_utf8(decode_utf8($SendData))."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}."&_=".int(time); $SendDataL = $SendData ; $SendData = ""; } elsif ($type eq "searchtracks" ) { $SendUrl .= "/api/cloudplayer/playlists/IMPORTED-V0-OBJECTID?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}."&size=50&offset=&mediaOwnerCustomerId=".$hash->{helper}{".CUSTOMER"}."&_=".int(time); } elsif ($type eq "getcards" ) { $SendUrl .= "/api/cards?limit=50&beforeCreationTime=".int(time)."000&_=".int(time); } elsif ($type eq "primeplayeigene_Albums" || $type eq "primeplayeigene_Tracks") { my $querytype = substr($type,16); $SendData =~ s/ /+/g; $SendUrl .= "/api/cloudplayer/search?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}. "&size=50&category=$querytype&query=". $SendData . "&offset=0" . "&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}."&_=".int(time); $SendDataL = $SendData ; $SendData = ""; } elsif ($type eq "primeplayeigene_Artists" ) { $SendData =~ s/ /+/g; $SendUrl .= "/api/cloudplayer/albums?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}. "&size=50&artistName=". $SendData ."&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}."&_=".int(time); $SendDataL = $SendData ; $SendData = ""; } elsif ($type eq "getprimeplayeigeneplaylist" ) { $SendUrl .= "/api/cloudplayer/playlists?deviceSerialNumber=".$hash->{helper}{".SERIAL"}."&deviceType=".$hash->{helper}{DEVICETYPE}. "&mediaOwnerCustomerId=".$hash->{IODev}->{helper}{".CUSTOMER"}."&_=".int(time); } elsif ($type eq "tts_translate" ) { $SendUrl = "http://www.online-translator.com/services/soap.asmx/GetTranslation"; $SendMetode = "POST"; } elsif ($type eq "sounds" ) { #Allgemeine Veariablen $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; $SendData = echodevice_getsequenceJson($hash,lc($SendData),""); $SendDataL = $SendData; } elsif ($type eq "info" ) { #Allgemeine Veariablen $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; $SendData = echodevice_getsequenceJson($hash,lc($SendData),""); $SendDataL = $SendData; } elsif ($type eq "getsettingstraffic") { $SendUrl .= "/api/traffic/settings"; $SendMetode = "GET"; $SendDataL = ""; $SendData = ""; } elsif ($type eq "address") { $SendUrl .= "/api/traffic/suggest?q=".urlEncode($SendData)."&suggestionType=LOCATION_ADDRESS"; $SendMetode = "GET"; $SendDataL = $SendData ; $SendData = ""; } elsif ($type eq "customer-history-records") { $SendUrl = "https://www.amazon.de/alexa-privacy/apd/rvh/customer-history-records/?startTime=0&endTime=2005090388459&recordType=VOICE_HISTORY&maxRecordSize=100"; $SendMetode = "GET"; $SendDataL = "" ; $SendData = ""; } elsif ($type eq "config_address_from" || $type eq "config_address_to" || $type eq "config_address_between") { $SendUrl .= "/api/traffic/settings"; $SendMetode = "POST"; my $InternFrom ; my $InternTo ; my $InternBetween ; $hash->{helper}{"getsettingstraffic"}{from} = $SendData if ($type eq "config_address_from"); $hash->{helper}{"getsettingstraffic"}{to} = $SendData if ($type eq "config_address_to"); $hash->{helper}{"getsettingstraffic"}{between} = $SendData if ($type eq "config_address_between"); #JSON String zusammenbauen if ($hash->{helper}{"getsettingstraffic"}{from} ne "") { $InternFrom = '"origin":{"label":"' . $hash->{helper}{"getsettingstraffic"}{from} . '"}' } else { $InternFrom = '"origin":null'; } if ($hash->{helper}{"getsettingstraffic"}{to} ne "") { $InternTo = '"destination":{"label":"' . $hash->{helper}{"getsettingstraffic"}{to} . '"}' } else { $InternTo = '"destination":null'; } if ($hash->{helper}{"getsettingstraffic"}{between} ne "") { $InternBetween = '"waypoints":[{"label":"' . $hash->{helper}{"getsettingstraffic"}{between} . '"}]' } else { $InternBetween = '"waypoints":[]'; } # Send JSON String fertigstellen $SendData = '{'.$InternFrom.','.$InternTo.','.$InternBetween.',"preferredTransportMode":"CAR","transportNames":null}' ; $SendDataL = $SendData ; } elsif ($type eq "volume") { #Allgemeine Veariablen $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; $SendData = echodevice_getsequenceJson($hash,$type,$SendData); $SendDataL = $SendData; } elsif ($type eq "speak") { #Allgemeine Veariablen $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; my $sequenceJson; # Sonderzeichen entfernen $SendData =~s/"/ /g; my $SpeakVolume; $SpeakVolume = int(AttrVal($hash->{IODev}{NAME},"speak_volume",0)); $SpeakVolume = int(AttrVal($name,"speak_volume",0)) if($SpeakVolume == 0); if($SpeakVolume > 0){ #if(ReadingsVal($name , "volume", 50) < ReadingsVal($name , "volume_alarm", 50)) { $SendData = '{"behaviorId":"PREVIEW","sequenceJson":"{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"@type\":\"com.amazon.alexa.behaviors.model.SerialNode\",\"nodesToExecute\":[{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"Alexa.DeviceControls.Volume\",\"operationPayload\":{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"locale\":\"de-DE\",\"value\":\"'.$SpeakVolume.'\",\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\"}},{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"Alexa.Speak\",\"operationPayload\":{\"locale\":\"de-DE\",\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\",\"textToSpeak\":\"'.$SendData.'\"}},{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"Alexa.DeviceControls.Volume\",\"operationPayload\":{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"locale\":\"de-DE\",\"value\":\"'.ReadingsVal($name , "volume", 50).'\",\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\"}}]}}","status":"ENABLED"}' } else { $SendData = echodevice_getsequenceJson($hash,$type,$SendData); } $SendDataL = $SendData; } elsif ($type eq "speak_ssml") { #Allgemeine Veariablen $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; my $sequenceJson; # Sonderzeichen entfernen $SendData =~s/"/'/g; my $SpeakVolume; $SpeakVolume = int(AttrVal($hash->{IODev}{NAME},"speak_volume",0)); $SpeakVolume = int(AttrVal($name,"speak_volume",0)) if($SpeakVolume == 0); if($SpeakVolume > 0){ $SendData = '{"behaviorId":"PREVIEW","sequenceJson":"{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"@type\":\"com.amazon.alexa.behaviors.model.SerialNode\",\"nodesToExecute\":[{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"Alexa.DeviceControls.Volume\",\"operationPayload\":{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"locale\":\"de-DE\",\"value\":\"'.$SpeakVolume.'\",\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\"}},{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"AlexaAnnouncement\",\"operationPayload\":{\"expireAfter\":\"PT5S\",\"content\":[{\"locale\":\"\",\"display\":{\"title\":\"FHEM\",\"body\":\"Speak\"},\"speak\":{\"type\":\"ssml\",\"value\":\"' . $SendData . '\"}}],\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"target\":{\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"devices\":[{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"deviceTypeId\":\"' . $hash->{helper}{DEVICETYPE} . '\"}]}}},{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"Alexa.DeviceControls.Volume\",\"operationPayload\":{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"locale\":\"de-DE\",\"value\":\"'.ReadingsVal($name , "volume", 50).'\",\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\"}}]}}","status":"ENABLED"}' } else { $SendData = '{"behaviorId": "PREVIEW","sequenceJson": "{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"type\":\"AlexaAnnouncement\",\"operationPayload\":{\"expireAfter\":\"PT5S\",\"content\":[{\"locale\":\"\",\"display\":{\"title\":\"FHEM\",\"body\":\"Speak\"},\"speak\":{\"type\":\"ssml\",\"value\":\"' . $SendData . '\"}}],\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"target\":{\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\",\"devices\":[{\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",\"deviceTypeId\":\"' . $hash->{helper}{DEVICETYPE} . '\"}]}}}}","status": "ENABLED"}'; } $SendDataL = $SendData; } elsif ($type eq "homescreen" ) { $SendUrl .= "/api/background-image"; $SendMetode = "POST"; $SendData = '{"backgroundImageID":"JqIFZhtBTx25wLGTJGdNGQ","backgroundImageType":"PERSONAL_PHOTOS","backgroundImageURL":"'.$SendData.'","deviceSerialNumber":"'.$hash->{helper}{".SERIAL"}.'","deviceType":"'.$hash->{helper}{DEVICETYPE}.'","softwareVersion":"'.$hash->{helper}{VERSION}.'"}'; $SendDataL = $SendData; } elsif ($type eq "textcommand") { $SendUrl .= "/api/behaviors/preview"; $SendMetode = "POST"; my $Messagetext = $SendData; $Messagetext =~ s/"/'/g; $SendData = '{"behaviorId":"PREVIEW","sequenceJson":"{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"@type\": \"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",\"nodeState\": null,\"name\": null,\"type\": \"Alexa.TextCommand\",\"skillId\": \"amzn1.ask.1p.tellalexa\",\"operationPayload\": {\"customerId\": \"' . $hash->{helper}{".CUSTOMER"} .'\",\"deviceType\": \"' . $hash->{helper}{DEVICETYPE} . '\",\"deviceSerialNumber\": \"' . $hash->{helper}{".SERIAL"} . '\",\"text\": \"' . $Messagetext .'\",\"locale\": \"de-DE\"},\"presentationDataList\": null,\"clientData\": null,\"context\": null,\"tag\": null},\"sequenceId\":\"amzn1.alexa.sequence.8f5aa289-c6d4-4a6f-a1b9-5b182e23be1e\"}","status":"ENABLED"}'; $SendDataL = $SendData; } else { return; } # Log Log3 $name, 4, "[$name] [echodevice_SendCommand] [$type] PushToCmdQueue SendURL =" .echodevice_anonymize($hash, $SendUrl); Log3 $name, 4, "[$name] [echodevice_SendCommand] [$type] PushToCmdQueue SendData=" .$SendDataL; #2018.01.14 - Übergabe SendCommandQuery $SendParam = { url => $SendUrl, hash => $hash, data => $SendData, method => $SendMetode, CL => $hash->{CL}, httpversion => "1.1", type => $type }; #2018.01.14 - PushToCmdQueue my $QueueSize = scalar @{$hash->{helper}{CMD_QUEUE}}; my $FoundCMDQueue = 0; my $Loglevel = 3; my @GetSettings = ("getnotifications","alarmvolume","bluetoothstate","getdnd","wakeword","listitems_task","listitems_shopping","getdevicesettings","getisonline","devices","namedListsIDs","devicesstate","account","cookielogin6","activities","getbehavior","getsettingstraffic"); # Doppelte Queue Einträge herausfiltern foreach my $CMDQueue (@{$hash->{helper}{CMD_QUEUE}}) { if ($CMDQueue->{type} eq $type) { if ((grep { $_ eq $type } @GetSettings)) { $Loglevel = 4 if ($type eq "activities"); Log3 $name, $Loglevel, "[$name] [echodevice_SendCommand] [$QueueSize] IGNORIERE Command=" . $type . ' Abfrage in CMD_Queue schon vorhanden!'; $FoundCMDQueue = 1; last; } } } if ($FoundCMDQueue == 0) { push @{$hash->{helper}{CMD_QUEUE}}, $SendParam; echodevice_HandleCmdQueue($hash); } return; } sub echodevice_HandleCmdQueue($) { my ($hash, $param) = @_; my $name = $hash->{NAME}; return undef if(!defined($hash->{helper}{CMD_QUEUE})); $hash->{helper}{RUNNING_REQUEST} = 0 if(!defined($hash->{helper}{RUNNING_REQUEST})); #Header auslesen my $AmazonHeader; # Browser User Agent my $HeaderLanguage = AttrVal($name,"browser_language","de,en-US;q=0.7,en;q=0.3"); my $UserAgent = AttrVal($name,"browser_useragent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"); if (AttrVal($name,"browser_useragent_random",0) == 1) { $UserAgent = join('', map{('a'..'z','A'..'Z',0..9)[rand 62]} 0..20); } # Queuenumber $QueueNumber += 1; readingsSingleUpdate ($hash, "BrowserUserAgent", $UserAgent ,0); readingsSingleUpdate ($hash, "BrowserLanguage", $HeaderLanguage ,0); $hash->{helper}{CMD_QUEUE_NUMBER} = $QueueNumber ; # if($hash->{model} eq "ACCOUNT") {$AmazonHeader = "Cookie: ".$hash->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} # else {$AmazonHeader = "Cookie: ".$hash->{IODev}->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{IODev}->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} if($hash->{model} eq "ACCOUNT") {$AmazonHeader = "User-Agent: ". $UserAgent ."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nCookie:".$hash->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} else {$AmazonHeader = "User-Agent: ". $UserAgent ."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nCookie:".$hash->{IODev}->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{IODev}->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} if(not($hash->{helper}{RUNNING_REQUEST}) and @{$hash->{helper}{CMD_QUEUE}}) { my $params = { url => $param->{url}, header => $AmazonHeader, timeout => 10, noshutdown => 1, keepalive => 1, method => $param->{method}, data => $param->{data}, CL => $param->{CL}, hash => $hash, type => $param->{type}, httpversion => $param->{httpversion}, queuenumber => $QueueNumber, callback => \&echodevice_Parse }; my $request = pop @{$hash->{helper}{CMD_QUEUE}}; map {$hash->{helper}{".HTTP_CONNECTION"}{$_} = $params->{$_}} keys %{$params}; map {$hash->{helper}{".HTTP_CONNECTION"}{$_} = $request->{$_}} keys %{$request}; my $type = $hash->{helper}{".HTTP_CONNECTION"}{type}; $hash->{helper}{RUNNING_REQUEST} = 1; my $QueueSize = scalar @{$hash->{helper}{CMD_QUEUE}}; Log3 $name, 4, "[$name] [echodevice_HandleCmdQueue] [$type] [$QueueNumber] [$QueueSize] send command=" .echodevice_anonymize($hash, $hash->{helper}{".HTTP_CONNECTION"}{url}). " Data=" . $hash->{helper}{".HTTP_CONNECTION"}{data}; HttpUtils_NonblockingGet($hash->{helper}{".HTTP_CONNECTION"}); } else { Log3 $name, 4, "[$name] [echodevice_HandleCmdQueue] RUNNING_REQUEST=" . $hash->{helper}{RUNNING_REQUEST} . " type=" . $hash->{helper}{".HTTP_CONNECTION"}{type} if($hash->{helper}{RUNNING_REQUEST} == 1); } } sub echodevice_SendLoginCommand($$$) { my ( $hash, $type, $SendData ) = @_; my $name = $hash->{NAME}; my $SendUrl; my $param; my $HeaderLanguage = AttrVal($name,"browser_language","de,en-US;q=0.7,en;q=0.3"); # Überspringen wenn Attr cookie gesetzt ist! if(AttrVal( $name, "cookie", "none" ) ne "none" && $type ne "cookielogin6") { Log3 $name, 3, "[$name] [echodevice_SendLoginCommand] echodevice_FirstStart"; echodevice_FirstStart($hash); return; } Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type]"; # Browser User Agent my $UserAgent = AttrVal($name,"browser_useragent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"); if (AttrVal($name,"browser_useragent_random",0) == 1) { $UserAgent = join('', map{('a'..'z','A'..'Z',0..9)[rand 62]} 0..20); } readingsSingleUpdate ($hash, "BrowserUserAgent", $UserAgent ,0); readingsSingleUpdate ($hash, "BrowserLanguage", $HeaderLanguage ,0); # COOKIE LOGIN if ($type eq "cookielogin1" ) { $param->{url} = "https://".$hash->{helper}{SERVER}."/"; $param->{method} = "GET"; $param->{ignoreredirects} = 1; $param->{header} = "User-Agent: ". $UserAgent ."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1"; $param->{callback} = \&echodevice_Parse; $param->{type} = $type; $param->{hash} = $hash; $param->{timeout} = 10; $param->{httpversion} = "1.1"; # Informationen füs Log Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] Accept-Language: $HeaderLanguage"; Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] User-Agent: $UserAgent"; #Daten zurücksetzen $hash->{helper}{".login_postdata"} = ""; $hash->{helper}{".login_location"} = ""; $hash->{helper}{".login_sessionid"} = ""; $hash->{helper}{".login_cookiestring"} = ""; readingsSingleUpdate ($hash, ".COOKIE", "" ,0); readingsSingleUpdate ($hash, "COOKIE_TYPE", "NEW" ,0); readingsSingleUpdate ($hash, "COOKIE_STATE", "START" ,0); } if ($type eq "cookielogin2" ) { $param->{url} = "https://".$hash->{helper}{SERVER}."/"; $param->{method} = "GET"; $param->{header} = "User-Agent: ".$UserAgent."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1"; $param->{callback} = \&echodevice_Parse; $param->{type} = $type; $param->{hash} = $hash; $param->{timeout} = 10; $param->{httpversion} = "1.1"; } if ($type eq "cookielogin3" ) { my $location = $hash->{helper}{".login_location"}; my $cookiestring = $hash->{helper}{".login_cookiestring"}; my $postdata = $hash->{helper}{".login_postdata"}; #Log3 $name, 3, "cookielogin3: ".$hash->{helper}{".login_cookiestring"} ; #Log3 $name, 3, "cookielogin3: ".$hash->{helper}{".login_postdata"} ; $param->{url} = "https://www.amazon.de/ap/signin"; $param->{method} = "POST"; $param->{header} = "User-Agent: ".$UserAgent."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nReferer: $location\r\nCookie: $cookiestring"; $param->{callback} = \&echodevice_Parse; $param->{data} = $postdata; $param->{type} = $type; $param->{hash} = $hash; #$param->{timeout} = 10; $param->{httpversion} = "1.0"; } if ($type eq "cookielogin4" ) { my $location = $hash->{helper}{".login_location"}; my $cookiestring = $hash->{helper}{".login_cookiestring"}; my $postdata = $hash->{helper}{".login_postdata"}; my $sessionid = $hash->{helper}{".login_sessionid"}; Log3 $name, 4, "cookielogin4: ".$hash->{helper}{".login_cookiestring"} ; Log3 $name, 4, "cookielogin4: ".$hash->{helper}{".login_postdata"} ; $param->{url} = "https://www.amazon.de/ap/signin"; $param->{method} = "POST"; $param->{header} = "User-Agent: ".$UserAgent."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nReferer: https://www.amazon.de/ap/signin/$sessionid\r\nCookie: $cookiestring"; $param->{callback} = \&echodevice_Parse; if ($hash->{helper}{TWOFA} eq "" || substr $hash->{helper}{TWOFA}, 0, 1 eq "n" || substr $hash->{helper}{TWOFA}, 0, 1 eq "u" ) { readingsSingleUpdate ($hash, "2FACode", "not used" ,0); $param->{data} = $postdata."email=".uri_escape(echodevice_decrypt($hash->{helper}{".USER"}))."&password=".uri_escape(echodevice_decrypt($hash->{helper}{".PASSWORD"}));#."&rememberMe=true"; Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] 2FACode not use"; #Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] " . $param->{data}; } else { readingsSingleUpdate ($hash, "2FACode", "used " .$hash->{helper}{TWOFA} ,0); my $zweiFA = $hash->{helper}{TWOFA} . "&rememberDevice"; $param->{data} = $postdata."email=".uri_escape(echodevice_decrypt($hash->{helper}{".USER"}))."&password=".uri_escape(echodevice_decrypt($hash->{helper}{".PASSWORD"})).$zweiFA; Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] 2FACode use " . $hash->{helper}{TWOFA} ; } $param->{ignoreredirects} = 1; $param->{type} = $type; $param->{hash} = $hash; #$param->{timeout} = 10; $param->{httpversion} = "1.0"; $hash->{helper}{TWOFA} = ""; } if ($type eq "cookielogin4captcha" ) { my $location = $hash->{helper}{".login_location"}; my $cookiestring = $hash->{helper}{".login_cookiestring"}; my $sessionid = $hash->{helper}{".login_sessionid"}; $param->{url} = "https://www.amazon.de/ap/signin"; $param->{method} = "POST"; $param->{header} = "User-Agent: ".$UserAgent."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nReferer: https://www.amazon.de/ap/signin/$sessionid\r\nCookie: $cookiestring"; $param->{callback} = \&echodevice_Parse; # Captcha Infos einlesen my $HTMLFilename = $name . "_cookielogin4.html"; my $file = $FW_dir . "/echodevice/results/" . $HTMLFilename ; my $document = do { local $/ = undef; open my $fh, "<", $file or die "could not open $file: $!"; <$fh>; }; my @formparams = ('create', 'workflowState','appActionToken','appAction','showRmrMe','captchaObfuscationLevel','openid.identity','forceValidateCaptcha','pageId','ces','openid.return_to','prevRID','openid.assoc_handle','openid.mode','prepopulatedLoginId','failedSignInCount','openid.claimed_id','openid.ns','showPasswordChecked','rememberMe','use_image_captcha'); my $postdata = ""; foreach my $formparam (@formparams){ my $value = ($document =~ /type="hidden" name="$formparam" value="(.*)"/); $value = $1; $value =~ /^(.*?)"/; $postdata .= $formparam."=".$1."&" } $param->{data} = $postdata."email=".uri_escape(echodevice_decrypt($hash->{helper}{".USER"}))."&guess=" .$hash->{helper}{CAPTCHA}."&password=".uri_escape(echodevice_decrypt($hash->{helper}{".PASSWORD"}));#."&rememberMe=true"; #Log3 $name, 4, "[$name] [echodevice_SendLoginCommand] [$type] " . $param->{data}; $param->{ignoreredirects} = 1; $param->{type} = $type; $param->{hash} = $hash; $param->{timeout} = 10; $param->{httpversion} = "1.1"; $hash->{helper}{TWOFA} = ""; $hash->{helper}{CAPTCHA} = ""; } if ($type eq "cookielogin5" ) { my $cookiestring = $hash->{helper}{".login_cookiestring"}; $param->{url} = "https://".$hash->{helper}{SERVER}."/api/bootstrap?version=0&_=".int(time); $param->{header} = "User-Agent: ".$UserAgent."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nReferer: https://".$hash->{helper}{SERVER}."/spa/index.html\r\nOrigin: https://".$hash->{helper}{SERVER}."\r\nCookie: $cookiestring"; $param->{callback} = \&echodevice_Parse; $param->{type} = $type; $param->{hash} = $hash; $param->{httpversion} = "1.1"; } if ($type eq "cookielogin6" ) { $param->{url} = "https://".$hash->{helper}{SERVER}."/api/bootstrap"; $param->{header} = 'Cookie: '.$hash->{helper}{".COOKIE"}; $param->{callback} = \&echodevice_ParseAuth; $param->{noshutdown} = 1; $param->{keepalive} = 1; $param->{type} = $type; $param->{hash} = $hash; $param->{timeout} = 10; $param->{httpversion} = "1.1"; } HttpUtils_NonblockingGet($param); } sub echodevice_Parse($$$) { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $msgtype = $param->{type}; my $msgnumber = $param->{queuenumber}; Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] "; Log3 $name, 5, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] DATA Dumper=" . Dumper(echodevice_anonymize($hash, $data)); $hash->{helper}{RUNNING_REQUEST} = 0; if ($msgtype eq "account") { Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] DATA Dumper=" . Dumper(echodevice_anonymize($hash, $data)); } # Nächsten Auftrag starten echodevice_HandleCmdQueue($hash); # HTML Informationen mit schreiben if (AttrVal($name,"browser_save_data",0) == 1) { #Verzeichnis echodevice anlegen mkdir($FW_dir . "/echodevice", 0777) unless(-d $FW_dir . "/echodevice" ); mkdir($FW_dir . "/echodevice/results", 0777) unless(-d $FW_dir . "/echodevice/results" ); # Eventuell vorhandene Datei löschen my $HTMLFilename = $name . "_" . $msgtype . ".html"; my $HeaderFilename = $name . "_" . $msgtype . "_header.html"; if ((-e $FW_dir . "/echodevice/results/". $HTMLFilename)) {unlink $FW_dir . "/echodevice/results/".$HTMLFilename} if ((-e $FW_dir . "/echodevice/results/". $HeaderFilename)) {unlink $FW_dir . "/echodevice/results/".$HeaderFilename} # Datei anlegen open(FH, ">$FW_dir/echodevice/results/$HTMLFilename"); print FH $data; close(FH); # Datei anlegen open(FH, ">$FW_dir/echodevice/results/$HeaderFilename"); print FH $param->{httpheader}; close(FH); } # COOKIE LOGIN Part if($msgtype eq "cookielogin1") { my $location = $param->{httpheader}; $location =~ /Location: (.+?)\s/; $location = $1; #$location = "https://www.amazon.de/ap/signin?_encoding=UTF8&accountStatusPolicy=&disableCorpSignUp=&openid.assoc_handle=deflex&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Flayla.amazon.de%2Fgp%2Fyourstore%3Fie%3DUTF8%26action%3Dsign-out%26path%3D%252Fgp%252Fyourstore%26ref_%3Dpd_irl_gw_r%26signIn%3D1%26useRedirectOnSuccess%3D1"; $hash->{helper}{".login_location"} = $location; #Log3 $name, 3, "Cookie 3 : ".$hash->{helper}{".login_location"} ; echodevice_SendLoginCommand($hash,"cookielogin2",""); return; } if($msgtype eq "cookielogin2") { my (@cookies) = ($param->{httpheader} =~ /Set-Cookie: (.*)\s/g); my $cookiestring = ""; my $sessionid = ""; foreach my $cookie (@cookies){ next if($cookie =~ /1970/); $cookie =~ /(.*) (expires=|Version=|Domain)/; $cookiestring .= $1." "; #Session ID my @SessionID = split("=",$1); if (@SessionID[0] eq "session-id") { $sessionid = @SessionID[1]; $sessionid =~ s/;//g; Log3 $name, 4, "Cookie 2 : COO = ".$sessionid ; $sessionid = $cookie; } } #my @formparams = ('appActionToken', 'appAction', 'openid.return_to', 'prevRID', 'workflowState', 'showPasswordChecked'); my @formparams = ('create', 'workflowState','appActionToken', 'appAction', 'showRmrMe', 'openid.return_to', 'prevRID', 'openid.identity', 'openid.assoc_handle', 'openid.mode', 'failedSignInCount', 'openid.claimed_id', 'pageId', 'openid.ns', 'showPasswordChecked'); my $postdata = ""; foreach my $formparam (@formparams){ my $value = ($data =~ /type="hidden" name="$formparam" value="(.*)"/); $value = $1; $value =~ /^(.*?)"/; $postdata .= $formparam."=".$1."&" } $hash->{helper}{".login_postdata"} = $postdata; $hash->{helper}{".login_cookiestring"} = $cookiestring; $hash->{helper}{".login_sessionid"} = $sessionid; #Log3 $name, 3, "Cookie 2 : login_postdata = ".$hash->{helper}{".login_postdata"} ; #Log3 $name, 3, "Cookie 2 : login_cookiestring = ".$hash->{helper}{".login_cookiestring"} ; #Log3 $name, 3, "Cookie 2 : login_sessionid = ".$hash->{helper}{".login_sessionid"} ; echodevice_SendLoginCommand($hash,"cookielogin3",""); return; } if($msgtype eq "cookielogin3") { #my @formparams = ('appActionToken', 'appAction', 'openid.return_to', 'prevRID', 'workflowState', 'showPasswordChecked'); my @formparams = ('create', 'workflowState','appActionToken', 'appAction', 'showRmrMe', 'openid.return_to', 'prevRID', 'openid.identity', 'openid.assoc_handle', 'openid.mode', 'failedSignInCount', 'openid.claimed_id', 'pageId', 'openid.ns', 'showPasswordChecked'); my $postdata = ""; foreach my $formparam (@formparams){ my $value = ($data =~ /type="hidden" name="$formparam" value="(.*)"/); $value = $1; $value =~ /^(.*?)"/; $postdata .= $formparam."=".$1."&" } my (@cookies2) = ($param->{httpheader} =~ /Set-Cookie: (.*)\s/g); my $sessionid = ""; my $cookiestring2 = ""; foreach my $cookie (@cookies2){ next if($cookie =~ /1970/); $cookie =~ /(.*) (expires|Version|Domain)/; $cookiestring2 .= $1." "; $cookiestring2 =~ /ubid-acbde=(.*);/; $sessionid = $1; } $hash->{helper}{".login_postdata"} = $postdata; $hash->{helper}{".login_sessionid"} = $sessionid; $hash->{helper}{".login_cookiestring"} = $hash->{helper}{".login_cookiestring"} . $cookiestring2; #Log3 $name, 3, "Cookie 3 : ".$hash->{helper}{".login_postdata"} ; #Log3 $name, 3, "Cookie 3 : ".$hash->{helper}{".login_sessionid"} ; #Log3 $name, 3, "Cookie 3 : ".$hash->{helper}{".login_cookiestring"} ; echodevice_SendLoginCommand($hash,"cookielogin4",""); return; } if($msgtype eq "cookielogin4" || $msgtype eq "cookielogin4captcha") { my (@cookies3) = ($param->{httpheader} =~ /Set-Cookie: (.*)\s/g); my $cookiestring3 = ""; my $cookiestring = $hash->{helper}{".login_cookiestring"}; foreach my $cookie (@cookies3){ #Log3 $name, 5, "Cookie: ".$cookie; next if($cookie =~ /1970/); $cookie =~ s/Version=1; //g; $cookie =~ /(.*) (expires|Version|Domain)/; $cookie = $1; next if($cookiestring =~ /\Q$cookie\E/); $cookiestring3 .= $cookie." "; } #Log3 $name, 4, "[$name] [echodevice_Parse] [cookiestring3] = $cookiestring3"; $hash->{helper}{".login_cookiestring"}.= $cookiestring3; #Log3 $name, 3, "Cookie 4 : ".$cookiestring3 ; #$cookiestring3 =~s/"//g; #Log3 $name, 3, "Cookie 4 : ".$cookiestring3 ; #Log3 $name, 3, "Cookie 4 : ".$hash->{helper}{".login_cookiestring"} ; echodevice_SendLoginCommand($hash,"cookielogin5",""); return; } if($msgtype eq "cookielogin5") { my (@cookies4) = ($param->{httpheader} =~ /Set-Cookie: (.*)\s/g); my $cookiestring4 = ""; my $cookiestring = $hash->{helper}{".login_cookiestring"}; foreach my $cookie (@cookies4){ #Log3 $name, 5, "Cookie: ".$cookie; next if($cookie =~ /1970/); $cookie =~ s/Version=1; //g; $cookie =~ /(.*) (expires|Version)/; $cookie = $1; next if($cookiestring =~ /\Q$cookie\E/); $cookiestring4 .= $cookie." "; } $cookiestring .= $cookiestring4; $hash->{helper}{".login_cookiestring"}.= $cookiestring4; if($cookiestring =~ /doctype html/) { #RemoveInternalTimer($hash); Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] Login failed"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "unauthorized", 1); readingsEndUpdate($hash,1); $hash->{STATE} = "LOGIN ERROR"; return undef; } #Log3 $name, 4, "[$name] [echodevice_Parse] [cookiestring] = $cookiestring"; $hash->{helper}{".COOKIE"} = $cookiestring; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/ if(defined($hash->{helper}{".COOKIE"})); $hash->{helper}{".CSRF"} = $1 if(defined($hash->{helper}{".COOKIE"})); #Log3 $name, 3, "Cookie 4 : ".$cookiestring ; if(defined($hash->{helper}{".COOKIE"})){ readingsSingleUpdate ($hash, ".COOKIE", $hash->{helper}{".COOKIE"} ,0); # Cookie als READING festhalten! readingsSingleUpdate ($hash, "COOKIE_TYPE", "NEW" ,0); } echodevice_SendLoginCommand($hash,"cookielogin6",""); return; } if($msgtype eq "cookielogin6") { readingsSingleUpdate ($hash, "COOKIE_STATE", "OK" ,0); echodevice_SendCommand($hash,"devices",""); return; } if($msgtype eq "notifications_delete" || $msgtype eq "alarm_on" || $msgtype eq "alarm_off" || $msgtype eq "reminderitem") { my $IODev = $hash->{IODev}->{NAME}; Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] sendToFHEM get $IODev settings"; print (fhem( "get $IODev settings" )) ; return; } if($data =~ /doctype html/ || $data =~ /cookie is missing/){ #RemoveInternalTimer($hash); Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] Invalid cookie"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "unauthorized", 1); readingsEndUpdate($hash,1); $hash->{STATE} = "COOKIE ERROR"; return undef; } if($err){ Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] connection error $msgtype $err"; return undef; } if($data =~ /No routes found/){ # Spezial set Volume if ($msgtype eq "command") {} else { Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] No routes found $msgtype"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "timeout", 1); readingsEndUpdate($hash,1); } return undef; } if($data =~ /UnknownOperationException/){ Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] Unknown Operation"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "unknown", 1); readingsEndUpdate($hash,1); return undef; } if($msgtype eq "setting" || $msgtype eq "command"){ InternalTimer( gettimeofday() + 3, "echodevice_GetSettings", $hash, 0); return undef; } elsif($msgtype eq "null" || $msgtype eq "primeplayeigeneplaylist" || $msgtype eq "primeplayeigene" || $msgtype eq "primeplaysender" || $msgtype eq "primeplaylist" || $msgtype eq "textmessage" || $msgtype eq "volume_alarm" || $msgtype eq "bluetooth_disconnect" || $msgtype eq "bluetooth_connect" || $msgtype eq "dnd" || $msgtype eq "list" || $msgtype eq "track") { return undef; } elsif($msgtype eq "item_task_delete" || $msgtype eq "item_task_add") { echodevice_SendCommand($hash,"listitems_task","TASK"); return undef; } elsif($msgtype eq "item_shopping_delete" || $msgtype eq "item_shopping_add") { echodevice_SendCommand($hash,"listitems_shopping","SHOPPING_ITEM"); return undef; } if($@) { if($data =~ /doctype html/ || $data =~ /cookie is missing/){ #RemoveInternalTimer($hash); Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] Invalid cookie"; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "unauthorized", 1); readingsEndUpdate($hash,1); $hash->{STATE} = "COOKIE ERROR"; #InternalTimer( gettimeofday() + 10, "echodevice_CheckAuth", $hash, 0) if($hash->{model} eq "ACCOUNT"); return undef; } readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "error", 1); readingsEndUpdate($hash,1); Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] json evaluation error ".$@."\n".Dumper(echodevice_anonymize($hash, $data)); return undef; } readingsBeginUpdate($hash); readingsBulkUpdateIfChanged($hash, "state", "connected", 1); readingsEndUpdate($hash,1); # Prüfen ob es sich um ein json String handelt! if (index($data, '{') == -1) {$data = '{"data": "nodata"}';} my $json = eval { JSON->new->utf8(0)->decode($data) }; if($msgtype eq "activities") { if (ref($json) eq "HASH") { if(defined($json->{activities}) && ref($json->{activities}) eq "ARRAY") { foreach my $card (@{$json->{activities}}) { # Device ID herausfiltern my $sourceDeviceIds = ""; foreach my $cards (@{$card->{sourceDeviceIds}}) { $sourceDeviceIds = $cards->{serialNumber}; } # Informationen in das ECHO Device eintragen if(defined($modules{$hash->{TYPE}}{defptr}{$sourceDeviceIds})) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$sourceDeviceIds}; #my $timestamp = int(time - ReadingsAge($echohash->{NAME},'voice',time))-5; my $timestamp = int(ReadingsVal($echohash->{NAME},'voice_timestamp',9999)); my $IgnoreVoiceCommand = AttrVal($name,"ignorevoicecommand",""); #Log3 $name, 3, "[$name] [echodevice_Parse] [" . $echohash->{NAME} . "] timestamp = $timestamp / " . int($card->{creationTimestamp}); #next if($timestamp eq $card->{creationTimestamp}); next if($timestamp >= int($card->{creationTimestamp})); #next if($timestamp >= int($card->{creationTimestamp}/1000)); next if($card->{description} !~ /firstUtteranceId/); #https://forum.fhem.de/index.php/topic,82631.msg906424.html#msg906424 next if($IgnoreVoiceCommand ne "" && $card->{description} =~ m/$IgnoreVoiceCommand/i); my $textjson = $card->{description}; $textjson =~ s/\\//g; my $cardjson = eval { JSON->new->utf8(0)->decode($textjson) }; next if($@); next if(!defined($cardjson->{summary})); next if($cardjson->{summary} eq ""); $echohash->{".updateTimestamp"} = FmtDateTime(int($card->{creationTimestamp}/1000)); readingsBeginUpdate($echohash); readingsBulkUpdate($echohash, "voice", $cardjson->{summary}, 1); readingsBulkUpdate($echohash, "voice_timestamp", $card->{creationTimestamp}, 1); readingsEndUpdate($echohash,1); $echohash->{CHANGETIME}[0] = FmtDateTime(int($card->{creationTimestamp}/1000)); #Log3 $name, 3, "[$name] [echodevice_Parse] [" . $echohash->{NAME} . "] Alexatext = ".$cardjson->{summary}; } } } # Timer für Realtime Check! my $IntervalVoice = int(AttrVal($name,"intervalvoice",999999)); if ($IntervalVoice != 999999 && $hash->{STATE} eq "connected" && AttrVal($name,"disable",0) == 0) { Log3 $name, 5, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] refresh voice command IntervalVoice=$IntervalVoice "; $hash->{helper}{echodevice_refreshvoice} = 1; $hash->{helper}{echodevice_refreshvoice_lastdate} = time(); RemoveInternalTimer($hash, "echodevice_refreshvoice"); InternalTimer(gettimeofday() + $IntervalVoice , "echodevice_refreshvoice", $hash, 0); } else { $hash->{helper}{echodevice_refreshvoice} = 0; } } else { Log3 $name, 3, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] WRONG JSON Type Type=" . ref($json); } } elsif($msgtype eq "account") { my $i=1; if ($data eq '{"data": "nodata"}') { } else { if(ref($json) eq 'ARRAY') { foreach my $account (@{$json}) { $hash->{helper}{".COMMSID"} = $account->{commsId} if(defined($account->{commsId})); $hash->{helper}{".DIRECTID"} = $account->{directedId} if(defined($account->{directedId})); last if(1<$i++); } } } } elsif($msgtype eq "cards") { my $timestamp = int(time - ReadingsAge($name,'voice',time)); return undef if(!defined($json->{cards})); return undef if(ref($json->{cards}) ne "ARRAY"); foreach my $card (reverse(@{$json->{cards}})) { #next if($card->{cardType} ne "TextCard"); #next if($card->{sourceDevice}{serialNumber} ne $hash->{helper}{".SERIAL"}); next if($timestamp >= int($card->{creationTimestamp}/1000)); next if(!defined($card->{playbackAudioAction}{mainText})); readingsBeginUpdate($hash); $hash->{".updateTimestamp"} = FmtDateTime(int($card->{creationTimestamp}/1000)); readingsBulkUpdate( $hash, "voice", $card->{playbackAudioAction}{mainText}, 1 ); $hash->{CHANGETIME}[0] = FmtDateTime(int($card->{creationTimestamp}/1000)); readingsEndUpdate($hash,1); } return undef; } elsif($msgtype eq "media") { readingsBeginUpdate($hash); if (defined($json->{currentState} )) { if ($json->{currentState} ne "IDLE") { #echodevice_SendCommand($hash,"player",""); # Player läuft! Daten abfragen! } else { readingsBulkUpdate($hash, "progress", "0", 1); readingsBulkUpdate($hash, "progresslen", "0", 1); readingsBulkUpdate($hash, "shuffle", $json->{shuffling}?"on":"off", 1) if(defined($json->{shuffling})); readingsBulkUpdate($hash, "repeat", $json->{looping}?"on":"off", 1) if(defined($json->{looping})); readingsBulkUpdate($hash, "volume", $json->{volume}, 1) if(defined($json->{volume})); readingsBulkUpdate($hash, "mute", $json->{muted}?"on":"off", 1) if(defined($json->{muted})); } } readingsEndUpdate($hash,1); } elsif($msgtype eq "player") { # Beenden wenn keine Daten vorhanden! if(defined($json->{playerInfo})){ readingsBeginUpdate($hash); my $TempTuneInName ; my $TempTuneInURL ; # Play Status if(!defined($json->{playerInfo}{state}) || $json->{playerInfo}{state} eq "IDLE" ){ readingsBulkUpdate($hash, "playStatus", "stopped", 1); readingsBulkUpdate($hash, "currentArtwork", "-", 1); readingsBulkUpdate($hash, "currentTitle", "-", 1); readingsBulkUpdate($hash, "currentArtist", "-", 1); readingsBulkUpdate($hash, "currentAlbum", "-", 1); readingsBulkUpdate($hash, "currentTuneInID", "-", 1); readingsBulkUpdate($hash, "channel", "-", 1); readingsBulkUpdate($hash, "progress", 0, 1); readingsBulkUpdate($hash, "progresslen", 0, 1); } else { readingsBulkUpdate($hash, "playStatus", lc($json->{playerInfo}{state}), 1); if(defined($json->{playerInfo}{infoText})) { readingsBulkUpdate($hash, "currentTitle", $json->{playerInfo}{infoText}{title}, 1) if(defined($json->{playerInfo}{infoText}{title})); readingsBulkUpdate($hash, "currentArtist", $json->{playerInfo}{infoText}{subText1}, 1) if(defined($json->{playerInfo}{infoText}{subText1})); readingsBulkUpdate($hash, "currentAlbum", $json->{playerInfo}{infoText}{subText2}, 1) if(defined($json->{playerInfo}{infoText}{subText2})); readingsBulkUpdate($hash, "currentTitle", "-", 1) if(!defined($json->{playerInfo}{infoText}{title})); readingsBulkUpdate($hash, "currentArtist", "-", 1) if(!defined($json->{playerInfo}{infoText}{subText1})); readingsBulkUpdate($hash, "currentAlbum", "-", 1) if(!defined($json->{playerInfo}{infoText}{subText2})); } if(defined($json->{playerInfo}{provider})) { readingsBulkUpdate($hash, "channel", $json->{playerInfo}{provider}{providerName}, 1) if(defined($json->{playerInfo}{provider}{providerName})); $TempTuneInName = "TuneIn" if (lc($json->{playerInfo}{provider}{providerName}) eq 'tunein live-radio' || lc($json->{playerInfo}{provider}{providerName}) eq 'tunein-liveradio') ; } else { readingsBulkUpdate($hash, "channel", "-", 1); } if(defined($json->{playerInfo}{mainArt})) { if(defined($json->{playerInfo}{mainArt}{url})){ readingsBulkUpdate($hash, "currentArtwork", $json->{playerInfo}{mainArt}{url}, 1); $TempTuneInURL = $json->{playerInfo}{mainArt}{url}; } else{ readingsBulkUpdate($hash, "currentArtwork", "-", 1); } } if (lc($json->{playerInfo}{state}) eq "playing") { # TuneIn ID festhalten if ($TempTuneInURL ne "" && $TempTuneInName eq "TuneIn") { my @TuneInID = split("/",$TempTuneInURL); if (@TuneInID >= 3) { $TempTuneInName = @TuneInID[3]; $TempTuneInName =~ s/(\D+)//; $TempTuneInName =~ s/(\D+)//; $TempTuneInName = "s" . $TempTuneInName; } else { $TempTuneInName = "-"; } readingsBulkUpdate($hash, "currentTuneInID", $TempTuneInName , 1); } else { readingsBulkUpdate($hash, "currentTuneInID", "-", 1); } } else { readingsBulkUpdate($hash, "currentTuneInID", "-", 1); } if(defined($json->{playerInfo}{progress})) { readingsBulkUpdate($hash, "progress", $json->{playerInfo}{progress}{mediaProgress}, 1) if(defined($json->{playerInfo}{progress}{mediaProgress})); readingsBulkUpdate($hash, "progress", 0, 1) if(!defined($json->{playerInfo}{progress}{mediaProgress})); readingsBulkUpdate($hash, "progresslen", $json->{playerInfo}{progress}{mediaLength}, 1) if(defined($json->{playerInfo}{progress}{mediaLength})); readingsBulkUpdate($hash, "progresslen", 0, 1) if(!defined($json->{playerInfo}{progress}{mediaLength})); } if(defined($json->{playerInfo}{volume})) { readingsBulkUpdate($hash, "volume", $json->{playerInfo}{volume}{volume}, 1) if(defined($json->{playerInfo}{volume}{volume})); readingsBulkUpdate($hash, "mute", $json->{playerInfo}{volume}{muted}?"on":"off", 1) if(defined($json->{playerInfo}{volume}{muted})); } if(defined($json->{playerInfo}{transport}{shuffle})) { if($json->{playerInfo}{transport}{shuffle} eq "SELECTED") {readingsBulkUpdate($hash, "shuffle", "true", 1);} else{readingsBulkUpdate($hash, "shuffle", "false", 1);} } if(defined($json->{playerInfo}{transport}{repeat})) { if($json->{playerInfo}{transport}{repeat} eq "SELECTED") {readingsBulkUpdate($hash, "repeat", "true", 1);} else{readingsBulkUpdate($hash, "repeat", "false", 1);} } } readingsEndUpdate($hash,1); } } elsif($msgtype eq "getbehavior") { $hash->{helper}{"getbehavior"} = (); return if (ref($json) ne "ARRAY"); foreach my $behavior (@{$json}) { $hash->{helper}{"getbehavior"}{$behavior->{automationId}} = (); $hash->{helper}{"getbehavior"}{$behavior->{automationId}}{triggers} = $behavior->{triggers}; $hash->{helper}{"getbehavior"}{$behavior->{automationId}}{sequence} = $behavior->{sequence}; $hash->{helper}{"getbehavior"}{$behavior->{automationId}}{status} = $behavior->{status}; } } elsif($msgtype eq "namedListsIDs") { if (ref($json) eq "HASH") { if(!defined($json->{lists})) {} elsif (ref($json->{lists}) ne "ARRAY") {} else { foreach my $device (@{$json->{lists}}) { if ($device->{type} eq "SHOPPING_LIST") {$hash->{helper}{SHOPPING_LIST_ID} = $device->{itemId}} if ($device->{type} eq "TO_DO") {$hash->{helper}{TO_DO_LIST_ID} = $device->{itemId}} } } } else { Log3 $name, 5, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] WRONG JSON Type Type=" . ref($json); } } elsif($msgtype eq "listitems_task" || $msgtype eq "listitems_shopping" ) { my $listtype ;#= $param->{listtype}; my @listitems; my $Firststart = "1"; my $Text ; my $TextOriginal; $listtype = "TASK" if ($msgtype eq "listitems_task"); $listtype = "SHOPPING_ITEM" if ($msgtype eq "listitems_shopping"); foreach my $item ( @{ $json->{values} } ) { if ($Firststart eq "1"){ $hash->{helper}{"ITEMS"}{$item->{type}} = (); $Firststart = "0"; } $TextOriginal = $item->{text}; next if ($item->{complete}); $item->{text} =~ s/,/;/g; $item->{text} =~ s/ /_/g; $Text = $item->{text}; push @listitems, $item->{text}; $hash->{helper}{"ITEMS"}{$item->{type}}{"ID"}{$item->{text}} = (split("#", $item->{itemId}))[1]; $hash->{helper}{"ITEMS"}{$item->{type}}{"TEXT"}{$item->{text}} = $TextOriginal; } readingsBeginUpdate($hash); if (@listitems) { readingsBulkUpdate( $hash, "list_".$listtype, join(",", @listitems), 1 ); } else { readingsBulkUpdate( $hash, "list_".$listtype, "", 1 ); } readingsEndUpdate($hash,1); } elsif($msgtype eq "getnotifications") { my @ncstrings; @ncstrings = (); $hash->{helper}{"notifications"} = (); my $RunningID = time(); my $NotifiCount ; my $NotifiReTime = 99999999; my $TimerReTime = 99999999 ; my $iFrom ; my $HelperNotifyID ; if (ref($json) eq "HASH") { foreach my $device (@{$json->{notifications}}) { #next if ($device->{status} eq "OFF" && (lc($device->{type}) ne "reminder" || lc($device->{type}) ne "timer")); $HelperNotifyID = $device->{notificationIndex}; my $ncstring ; if(lc($device->{type}) eq "reminder") { $ncstring = $device->{type} . "_" . FmtDateTime($device->{alarmTime}/1000) . "_"; $ncstring .= $device->{recurringPattern} . "_" if (defined($device->{recurringPattern})); $ncstring .= $device->{reminderLabel} ; } elsif(lc($device->{type}) eq "timer") { $ncstring = $device->{type} . "_" . $device->{remainingTime} } else { $ncstring = $device->{type} . "_" . $device->{originalTime} ; } $hash->{helper}{"notifications"}{$device->{deviceSerialNumber}}{$device->{notificationIndex}} = $ncstring; #Reading anlegen my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; if (!defined($hash->{helper}{"notifications"}{"_".$device->{deviceSerialNumber}}{"count_" . $device->{type}})) { $NotifiCount = 1; } else { $NotifiCount = int($hash->{helper}{"notifications"}{"_".$device->{deviceSerialNumber}}{"count_" . $device->{type}}) + 1 } next if(!defined($echohash)); readingsBeginUpdate($echohash); if(lc($device->{type}) eq "reminder") { readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_alarmtime" , FmtDateTime($device->{alarmTime}/1000), 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_alarmticks" , $device->{alarmTime}/1000, 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_id" , $device->{notificationIndex},1); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_recurring" , $device->{recurringPattern},1) if (defined($device->{recurringPattern})); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_recurring" , 0,1) if (!defined($device->{recurringPattern})); } elsif(lc($device->{type}) eq "timer") { readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_remainingtime" , int($device->{remainingTime} / 1000), 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_id" , $device->{notificationIndex},1); if (int($device->{remainingTime} / 1000) < $TimerReTime) { $TimerReTime = int($device->{remainingTime} / 1000); readingsBulkUpdate( $echohash, lc($device->{type}) . "_remainingtime" , int($device->{remainingTime} / 1000), 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_id" , $device->{notificationIndex},1); } if ($TimerReTime <$NotifiReTime) {$NotifiReTime = $TimerReTime;} } else { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID} = (); if ($device->{musicEntity} eq "") { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"musicEntity"} = "null"; } else { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"musicEntity"} = '"'.$device->{musicEntity}.'"'; } if ($device->{musicAlarmId} eq "") { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"musicAlarmId"} = "null"; } else { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"musicAlarmId"} = '"'.$device->{musicAlarmId}.'"'; } if ($device->{recurringPattern} eq "") { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"recurringPattern"} = "null"; } else { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"recurringPattern"} = $device->{recurringPattern}; } if ($device->{provider} eq "") { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"provider"} = "null"; } else { $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"provider"} = '"'.$device->{provider}.'"'; } $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"remainingTime"} = $device->{remainingTime}; $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"alarmTime"} = $device->{alarmTime}; $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"originalDate"} = $device->{originalDate}; $hash->{helper}{$device->{type}}{$device->{deviceSerialNumber}}{$HelperNotifyID}{"originalTime"} = $device->{originalTime}; readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_originalTime" , $device->{originalTime}, 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_originalDate" , $device->{originalDate}, 1 ); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_id" , $device->{notificationIndex},1); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_status" , lc($device->{status}),1); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_recurring" , $device->{recurringPattern},1) if (defined($device->{recurringPattern})); readingsBulkUpdate( $echohash, lc($device->{type}) . "_" . sprintf("%02d",$NotifiCount) . "_recurring" , 0,1) if (!defined($device->{recurringPattern})); } # Infos im Hash hinterlegen $hash->{helper}{"notifications"}{"_".$device->{deviceSerialNumber}}{"count_" . $device->{type}} = $NotifiCount; $hash->{helper}{"notifications"}{"_".$device->{deviceSerialNumber}}{lc($device->{type})."_aktiv"} = 1; readingsEndUpdate($echohash,1); } # Notifications Counter setzen foreach my $DeviceID (sort keys %{$modules{$hash->{TYPE}}{defptr}}) { foreach my $NotifyCounter (sort keys %{$hash->{helper}{"notifications"}{"_".$DeviceID}}) { if ($NotifyCounter =~ m/count/ ) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$DeviceID}; readingsSingleUpdate($echohash, lc((split ("_", $NotifyCounter))[1]). "_count" ,$hash->{helper}{"notifications"}{"_".$DeviceID}{$NotifyCounter} , 1); } } } # Timer neu setzen wenn der Timer gleich abläuft if ($NotifiReTime < 60 && $NotifiReTime > 0) {InternalTimer(gettimeofday() + $NotifiReTime , "echodevice_GetSettings", $hash, 0);} # Readings bereinigen my $nextupdate = int(AttrVal($name,"intervalsettings",60)); foreach my $DeviceID (sort keys %{$modules{$hash->{TYPE}}{defptr}}) { next if (echodevice_getModel($modules{$hash->{TYPE}}{defptr}{$DeviceID}{model}) eq "Echo Multiroom"); next if (echodevice_getModel($modules{$hash->{TYPE}}{defptr}{$DeviceID}{model}) eq "Sonos Display"); next if (echodevice_getModel($modules{$hash->{TYPE}}{defptr}{$DeviceID}{model}) eq "Echo Stereopaar"); next if (echodevice_getModel($modules{$hash->{TYPE}}{defptr}{$DeviceID}{model}) eq "unbekannt"); my $DeviceName = $modules{$hash->{TYPE}}{defptr}{$DeviceID}{NAME}; my $echohash = $modules{$hash->{TYPE}}{defptr}{$DeviceID}; readingsBeginUpdate($echohash); # Timer auswerten my $TimerAktiv = 0; foreach my $i (1..20) { my $ReadingAge = int(ReadingsAge($DeviceName, "timer_" . sprintf("%02d",$i) . "_remainingtime", 2000)); if ($ReadingAge == 2000){last;} elsif ($ReadingAge > $nextupdate) { readingsDelete($echohash, "timer_" . sprintf("%02d",$i) . "_id") ; readingsDelete($echohash, "timer_" . sprintf("%02d",$i) . "_remainingtime") ; } else {$TimerAktiv=1;} } if ($TimerAktiv == 0) { readingsBulkUpdate( $echohash, "timer_count" , 0,1); readingsBulkUpdate( $echohash, "timer_id" , "-",1); readingsBulkUpdate( $echohash, "timer_remainingtime" , 0,1); } # Erinnerungen auswerten my $ReminderAktiv = 0; $ReminderAktiv = $hash->{helper}{"notifications"}{"_".$DeviceID}{"reminder_aktiv"} if (defined($hash->{helper}{"notifications"}{"_".$DeviceID}{"reminder_aktiv"})); if ($ReminderAktiv eq "0") { readingsBulkUpdate( $echohash, "reminder_count" , 0,1); } else { $hash->{helper}{"notifications"}{"_".$DeviceID}{"reminder_aktiv"} = 0 } $iFrom = int(ReadingsVal($DeviceName, "reminder_count", 0)) +1 ; foreach my $i ($iFrom..20) { if (ReadingsVal($DeviceName, "reminder_" . sprintf("%02d",$i) . "_alarmticks", "none") ne "none"){ readingsDelete($echohash, "reminder_" . sprintf("%02d",$i) . "_id") ; readingsDelete($echohash, "reminder_" . sprintf("%02d",$i) . "_alarmticks") ; readingsDelete($echohash, "reminder_" . sprintf("%02d",$i) . "_alarmtime") ; readingsDelete($echohash, "reminder_" . sprintf("%02d",$i) . "_recurring") ; } else {last;} } # Alarm auswerten my $AlarmAktiv = 0; $AlarmAktiv = $hash->{helper}{"notifications"}{"_".$DeviceID}{"alarm_aktiv"} if (defined($hash->{helper}{"notifications"}{"_".$DeviceID}{"alarm_aktiv"})); if ($AlarmAktiv eq "0") { readingsBulkUpdate( $echohash, "alarm_count" , 0,1); } else { $hash->{helper}{"notifications"}{"_".$DeviceID}{"alarm_aktiv"} = 0 } $iFrom = int(ReadingsVal($DeviceName, "alarm_count", 0)) +1 ; foreach my $i ($iFrom..20) { if (ReadingsVal($DeviceName, "alarm_" . sprintf("%02d",$i) . "_id", "none") ne "none"){ readingsDelete($echohash, "alarm_" . sprintf("%02d",$i) . "_id") ; readingsDelete($echohash, "alarm_" . sprintf("%02d",$i) . "_originalTime") ; readingsDelete($echohash, "alarm_" . sprintf("%02d",$i) . "_originalDate") ; readingsDelete($echohash, "alarm_" . sprintf("%02d",$i) . "_status") ; readingsDelete($echohash, "alarm_" . sprintf("%02d",$i) . "_recurring") ; } else {last;} } # Musikalarm auswerten my $MusikAlarmAktiv = 0; $MusikAlarmAktiv = $hash->{helper}{"notifications"}{"_".$DeviceID}{"musikalarm_aktiv"} if (defined($hash->{helper}{"notifications"}{"_".$DeviceID}{"musicalarm_aktiv"})); if ($MusikAlarmAktiv eq "0") { readingsBulkUpdate( $echohash, "musicalarm_count" , 0,1); } else { $hash->{helper}{"notifications"}{"_".$DeviceID}{"musicalarm_aktiv"} = 0 } $iFrom = int(ReadingsVal($DeviceName, "musicalarm_count", 0)) +1 ; foreach my $i ($iFrom..20) { if (ReadingsVal($DeviceName, "musicalarm_" . sprintf("%02d",$i) . "_id", "none") ne "none"){ readingsDelete($echohash, "musicalarm_" . sprintf("%02d",$i) . "_id") ; readingsDelete($echohash, "musicalarm_" . sprintf("%02d",$i) . "_originalTime") ; readingsDelete($echohash, "musicalarm_" . sprintf("%02d",$i) . "_originalDate") ; readingsDelete($echohash, "musicalarm_" . sprintf("%02d",$i) . "_status") ; readingsDelete($echohash, "musicalarm_" . sprintf("%02d",$i) . "_recurring") ; } else {last;} } readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "homegroup") { $hash->{helper}{HOMEGROUP} = $json->{homeGroupId} if(defined($json->{homeGroupId})); $hash->{helper}{SIPS} = $json->{aor} if(defined($json->{aor})); } elsif($msgtype eq "bluetoothstate") { my @btstrings; my @btdevices; my $echohash; my $ConnectState; foreach my $device (@{$json->{bluetoothStates}}) { @btstrings = (); if(defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}})) { foreach my $btdevice (@{$device->{pairedDeviceList}}) { next if(!defined($btdevice->{friendlyName})); next if (echodevice_getModel($btdevice->{deviceType}) eq "Reverb"); next if (echodevice_getModel($btdevice->{deviceType}) eq "unbekannt"); $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; $btdevice->{address} =~ s/:/-/g; $btdevice->{friendlyName} =~ s/ /_/g; $btdevice->{friendlyName} =~ s/,/./g; if (int($btdevice->{connected}) == 0) {$ConnectState = "disconnected";} elsif (int($btdevice->{connected}) == 1) {$ConnectState = "connected"} else {$ConnectState = "unknown";} readingsSingleUpdate($echohash, "bluetooth_" . $btdevice->{address} ,$ConnectState , 1); my $btstring .= $btdevice->{address}."/".$btdevice->{friendlyName}; push @btstrings, $btstring; push @btdevices, "bluetooth_" . $btdevice->{address}; } $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}->{helper}{bluetooth} = join(",", @btstrings) if (@btstrings); $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}->{helper}{bluetooth} = "-" if(!defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}->{helper}{bluetooth})); } } # Bluetooth Geräte bereinigen! my $echohash; foreach my $DeviceID (sort keys %{$modules{$hash->{TYPE}}{defptr}}) { $echohash = $modules{$hash->{TYPE}}{defptr}{$DeviceID}; foreach my $BluetoothDevice (sort keys %{$modules{$hash->{TYPE}}{defptr}{$DeviceID}{READINGS}}) { if ( grep( /^$BluetoothDevice$/, @btdevices ) ) { #Log3 $name, 5, "DEBUG $name [bluetoothstate] FOUND Device=" . $DeviceID . " Reading=" . $BluetoothDevice if ($BluetoothDevice =~ m/bluetooth_/ ); } else { readingsDelete($echohash, $BluetoothDevice ) if ($BluetoothDevice =~ m/bluetooth_/ ); } } } } elsif($msgtype eq "getdnd") { foreach my $device (@{$json->{doNotDisturbDeviceStatusList}}) { if(defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}})) { next if (echodevice_getModel($device->{deviceType}) eq "Reverb"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Multiroom"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Stereopaar"); next if (echodevice_getModel($device->{deviceType}) eq "Sonos Display"); next if (echodevice_getModel($device->{deviceType}) eq "Sonos One"); next if (echodevice_getModel($device->{deviceType}) eq "Sonos Beam"); next if (echodevice_getModel($device->{deviceType}) eq "unbekannt"); my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; readingsBeginUpdate($echohash); readingsBulkUpdate($echohash, "dnd", $device->{enabled}?"on":"off", 1); readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "alarmvolume") { foreach my $device (@{$json->{deviceNotificationStates}}) { if(defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}})) { next if (echodevice_getModel($device->{deviceType}) eq "Reverb"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Multiroom"); next if (echodevice_getModel($device->{deviceType}) eq "Sonos Display"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Stereopaar"); next if (echodevice_getModel($device->{deviceType}) eq "unbekannt"); my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; readingsBeginUpdate($echohash); readingsBulkUpdate($echohash, "volume_alarm", $device->{volumeLevel}, 1)if(defined($device->{volumeLevel})); readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "dndset") { readingsBeginUpdate($hash); readingsBulkUpdate($hash, "dnd", $json->{enabled}?"on":"off", 1) if(defined($json->{enabled})); readingsEndUpdate($hash,1); } elsif($msgtype eq "tunein") { } elsif($msgtype eq "ttstunein") { InternalTimer(gettimeofday() + $hash->{helper}{lasttuneindelay} , "echodevice_StartLastMedia" , $hash, 0); Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] Setze echodevice_StartLastMedia Timer in " . $hash->{helper}{lasttuneindelay} . " Sekunden."; } elsif($msgtype eq "tts_translate") { my $TTS_Translate_Result = "ERROR no result!!!!"; my $TTS_Translate_ResultTags = ""; if (defined($json->{d})) { if (defined($json->{d}{result})) { $TTS_Translate_Result = $json->{d}{result}; } if (defined($json->{d}{resultNoTags})) { $TTS_Translate_ResultTags = $json->{d}{resultNoTags}; } } # Prüfen ob Text erkannt wurde if (index($TTS_Translate_Result,'=0 && $TTS_Translate_ResultTags ne "" ) { # Kein Text erkannt! my @TTS_ResultArray = split("\n",$TTS_Translate_ResultTags); $TTS_Translate_Result = substr($TTS_ResultArray[1], 5,index($TTS_ResultArray[1],",")-5 ); } # Verzeichnis anlegen my $filedir = "cache"; mkdir($filedir, 0777) unless(-d $filedir ); # Text in Datei zwischenspeichern open(FH, ">$filedir/$name.html"); print FH $TTS_Translate_Result; close(FH); # Reading aktualisieren. readingsBeginUpdate($hash); readingsBulkUpdate($hash, "tts_translate_result",$TTS_Translate_Result , 1); readingsEndUpdate($hash,1); # Datei wieder auslesen open FILE, "$filedir/$name.html" or do { return; }; chomp(my $TTS_Translate_Result = ); close FILE; # TTS Nachricht abspielen if ($TTS_Translate_Result ne "ERROR no result!!!!") { echodevice_Amazon($hash,$TTS_Translate_Result,$msgtype); } } elsif($msgtype eq "wakeword") { foreach my $device (@{$json->{wakeWords}}) { if(defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}})) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; readingsBeginUpdate($echohash); #readingsBulkUpdate($echohash, "active", $device->{active}?"true":"false", 1) if(defined($device->{active})); readingsBulkUpdate($echohash, "wakeword", $device->{wakeWord}, 1) if(defined($device->{wakeWord})); #readingsBulkUpdate($echohash, "midfield", $device->{midFieldState}, 1) if(defined($device->{midFieldState})); readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "getdevicesettings") { foreach my $device (@{$json->{devicePreferences}}) { if(defined($modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}})) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{deviceSerialNumber}}; next if (echodevice_getModel($device->{deviceType}) eq "Echo Multiroom"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Stereopaar"); next if (echodevice_getModel($device->{deviceType}) eq "unbekannt"); readingsBeginUpdate($echohash); readingsBulkUpdate($echohash, "microphone", $device->{notificationEarconEnabled}?"false":"true", 1) if(defined($device->{notificationEarconEnabled})); readingsBulkUpdate($echohash, "deviceAddress", $device->{deviceAddress}, 1) if(defined($device->{deviceAddress})); readingsBulkUpdate($echohash, "timeZoneId", $device->{timeZoneId}, 1) if(defined($device->{timeZoneId})); readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "getisonline") { foreach my $device (@{$json->{devices}}) { if(defined($modules{$hash->{TYPE}}{defptr}{$device->{serialNumber}})) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$device->{serialNumber}}; next if (echodevice_getModel($device->{deviceType}) eq "Echo Multiroom"); next if (echodevice_getModel($device->{deviceType}) eq "Echo Stereopaar"); next if (echodevice_getModel($device->{deviceType}) eq "Sonos Display"); next if (echodevice_getModel($device->{deviceType}) eq "unbekannt"); readingsBeginUpdate($echohash); readingsBulkUpdate($echohash, "online", $device->{online}?"true":"false", 1) if(defined($device->{online})); readingsEndUpdate($echohash,1); } } } elsif($msgtype eq "conversations") { my $return = ''; $return .= "

Conversations:

"; $return .= ""; my $conversations_date = ""; my $conversations_msg = ""; if(!defined($json->{conversations})) {} elsif(ref($json->{conversations}) ne "ARRAY") {} else{ foreach my $conversation (@{$json->{conversations}}) { if(defined($conversation->{lastMessage}{payload}{text})){ $conversations_date = $conversation->{lastMessage}{time}; $conversations_msg = substr($conversation->{lastMessage}{payload}{text},0,32); $conversations_msg =~ s/[\x0A\x0D]//g; } else { $conversations_msg = "no previous messages"; $conversations_date = "no date"; } $return .= ""; } } $return .= "
IDDateMessage
".$conversation->{conversationId}."   ".$conversations_date."   ".$conversations_msg."   
"; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "devices" || $msgtype eq "autocreate_devices") { my $autocreated = 0; my $autocreate = 0; my $isautocreated = 0; $autocreate=1 if($msgtype eq "autocreate_devices"); my $return = ''; $return .= "

Devices:

"; $return .= ""; if(!defined($json->{devices})) {} elsif (ref($json->{devices}) ne "ARRAY") {} else { foreach my $device (@{$json->{devices}}) { #next if($device->{deviceFamily} eq "UNKNOWN"); #next if($device->{deviceFamily} eq "FIRE_TV"); #next if($device->{deviceFamily} =~ /AMAZON/); $isautocreated = 0; if($autocreate && ($device->{deviceFamily} eq "UNKNOWN" || $device->{deviceFamily} eq "FIRE_TV" || $device->{deviceFamily} eq "TABLET" || $device->{deviceFamily} eq "ECHO" || $device->{deviceFamily} eq "KNIGHT" || $device->{deviceFamily} eq "THIRD_PARTY_AVS_SONOS_BOOTLEG" || $device->{deviceFamily} eq "THIRD_PARTY_AVS_MEDIA_DISPLAY" || $device->{deviceFamily} eq "WHA" || $device->{deviceFamily} eq "ROOK" )) { if( defined($modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}) ) { Log3 $name, 4, "[$name] [echodevice_Parse] device '$device->{serialNumber}' already defined"; if (AttrVal($name, "autocreate_refresh", 0) == 1) { my $devicehash = $modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}; print (fhem( "attr " . $devicehash->{NAME} ." alias " .$device->{accountName} )) if( defined($device->{accountName}) ); print (fhem( "attr " . $devicehash->{NAME} ." icon echo" ))if (-e "././www/images/fhemSVG/echo.svg"); } } else { $isautocreated = 1; my $devname = "ECHO_".$device->{serialNumber}; my $define= "$devname echodevice ".$device->{deviceType}." ".$device->{serialNumber}; Log3 $name, 3, "[$name] [echodevice_Parse] create new device '$devname'"; my $cmdret= CommandDefine(undef,$define); if($cmdret) { Log3 $name, 1, "[$name] [echodevice_Parse] Autocreate: An error occurred while creating device for serial '$device->{serialNumber}': $cmdret"; } else { $cmdret= CommandAttr(undef,"$devname alias ".$device->{accountName}) if( defined($device->{accountName}) ); $cmdret= CommandAttr(undef,"$devname icon echo" )if (-e "././www/images/fhemSVG/echo.svg"); $cmdret= CommandAttr(undef,"$devname IODev $name"); $cmdret= CommandAttr(undef,"$devname room Amazon"); $autocreated++; } $hash->{helper}{VERSION} = $device->{softwareVersion} if(!defined($hash->{helper}{VERSION})); $hash->{helper}{".CUSTOMER"} = $device->{deviceOwnerCustomerId} if(!defined($hash->{helper}{".CUSTOMER"})); $hash->{helper}{".SERIAL"} = $device->{serialNumber} if(!defined($hash->{helper}{".SERIAL"})); $hash->{helper}{DEVICETYPE} = $device->{deviceType} if(!defined($hash->{helper}{DEVICETYPE})); } } elsif($device->{deviceFamily} eq "ECHO") { Log3 $name, 4, "[echodevice_GetSettings] SET 2 DEF=" . $hash->{DEF} . " TYPE=".$device->{deviceType} . 'SN Hash=' . $hash->{helper}{".SERIAL"} . ' SN Result=' . $device->{serialNumber} ; $hash->{helper}{VERSION} = $device->{softwareVersion} if(!defined($hash->{helper}{VERSION})); $hash->{helper}{".CUSTOMER"} = $device->{deviceOwnerCustomerId} if(!defined($hash->{helper}{".CUSTOMER"})); $hash->{helper}{".SERIAL"} = $device->{serialNumber} if(!defined($hash->{helper}{".SERIAL"})); $hash->{helper}{DEVICETYPE} = $device->{deviceType} if(!defined($hash->{helper}{DEVICETYPE})); if( defined($modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}) ) { my $devicehash = $modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}; } } if ($isautocreated == 0) { $return .= ""; } else { $return .= ""; } } foreach my $device (@{$json->{devices}}) { my $devicehash = $modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}; next if( !defined($devicehash) ); $devicehash->{model} = echodevice_getModel($device->{deviceType});#$device->{deviceType}; readingsBeginUpdate($devicehash); readingsBulkUpdate($devicehash, "model", $devicehash->{model}, 1); readingsBulkUpdate($devicehash, "presence", ($device->{online}?"present":"absent"), 1); #readingsBulkUpdate($devicehash, "state", "absent", 1) if(!$device->{online}); readingsBulkUpdate($devicehash, "version", $device->{softwareVersion}, 1); readingsEndUpdate($devicehash,1); $devicehash->{helper}{".SERIAL"} = $device->{serialNumber}; $devicehash->{helper}{DEVICETYPE} = $device->{deviceType}; $devicehash->{helper}{NAME} = $device->{accountName}; $devicehash->{helper}{FAMILY} = $device->{deviceFamily}; $devicehash->{helper}{VERSION} = $device->{softwareVersion}; $devicehash->{helper}{".CUSTOMER"} = $device->{deviceOwnerCustomerId}; if ($device->{deviceFamily} eq "ECHO" || $device->{deviceFamily} eq "KNIGHT") { Log3 $name, 4, "[echodevice_GetSettings] SET 4 DEF=" . $devicehash->{DEF} . " TYPE=".$device->{deviceType} . 'SN Hash=' . $devicehash->{helper}{".SERIAL"} . ' SN Result=' . $device->{serialNumber} ; $hash->{helper}{".SERIAL"} = $device->{serialNumber}; $hash->{helper}{DEVICETYPE} = $device->{deviceType}; } } readingsSingleUpdate ($hash, "autocreate_devices", "found: ".$autocreated, 0 ) if($msgtype eq "autocreate_devices"); $return .= "
SerialFamilyDevicetypeName
".$device->{serialNumber}."   ".$device->{deviceFamily}."   ".$device->{deviceType}."   ".$device->{accountName}."   
*".$device->{serialNumber}."   ".$device->{deviceFamily}."   ".$device->{deviceType}."   ".$device->{accountName}."   
"; $return .= "

* ".$autocreated." devices created

" if($msgtype eq "autocreate_devices"); $return .= ""; } $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "devicesstate") { if (ref($json) eq "HASH") { if(!defined($json->{devices})) {} elsif (ref($json->{devices}) ne "ARRAY") {} else { foreach my $device (@{$json->{devices}}) { my $devicehash = $modules{$hash->{TYPE}}{defptr}{"$device->{serialNumber}"}; next if( !defined($devicehash) ); $devicehash->{model} = echodevice_getModel($device->{deviceType});#$device->{deviceType}; Log3 $name, 4, "[echodevice_GetSettings] SET 6 DEF=" . $devicehash->{DEF} . " TYPE=".$device->{deviceType}. 'SN Hash=' . $devicehash->{helper}{".SERIAL"} . ' SN Result=' . $device->{serialNumber} . " Model=" .$devicehash->{model} ; readingsBeginUpdate($devicehash); readingsBulkUpdate($devicehash, "model", $devicehash->{model}, 1); readingsBulkUpdate($devicehash, "presence", ($device->{online}?"present":"absent"), 1); #readingsBulkUpdate($devicehash, "state", "absent", 1) if(!$device->{online}); readingsBulkUpdate($devicehash, "version", $device->{softwareVersion}, 1); readingsEndUpdate($devicehash,1); $devicehash->{helper}{".SERIAL"} = $device->{serialNumber}; $devicehash->{helper}{DEVICETYPE} = $device->{deviceType}; $devicehash->{helper}{NAME} = $device->{accountName}; $devicehash->{helper}{FAMILY} = $device->{deviceFamily}; $devicehash->{helper}{VERSION} = $device->{softwareVersion}; $devicehash->{helper}{".CUSTOMER"} = $device->{deviceOwnerCustomerId}; if ($device->{deviceFamily} eq "ECHO" || $device->{deviceFamily} eq "KNIGHT") { Log3 $name, 4, "[echodevice_GetSettings] SET 5 DEF=" . $devicehash->{DEF} . " TYPE=".$device->{deviceType}. 'SN Hash=' . $devicehash->{helper}{".SERIAL"} . ' SN Result=' . $device->{serialNumber} ; $hash->{helper}{".SERIAL"} = $device->{serialNumber}; $hash->{helper}{DEVICETYPE} = $device->{deviceType}; } } } } else { Log3 $name, 5, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] WRONG JSON Type Type=" . ref($json); } } elsif($msgtype eq "searchtunein") { my $tuneincount = 0; my $return = ''; $return .= "

TuneIn:

"; $return .= ""; if (!defined($json->{browseList})) {} elsif (ref($json->{browseList}) ne "ARRAY") {} else { # Play on Device foreach my $result (@{$json->{browseList}}) { next if(!$result->{available}); next if($result->{contentType} ne "station"); $tuneincount ++; $return .= ""; } } $return .= "
IDNameStart
".$result->{id}."   ".$result->{name}."   
"; $return .= "

".$tuneincount. " tunein IDs found

"; $return .= ""; $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "searchtracks") { my $trackcount = 0; my $return = ''; my $tracktitle = ""; $return .= "

Tracks:

"; $return .= ""; if (!defined($json->{playlist}{entryList})) {} elsif (ref($json->{playlist}{entryList}) ne "ARRAY") {} else { foreach my $track (@{$json->{playlist}{entryList}}) { if(defined($track->{metadata}{title})){$tracktitle = $track->{metadata}{title};} else {$tracktitle= "unknown title";} $trackcount ++; $return .= ""; } } $return .= "
IDTitle
".$track->{trackId}."   ".$tracktitle."   
"; $return .= "

".$trackcount." track IDs found

"; $return .= ""; $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "primeplayeigene_Albums" || $msgtype eq "primeplayeigene_Tracks" || $msgtype eq "primeplayeigene_Artists" ) { my $querytype = substr($msgtype,16); my $albumcount = 0; my $artistcolum = ""; $artistcolum = "Title   ID   " if ($msgtype eq "primeplayeigene_Tracks" ) ; my $return = ''; $return .= "

$querytype:

"; $return .= "$artistcolum"; if (!defined($json->{selectItemList})) {} elsif (ref($json->{selectItemList}) ne "ARRAY") {} else { # Play on Device foreach my $result (@{$json->{selectItemList}}) { #next if(!$result->{available}); #next if($result->{contentType} ne "station"); $albumcount ++; if ($msgtype eq "primeplayeigene_Tracks" ) { $return .= ""; } else { $return .= ""; } } } $return .= "
Artist   Albumname   Tracks   Start
".$result->{metadata}{albumArtistName}."   ".$result->{metadata}{albumName}."   ".$result->{metadata}{title}."   ".$result->{metadata}{objectId}."   1   
".$result->{metadata}{albumArtistName}."   ".$result->{metadata}{albumName}."   ".$result->{numTracks}."   {metadata}{albumName}).'>play   ' . "
"; $return .= "

".$albumcount. " ". lc($querytype) ." IDs found

"; $return .= ""; $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "getprimeplayeigeneplaylist" ) { my $playlistcount = 0; my $return = ''; $return .= "

Playlists:

"; $return .= ""; if (!defined($json->{playlists})) {} elsif (ref($json->{playlists}) ne "HASH") {} else { foreach my $result (sort keys %{$json->{playlists}}) { $playlistcount ++; $return .= ""; } } $return .= "
Name   ID   Tracks   Start
".$result."   ".$json->{playlists}{"$result"}[0]{playlistId}."   ".$json->{playlists}{"$result"}[0]{trackCount}."   play   ' . "
"; $return .= "

".$playlistcount. " playlist IDs found

"; $return .= ""; $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "getcards") { my $return = ''; $return .= "

Actions:

"; $return .= ""; if (!defined($json->{cards})) {} elsif (ref($json->{cards}) ne "ARRAY") {} else { foreach my $cards (@{$json->{cards}}) { my $devicehash = $modules{$hash->{TYPE}}{defptr}{"$cards->{sourceDevice}{serialNumber}"}; my $devicename = $devicehash->{NAME}; my $VoiceText = $cards->{playbackAudioAction}{mainText}; $VoiceText = "No voice command detected. Action was started by the Alexa app." if ($VoiceText eq ""); if ($devicename ne ""){ if (AttrVal( $devicename, "alias", "none" ) ne "none") {$devicename = AttrVal( $devicename, "alias", "none" );} $return .= ""; } } } $return .= "
TitleSubtitleVoiceDevice
".$cards->{title}."   ".$cards->{subtitle}."   ".$VoiceText."   ".$devicename."   
"; $return .= ""; $return =~ s/'/'/g; asyncOutput( $param->{CL}, $return ); } elsif($msgtype eq "getsettingstraffic") { readingsBeginUpdate($hash); $hash->{helper}{"getsettingstraffic"} = (); if ((!defined($json->{origin}{label}))) { $hash->{helper}{"getsettingstraffic"}{from} = ""; readingsBulkUpdate ($hash, "config_address_from", "-", 1); } else{ $hash->{helper}{"getsettingstraffic"}{from} = encode_utf8($json->{origin}{label}); readingsBulkUpdate ($hash, "config_address_from", $json->{origin}{label}, 1); } if ((!defined($json->{destination}{label}))) { $hash->{helper}{"getsettingstraffic"}{to} = ""; readingsBulkUpdate ($hash, "config_address_to", "-", 1); } else{ $hash->{helper}{"getsettingstraffic"}{to} = encode_utf8($json->{destination}{label}); readingsBulkUpdate ($hash, "config_address_to", $json->{destination}{label}, 1); } if (ref($json->{waypoints}) ne "ARRAY") { $hash->{helper}{"getsettingstraffic"}{between} = ""; readingsBulkUpdate ($hash, "config_address_between", "-", 1); } else{ if ($json->{waypoints}[0]{label} eq "") { $hash->{helper}{"getsettingstraffic"}{between} = ""; readingsBulkUpdate ($hash, "config_address_between", "-", 1); } else { $hash->{helper}{"getsettingstraffic"}{between} = encode_utf8($json->{waypoints}[0]{label}); readingsBulkUpdate ($hash, "config_address_between", $json->{waypoints}[0]{label}, 1); } } readingsEndUpdate ($hash,1); } elsif($msgtype eq "address") { my $addresscount = 0; my $return = ''; $return .= "

Adressen:

"; $return .= ""; if (!defined($json->{suggestionList})) {} elsif (ref($json->{suggestionList}) ne "ARRAY") {} else { # Play on Device foreach my $result (@{$json->{suggestionList}}) { #next if(!$result->{available}); #next if($result->{contentType} ne "station"); $addresscount ++; $return .= ""; } } $return .= "
Adresse   Start   Z.-Ziel   Ziel
".$result->{internalLabel}."                
"; $return .= "

".$addresscount. " Adressen gefunden

"; $return .= ""; $return =~ s/'/'/g; if ($addresscount == 0) { asyncOutput( $param->{CL}, "Die Adresse konnte nicht gefunden werden!" ); } else { asyncOutput( $param->{CL}, $return ); } } elsif($msgtype eq "customer-history-records") { my $addresscount = 0; my $Voicetext; my $return = ''; $return .= "

Sprachaufnahmen-Verlauf:

"; $return .= ""; if (!defined($json->{customerHistoryRecords})) {} elsif (ref($json->{customerHistoryRecords}) ne "ARRAY") {} else { # Play on Device foreach my $result (@{$json->{customerHistoryRecords}}) { foreach my $Voicerecords (@{$result->{voiceHistoryRecordItems}}) { next if ($Voicerecords->{recordItemType} eq "TTS_REPLACEMENT_TEXT") ; my ($S, $M, $H, $d, $m, $Y) = localtime($result->{timestamp} / 1000); $m += 1; $Y += 1900; my $dt = sprintf("%02d.%02d.%04d %02d:%02d:%02d", $d,$m, $Y, $H, $M, $S); $addresscount ++; if ($Voicerecords->{transcriptText} eq "") {$Voicetext = "Falsch erkannt";} else {$Voicetext=$Voicerecords->{transcriptText};} my $MP3Filename = $Voicerecords->{recordItemKey} . ".mp3"; $MP3Filename =~ s/#//g; $return .= ""; if ((-e $FW_dir . "/echodevice/VoiceRecords/". $MP3Filename)) { Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] Use EXIST MP3File = " . $MP3Filename ; } else { Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] Download MP3File = " . $MP3Filename ; #Header auslesen my $AmazonHeader; # Browser User Agent my $HeaderLanguage = AttrVal($name,"browser_language","de,en-US;q=0.7,en;q=0.3"); my $UserAgent = AttrVal($name,"browser_useragent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"); if (AttrVal($name,"browser_useragent_random",0) == 1) { $UserAgent = join('', map{('a'..'z','A'..'Z',0..9)[rand 62]} 0..20); } if($hash->{model} eq "ACCOUNT") {$AmazonHeader = "User-Agent: ". $UserAgent ."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nCookie:".$hash->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} else {$AmazonHeader = "User-Agent: ". $UserAgent ."\r\nAccept-Language: " . $HeaderLanguage . "\r\nDNT: 1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nCookie:".$hash->{IODev}->{helper}{".COOKIE"}."\r\ncsrf: ".$hash->{IODev}->{helper}{".CSRF"}."\r\nContent-Type: application/json; charset=UTF-8";} my $params = { url => "https://www.amazon.de/alexa-privacy/apd/rvh/audio?uid=" . urlEncode($Voicerecords->{utteranceId}), header => $AmazonHeader, timeout => 10, noshutdown => 1, keepalive => 1, method => "GET", CL => $param->{CL}, hash => $hash, type => $MP3Filename, httpversion => $param->{httpversion}, callback => \&echodevice_AmazonVoiceMP3 }; HttpUtils_NonblockingGet($params); } } } } $return .= "
Datum   Echoname   Aufname
" . $dt . "         ". $result->{device}{"deviceName"} . '     ' . '' . "

" . $Voicetext . "

   
"; $return .= "

".$addresscount. " Sprachaufnahmen gefunden

"; $return .= ""; $return =~ s/'/'/g; if ($addresscount == 0) { asyncOutput( $param->{CL}, "Es konnten keine Sprachaufnahmen gefunden werden!" ); } else { asyncOutput( $param->{CL}, $return ); } } else { Log3 $name, 4, "[$name] [echodevice_Parse] [$msgtype] [$msgnumber] json for unknown message \n".Dumper(echodevice_anonymize($hash, $json)); } } ########################## sub echodevice_GetSettings($) { my ($hash) = @_; my $name = $hash->{NAME}; my $nextupdate = int(AttrVal($name,"intervalsettings",60)); my $ConnectState = ""; return if($hash->{model} eq "unbekannt"); # ECHO Device disable if (AttrVal($name,"disable",0) == 1) { RemoveInternalTimer($hash, "echodevice_GetSettings"); InternalTimer(gettimeofday() + $nextupdate, "echodevice_GetSettings", $hash, 0); return; } # ECHO am Account registrierern if($hash->{model} ne "ACCOUNT") { $hash->{IODev}->{helper}{"DEVICETYPE"} = $hash->{helper}{"DEVICETYPE"} ; $hash->{IODev}->{helper}{".SERIAL"} = $hash->{helper}{".SERIAL"}; $hash->{IODev}->{helper}{"VERSION"} = $hash->{helper}{"VERSION"}; } if($hash->{model} eq "ACCOUNT") {$ConnectState = $hash->{STATE}} else {$ConnectState = $hash->{IODev}->{STATE}} Log3 $name, 5, "[$name] [echodevice_GetSettings] start refresh settings" ; if ($ConnectState eq "connected") { if($hash->{model} eq "ACCOUNT") { echodevice_SendCommand($hash,"getnotifications",""); echodevice_SendCommand($hash,"alarmvolume",""); echodevice_SendCommand($hash,"bluetoothstate",""); echodevice_SendCommand($hash,"getdnd",""); echodevice_SendCommand($hash,"wakeword",""); echodevice_SendCommand($hash,"listitems_task","TASK"); echodevice_SendCommand($hash,"listitems_shopping","SHOPPING_ITEM"); echodevice_SendCommand($hash,"getdevicesettings",""); echodevice_SendCommand($hash,"getisonline",""); echodevice_SendCommand($hash,"devices","") if ($hash->{helper}{VERSION} eq ""); echodevice_SendCommand($hash,"namedListsIDs","") if ($hash->{helper}{SHOPPING_LIST_ID} eq ""); echodevice_SendCommand($hash,"devicesstate",""); echodevice_SendCommand($hash,"account","") if ($hash->{helper}{".COMMSID"} eq ""); echodevice_SendLoginCommand($hash,"cookielogin6",""); # Voice Reading my $IntervalVoice = int(AttrVal($name,"intervalvoice",999999)); $hash->{helper}{echodevice_refreshvoice} = 0 if ($hash->{helper}{echodevice_refreshvoice} eq ""); $hash->{helper}{echodevice_refreshvoice_lastdate} = time() if ($hash->{helper}{echodevice_refreshvoice_lastdate} eq ""); if ($hash->{helper}{echodevice_refreshvoice} == 0 && $IntervalVoice != 999999) { Log3 $name, 5, "[$name] [echodevice_GetSettings] refresh voice command IntervalVoice=$IntervalVoice "; $hash->{helper}{echodevice_refreshvoice} = 1; InternalTimer(gettimeofday() + $IntervalVoice , "echodevice_refreshvoice", $hash, 0); } elsif ($hash->{helper}{echodevice_refreshvoice} == 1 && time() - $hash->{helper}{echodevice_refreshvoice_lastdate} >= $IntervalVoice + 20 ){ Log3 $name, 5, "[$name] [echodevice_GetSettings] restart refresh voice command IntervalVoice=$IntervalVoice DIFF " . (time() - $hash->{helper}{echodevice_refreshvoice_lastdate}) . ">=" . ($IntervalVoice + 20) ; RemoveInternalTimer($hash, "echodevice_refreshvoice"); InternalTimer(gettimeofday() + $IntervalVoice , "echodevice_refreshvoice", $hash, 0); } else { Log3 $name, 5, "[$name] [echodevice_GetSettings] refresh voice command"; echodevice_SendCommand($hash,"activities",""); } echodevice_SendCommand($hash,"getbehavior",""); echodevice_SendCommand($hash,"getsettingstraffic",""); } else { Log3 $name, 4, "[echodevice_GetSettings] Devicename=$name DEF=" . $hash->{DEF} . " DEVICETYPE=" . $hash->{helper}{DEVICETYPE} . " Model=" . $hash->{model}; if ($hash->{model} eq "Reverb" || $hash->{model} eq "Sonos One" || $hash->{model} eq "Sonos Beam") { if ($hash->{IODev}{STATE} eq "connected") { readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", $hash->{IODev}{STATE}, 1); readingsEndUpdate($hash,1); } else {$nextupdate = 10;} } else { if (ReadingsVal($name, "playStatus", "off") ne "paused") { my $CalcInterval = int(ReadingsVal($name, "progresslen", 0)) - (int(ReadingsVal($name, "progress", 0)) + $nextupdate); if ($CalcInterval < 0) {} elsif ($CalcInterval < ($nextupdate -1) ){$nextupdate = $CalcInterval + 4;} Log3( $name, 4, "[$name] [echodevice_GetSettings] Timer CINTERVAL = " . $CalcInterval); } if ($hash->{IODev}{STATE} eq "connected") { echodevice_SendCommand($hash,"player",""); echodevice_SendCommand($hash,"media",""); } else { $nextupdate = 10; } } # Readings löschen readingsDelete($hash, "COOKIE_STATE") ; readingsDelete($hash, "COOKIE_TYPE") ; readingsDelete($hash, "2FACode") ; readingsDelete($hash, "BrowserUserAgent") ; } # Readings Bereinigung readingsDelete($hash, "active") if (ReadingsVal($name , "active", "none") ne "none"); Log3( $name, 4, "[$name] [echodevice_GetSettings] Timer INTERVAL = " . $nextupdate); } else { Log3 $name, 5, "[$name] [echodevice_GetSettings] unknown state / state = $ConnectState" ; } RemoveInternalTimer($hash, "echodevice_GetSettings"); InternalTimer(gettimeofday() + $nextupdate, "echodevice_GetSettings", $hash, 0); return; } ########################## sub echodevice_FirstStart($) { my ($hash) = @_; my $name = $hash->{NAME}; my $CookieDevice = ""; readingsSingleUpdate ($hash, "version", $ModulVersion ,0); readingsSingleUpdate ($hash, "autocreate_devices", "stop", 0 ); # Migration NPM von Version älter 0.0.55 if ((ReadingsVal($name, "COOKIE_TYPE", "unbekannt") eq "READING_NPM" || ReadingsVal($name, "COOKIE_TYPE", "unbekannt") eq "NPM_Login" ) && ReadingsVal($name, "amazon_refreshtoken", "unbekannt") eq "vorhanden" && $hash->{DEF} ne "xxx\@xxx.xx xxx") { $hash->{DEF} = "xxx\@xxx.xx xxx"; $hash->{LOGINMODE} = "NPM"; } if(AttrVal( $name, "cookie", "none" ) ne "none") { readingsSingleUpdate ($hash, "COOKIE_TYPE", "ATTRIBUTE" ,0); $hash->{helper}{".COOKIE"} = AttrVal( $name, "cookie", "none" ); $hash->{helper}{".COOKIE"} =~ s/Cookie: //g; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/; $hash->{helper}{".CSRF"} = $1; readingsSingleUpdate ($hash, ".COOKIE", $hash->{helper}{".COOKIE"} ,0); # Cookie als READING festhalten! } elsif (ReadingsVal( $name, ".COOKIE", "none" ) ne "none") { $hash->{helper}{".COOKIE"} = ReadingsVal( $name, ".COOKIE", "none" ); # Prüfen ob es sich um ein NPM Login handelt if (index($hash->{helper}{".COOKIE"}, "{") != -1) { # NPM Login erkannt readingsSingleUpdate ($hash, "COOKIE_TYPE", "READING_NPM" ,0); $hash->{helper}{".COOKIE"} =~ /"localCookie":".*session-id=(.*)","?/; $hash->{helper}{".COOKIE"} = "session-id=" . $1; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/ if(defined($hash->{helper}{".COOKIE"})); $hash->{helper}{".CSRF"} = $1 if(defined($hash->{helper}{".COOKIE"})); } else { # OLD Style readingsSingleUpdate ($hash, "COOKIE_TYPE", "READING" ,0); $hash->{helper}{".COOKIE"} =~ s/Cookie: //g; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/; $hash->{helper}{".CSRF"} = $1; } } else { readingsSingleUpdate ($hash, "COOKIE_TYPE", "NEW" ,0); } Log3 $name, 4, "[$name] [echodevice_FirstStart] COOKIE = " . $hash->{helper}{".COOKIE"}; Log3 $name, 4, "[$name] [echodevice_FirstStart] COOKIE_TYPE = " . ReadingsVal( $name, "COOKIE_TYPE", "none" ); $hash->{STATE} = "INITIALIZED"; echodevice_CheckAuth($hash); if(defined($hash->{helper}{".COOKIE"})) { echodevice_SendCommand($hash,"devices",""); echodevice_SendCommand($hash,"account",""); } # Alte Readingsbereinigen readingsDelete($hash, "COOKIE"); # Migration aws_secret_key & aws_access_key if (ReadingsVal($name , "aws_access_key", "none") ne "none") { readingsSingleUpdate ($hash, ".aws_access_key", ReadingsVal($name , lc("AWS_Access_Key"), "none") ,0); readingsDelete($hash, "aws_access_key"); } if (ReadingsVal($name , lc("aws_secret_key"), "none") ne "none") { readingsSingleUpdate ($hash, ".aws_secret_key", ReadingsVal($name , lc("aws_secret_key"), "none") ,0); readingsDelete($hash, "aws_secret_key"); } # Login Timer setzen RemoveInternalTimer($hash, "echodevice_LoginStart"); InternalTimer(gettimeofday() + 10, "echodevice_LoginStart", $hash, 0); } sub echodevice_LoginStart($) { my ($hash) = @_; my $name = $hash->{NAME}; my $nextupdate = int(AttrVal($name,"intervallogin",60)); my $npm_refresh_intervall = int(AttrVal($name,"npm_refresh_intervall",6000)); my $DeviceState = ""; # Bestehenden Timer löschen RemoveInternalTimer($hash, "echodevice_LoginStart"); # ECHO Device disable if (AttrVal($name,"disable",0) == 1) { echodevice_setState($hash,"disable"); $DeviceState = "disable"; } else { $DeviceState = "enable"; if ($hash->{STATE} ne "connected" && $hash->{STATE} ne "connected but loginerror") { if ($hash->{STATE} eq "disable") { echodevice_setState($hash,"connected"); } else { if (index(ReadingsVal($name , ".COOKIE", "0"), "{") != -1) { echodevice_SendLoginCommand($hash,"cookielogin6","");# if($hash->{LOGINMODE} eq "NORMAL"); } else { if($hash->{LOGINMODE} eq "NORMAL") { Log3 $name, 4, "[$name] [echodevice_LoginStart] start login"; $hash->{helper}{RUNLOGIN} = 0; echodevice_SendLoginCommand($hash,"cookielogin1","") if(!defined($attr{$name}{cookie})); } } } } elsif ($hash->{STATE} eq "connected but loginerror") { Log3 $name, 3, "[$name] [echodevice_LoginStart] connected but loginerror"; echodevice_SendLoginCommand($hash,"cookielogin6","");# if($hash->{LOGINMODE} eq "NORMAL"); } else { if (index(ReadingsVal($name , ".COOKIE", "0"), "{") != -1) { # Refresh COOKIE if (ReadingsAge($name,'.COOKIE',0) > $npm_refresh_intervall) { Log3 $name, 3, "[$name] [echodevice_LoginStart] Alter COOKIE=" . ReadingsAge($name,'.COOKIE',0) ."/$npm_refresh_intervall Refresh Cookie!"; echodevice_NPMLoginRefresh($hash); } else { Log3 $name, 4, "[$name] [echodevice_LoginStart] Alter COOKIE=" . ReadingsAge($name,'.COOKIE',0) . "/$npm_refresh_intervall"; } } } } InternalTimer(gettimeofday() + $nextupdate, "echodevice_LoginStart", $hash, 0); Log3 $name, 4, "[$name] [echodevice_LoginStart] [$DeviceState] set next internal timer start in $nextupdate seconds."; } sub echodevice_CheckAuth($) { my ($hash) = @_; my $name = $hash->{NAME}; return undef if($hash->{model} ne "ACCOUNT"); # Erneut Login ausführen wenn Cookie nicht gesetzt wurde! if(!defined($hash->{helper}{".COOKIE"})) { echodevice_SendLoginCommand($hash,"cookielogin1","") if($hash->{LOGINMODE} eq "NORMAL"); } else { echodevice_SendLoginCommand($hash,"cookielogin6",""); } return undef; } sub echodevice_ParseAuth($$$) { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $msgtype = $param->{type}; my $nextupdate = int(AttrVal($name,"intervallogin",60)); Log3 $name, 4, "[$name] [echodevice_ParseAuth] [$msgtype] "; Log3 $name, 5, "[$name] [echodevice_ParseAuth] [$msgtype] DATA Dumper=" . Dumper(echodevice_anonymize($hash, $data)); # HTML Informationen mit schreiben if (AttrVal($name,"browser_save_data",0) == 1) { #Verzeichnis echodevice anlegen mkdir($FW_dir . "/echodevice", 0777) unless(-d $FW_dir . "/echodevice" ); mkdir($FW_dir . "/echodevice/results", 0777) unless(-d $FW_dir . "/echodevice/results" ); # Eventuell vorhandene Datei löschen my $HTMLFilename = $name . "_" . $msgtype . ".html"; my $HeaderFilename = $name . "_" . $msgtype . "_header.html"; if ((-e $FW_dir . "/echodevice/results/". $HTMLFilename)) {unlink $FW_dir . "/echodevice/results/".$HTMLFilename} if ((-e $FW_dir . "/echodevice/results/". $HeaderFilename)) {unlink $FW_dir . "/echodevice/results/".$HeaderFilename} # Datei anlegen open(FH, ">$FW_dir/echodevice/results/$HTMLFilename"); print FH $data; close(FH); # Datei anlegen open(FH, ">$FW_dir/echodevice/results/$HeaderFilename"); print FH $param->{httpheader}; close(FH); } if($err){ echodevice_LostConnect($hash,"connection error = $err"); return undef; } if($data =~ /cookie is missing/) { echodevice_LostConnect($hash,"connection error = cookie is missing"); return undef; } my $json = eval { JSON->new->utf8(0)->decode($data) }; if($@) { echodevice_LostConnect($hash,"JSON error = no content"); return undef; } if($json->{authentication}{authenticated}){ if ($hash->{helper}{".LOGINERROR"} >= 1) { Log3 $name, 3, "[$name] [echodevice_ParseAuth] reset loginerror from " . $hash->{helper}{".LOGINERROR"} . " to 0" ; $hash->{helper}{".LOGINERROR"} = 0; } echodevice_setState($hash,"connected"); readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", "connected", 1); readingsBulkUpdate($hash, "COOKIE_STATE", "OK", 1); readingsEndUpdate($hash,1); $hash->{helper}{".CUSTOMER"} = $json->{authentication}{customerId}; Log3 $name, 4, "[$name] [echodevice_ParseAuth] JSON OK = {authentication}{authenticated}"; } elsif($json->{authentication}) { echodevice_LostConnect($hash,"JSON error = {authentication}"); } return undef; } sub echodevice_LostConnect($$){ my ($hash,$State) = @_; my $name = $hash->{NAME}; $hash->{helper}{CMD_QUEUE} = (); # Query zurücksetzen Log3 $name, 4, "[$name] [echodevice_LostConnect] clear CMD_QUEUE" ; if ($hash->{helper}{".LOGINERROR"} >= 5) { $hash->{helper}{".LOGINERROR"} = 0; Log3 $name, 3, "[$name] [echodevice_LostConnect] $State / Generate new COOKIE! / set loginerror to 0" ; echodevice_setState($hash,"disconnected"); } else { $hash->{helper}{".LOGINERROR"} = $hash->{helper}{".LOGINERROR"} + 1; Log3 $name, 3, "[$name] [echodevice_LostConnect] $State / set loginerror to " . $hash->{helper}{".LOGINERROR"}; echodevice_setState($hash,"connected but loginerror"); } } ########################## # HELPER ########################## sub echodevice_getModel($){ my ($ModelNumber) = @_; my $name = $hash->{NAME}; if ($ModelNumber eq "AB72C64C86AW2" || $ModelNumber eq "Echo") {return "Echo";} elsif($ModelNumber eq "A3S5BH2HU6VAYF" || $ModelNumber eq "Echo Dot") {return "Echo Dot";} elsif($ModelNumber eq "A32DOYMUN6DTXA" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen3";} elsif($ModelNumber eq "A32DDESGESSHZA" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen3";} elsif($ModelNumber eq "A1RABVCI4QCIKC" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen3";} elsif($ModelNumber eq "A3RMGO6LYLH7YN" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen4";} elsif($ModelNumber eq "A2U21SRK4QGSE1" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen4";} elsif($ModelNumber eq "A2H4LV5GIZ1JFT" || $ModelNumber eq "Echo Dot") {return "Echo Dot Gen4 with Clock";} elsif($ModelNumber eq "A10A33FOX2NUBK" || $ModelNumber eq "Echo Spot") {return "Echo Spot";} elsif($ModelNumber eq "A1NL4BVLQ4L3N3" || $ModelNumber eq "Echo Show") {return "Echo Show";} elsif($ModelNumber eq "AWZZ5CVHX2CD" || $ModelNumber eq "Echo Show") {return "Echo Show Gen2";} elsif($ModelNumber eq "AIPK7MM90V7TB" || $ModelNumber eq "Echo Show") {return "Echo Show Gen3";} elsif($ModelNumber eq "A4ZP7ZC4PI6TO" || $ModelNumber eq "Echo Show 5") {return "Echo Show 5";} elsif($ModelNumber eq "A1XWJRHALS1REP" || $ModelNumber eq "Echo Show 5") {return "Echo Show 5 Gen2";} elsif($ModelNumber eq "A1Z88NGR2BK6A2" || $ModelNumber eq "Echo Show 8") {return "Echo Show 8";} elsif($ModelNumber eq "A15996VY63BQ2D" || $ModelNumber eq "Echo Show 8") {return "Echo Show 8 Gen2";} elsif($ModelNumber eq "A2M35JJZWCQOMZ" || $ModelNumber eq "Echo Plus") {return "Echo Plus";} elsif($ModelNumber eq "A1JJ0KFC4ZPNJ3" || $ModelNumber eq "Echo Input") {return "Echo Input";} elsif($ModelNumber eq "A18O6U1UQFJ0XK" || $ModelNumber eq "Echo Plus 2") {return "Echo Plus 2";} elsif($ModelNumber eq "A3VRME03NAXFUB" || $ModelNumber eq "Echo Flex") {return "Echo Flex";} elsif($ModelNumber eq "A3FX4UWTP28V1P" || $ModelNumber eq "Echo") {return "Echo Gen3";} elsif($ModelNumber eq "A30YDR2MK8HMRV" || $ModelNumber eq "Echo") {return "Echo Gen3";} elsif($ModelNumber eq "A3RBAYBE7VM004" || $ModelNumber eq "Echo Studio") {return "Echo Studio";} elsif($ModelNumber eq "A3SSG6GR8UU7SN" || $ModelNumber eq "Echo Sub") {return "Echo Sub";} elsif($ModelNumber eq "AILBSA2LNTOYL" || $ModelNumber eq "Reverb") {return "Reverb";} elsif($ModelNumber eq "A15ERDAKK5HQQG" || $ModelNumber eq "Sonos Display") {return "Sonos Display";} elsif($ModelNumber eq "A2OSP3UA4VC85F" || $ModelNumber eq "Sonos One") {return "Sonos One";} elsif($ModelNumber eq "A3NPD82ABCPIDP" || $ModelNumber eq "Sonos Beam") {return "Sonos Beam";} elsif($ModelNumber eq "A2Z8O30CD35N8F" || $ModelNumber eq "Sonos Arc") {return "Sonos Arc";} elsif($ModelNumber eq "A7WXQPH584YP" || $ModelNumber eq "Echo Gen2") {return "Echo Gen2";} elsif($ModelNumber eq "A3C9PE6TNYLTCH" || $ModelNumber eq "Echo Multiroom") {return "Echo Multiroom";} elsif($ModelNumber eq "AP1F6KUH00XPV" || $ModelNumber eq "Echo Stereopaar") {return "Echo Stereopaar";} elsif($ModelNumber eq "A1DL2DVDQVK3Q" || $ModelNumber eq "Fire Tab HD 10") {return "Fire Tab HD 10";} elsif($ModelNumber eq "A3R9S4ZZECZ6YL" || $ModelNumber eq "Fire Tab HD 10") {return "Fire Tab HD 10";} elsif($ModelNumber eq "A3L0T0VL9A921N" || $ModelNumber eq "Fire Tab HD 8") {return "Fire Tab HD 8";} elsif($ModelNumber eq "A2M4YX06LWP8WI" || $ModelNumber eq "Fire Tab 7") {return "Fire Tab 7";} elsif($ModelNumber eq "A2E0SNTXJVT7WK" || $ModelNumber eq "Fire TV V1") {return "Fire TV V1";} elsif($ModelNumber eq "A2GFL5ZMWNE0PX" || $ModelNumber eq "Fire TV") {return "Fire TV";} elsif($ModelNumber eq "A12GXV8XMS007S" || $ModelNumber eq "Fire TV") {return "Fire TV";} elsif($ModelNumber eq "A3HF4YRA2L7XGC" || $ModelNumber eq "Fire TV Cube") {return "Fire TV Cube";} elsif($ModelNumber eq "ADVBD696BHNV5" || $ModelNumber eq "Fire TV Stick V1") {return "Fire TV Stick V1";} elsif($ModelNumber eq "A2LWARUGJLBYEW" || $ModelNumber eq "Fire TV Stick V2") {return "Fire TV Stick V2";} elsif($ModelNumber eq "AKPGW064GI9HE" || $ModelNumber eq "Fire TV Stick 4K") {return "Fire TV Stick 4K";} elsif($ModelNumber eq "A265XOI9586NML" || $ModelNumber eq "Fire TV Stick 4K") {return "Fire TV Stick 4K";} elsif($ModelNumber eq "A3EVMLQTU6WL1W" || $ModelNumber eq "Fire TV Stick 4K Max") {return "Fire TV Stick 4K Max";} elsif($ModelNumber eq "A31DTMEEVDDOIV" || $ModelNumber eq "Fire TV Stick 4K") {return "Fire TV";} elsif($ModelNumber eq "A2JKHJ0PX4J3L3" || $ModelNumber eq "ECHO FireTv Cube 4K") {return "ECHO FireTv Cube 4K";} elsif($ModelNumber eq "A10L5JEZTKKCZ8" || $ModelNumber eq "VOBOT") {return "VOBOT";} elsif($ModelNumber eq "A37SHHQ3NUL7B5" || $ModelNumber eq "Bose Home Speaker 500") {return "Bose Home Speaker 500";} elsif($ModelNumber eq "AVN2TMX8MU2YM" || $ModelNumber eq "Bose Home Speaker 500") {return "Bose Home Speaker 500";} elsif($ModelNumber eq "A1RTAM01W29CUP" || $ModelNumber eq "Alexa App for PC") {return "Alexa App for PC";} elsif($ModelNumber eq "A21Z3CGI8UIP0F" || $ModelNumber eq "HEOS") {return "HEOS";} elsif($ModelNumber eq "AKOAGQTKAS9YB" || $ModelNumber eq "Echo Connect") {return "Echo Connect";} elsif($ModelNumber eq "A3NTO4JLV9QWRB" || $ModelNumber eq "Gigaset L800HX") {return "Gigaset L800HX";} elsif($ModelNumber eq "A1HNT9YTOBE735" || $ModelNumber eq "Telekom Smart Speaker") {return "Telekom Smart Speaker";} elsif($ModelNumber eq "A1WAR447VT003J" || $ModelNumber eq "Yamaha MusicCast 20") {return "Yamaha MusicCast 20";} elsif($ModelNumber eq "AVE5HX13UR5NO" || $ModelNumber eq "Zero Touch (Logitech)") {return "Zero Touch (Logitech)";} elsif($ModelNumber eq "A3GZUE7F9MEB4U" || $ModelNumber eq "Sony WH-100XM3") {return "Sony WH-100XM3";} elsif($ModelNumber eq "A2J0R2SD7G9LPA" || $ModelNumber eq "Lenovo P10") {return "Lenovo P10";} elsif($ModelNumber eq "A1J16TEDOYCZTN" || $ModelNumber eq "Amazon Tablet") {return "Amazon Tablet";} elsif($ModelNumber eq "A38EHHIB10L47V" || $ModelNumber eq "Fire HD 8 Tablet") {return "Fire HD 8 Tablet";} elsif($ModelNumber eq "A112LJ20W14H95" || $ModelNumber eq "Media Display") {return "Media Display";} elsif($ModelNumber eq "A1H0CMF1XM0ZP4" || $ModelNumber eq "Bose Soundtouch") {return "Bose Soundtouch";} elsif($ModelNumber eq "AAMFMBBEW2960" || $ModelNumber eq "Garmin DriveSmart 65 with Amazon Alexa") {return "Garmin DriveSmart 65 with Amazon Alexa";} elsif($ModelNumber eq "A2IVLV5VM2W81" || $ModelNumber eq "Mobile Voice iOS") {return "Mobile Voice iOS";} elsif($ModelNumber eq "A2TF17PFR55MTB" || $ModelNumber eq "Mobile Voice Android") {return "Mobile Voice Android";} elsif($ModelNumber eq "A3V3VA38K169FO" || $ModelNumber eq "Fire Tablet") {return "Fire Tablet";} elsif($ModelNumber eq "AVD3HM0HOJAAL" || $ModelNumber eq "Sonos One") {return "Sonos One";} elsif($ModelNumber eq "A1C66CX2XD756O" || $ModelNumber eq "Fire HD 8 Tablet") {return "Fire HD 8 Tablet";} elsif($ModelNumber eq "A17LGWINFBUTZZ" || $ModelNumber eq "Anker Roav Car Charger") {return "Anker Roav Car Charger";} elsif($ModelNumber eq "A2XPGY5LRKB9BE" || $ModelNumber eq "FitBit watch") {return "FitBit watch";} elsif($ModelNumber eq "A2Y04QPFCANLPQ" || $ModelNumber eq "Bose QC35 II") {return "Bose QC35 II";} elsif($ModelNumber eq "A2WFDCBDEXOXR8" || $ModelNumber eq "Bose Soundbar") {return "Bose Soundbar";} elsif($ModelNumber eq "A3BW5ZVFHRCQPO" || $ModelNumber eq "Alexa Car") {return "Alexa Car";} elsif($ModelNumber eq "A303PJF6ISQ7IC" || $ModelNumber eq "Echo Auto") {return "Echo Auto";} elsif($ModelNumber eq "A1ZB65LA390I4K" || $ModelNumber eq "Fire HD 10 Tablet") {return "Fire HD 10 Tablet";} elsif($ModelNumber eq "AVU7CPPF2ZRAS" || $ModelNumber eq "Fire HD 8 Plus (2020)") {return "Fire HD 8 Plus (2020)";} elsif($ModelNumber eq "A24Z7PEXY4MDTK" || $ModelNumber eq "Sony WF-1000X") {return "Sony WF-1000X";} elsif($ModelNumber eq "ABN8JEI7OQF61" || $ModelNumber eq "Sony WF-1000XM3") {return "Sony WF-1000XM3";} elsif($ModelNumber eq "A7S41FQ5TWBC9" || $ModelNumber eq "Sony WH-1000XM4") {return "Sony WH-1000XM4";} elsif($ModelNumber eq "A2WN1FJ2HG09UN" || $ModelNumber eq "Ultimate Alexa") {return "Ultimate Alexa";} elsif($ModelNumber eq "A23FPV4BT7FH68" || $ModelNumber eq "Yamaha YAS-209 Soundbar"){return "Yamaha YAS-209 Soundbar";} elsif($ModelNumber eq "A39Y3UG1XLEJLZ" || $ModelNumber eq "Fitbit Sense") {return "Fitbit Sense";} elsif($ModelNumber eq "") {return "";} elsif($ModelNumber eq "ACCOUNT") {return "ACCOUNT";} else {return "unbekannt";} } sub echodevice_getHelpText($){ my ($HelpTextType) = @_; my $ReturnHelpText = "

Help:

"; if ($HelpTextType eq "no arg") { $ReturnHelpText .= "No argument given."; } else { $ReturnHelpText .= $HelpTextType; } #Allgemeine Infos $ReturnHelpText .= "

More informations: https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/#Set"; return $ReturnHelpText; } sub echodevice_Attr($$$) { my ($cmd, $name, $attrName, $attrVal) = @_; my $hash = $defs{$name}; if( $attrName eq "cookie" ) { #my $hash = $defs{$name}; if( $cmd eq "set" ) { $attrVal =~ s/Cookie: //g; $hash->{helper}{".COOKIE"} = $attrVal; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/; $hash->{helper}{".CSRF"} = $1; $hash->{STATE} = "INITIALIZED"; } } if ( $attrName eq "server" ) { #my $hash = $defs{$name}; if( $cmd eq "set" ) { $hash->{helper}{SERVER} = $attrVal; } } $attr{$name}{$attrName} = $attrVal; return; } sub echodevice_anonymize($$) { my ($hash, $string) = @_; my $s1 = $hash->{helper}{".SERIAL"}; my $s2 = $hash->{helper}{".CUSTOMER"}; my $s3 = $hash->{helper}{HOMEGROUP}; my $s4 = $hash->{helper}{".COMMSID"}; my $s5; $s5 = echodevice_decrypt($hash->{helper}{".USER"}) if(defined($hash->{helper}{".USER"})); $s5 = echodevice_decrypt($hash->{IODev}->{helper}{".USER"}) if(defined($hash->{IODev}->{helper}{".USER"}));; $s1 = "SERIAL" if(!defined($s1)); $s2 = "CUSTOMER" if(!defined($s2)); $s3 = "HOMEGROUP" if(!defined($s3)); $s4 = "COMMSID" if(!defined($s4)); $s5 = "USER" if(!defined($s5)); $string =~ s/$s1/SERIAL/g; $string =~ s/$s2/CUSTOMER/g; $string =~ s/$s3/HOMEGROUP/g; $string =~ s/$s4/COMMSID/g; $string =~ s%$s5%USER%g; return $string; } sub echodevice_encrypt($) { my ($decoded) = @_; my $key = getUniqueId(); my $encoded; return $decoded if( $decoded =~ /\Qcrypt:\E/ ); for my $char (split //, $decoded) { my $encode = chop($key); $encoded .= sprintf("%.2x",ord($char)^ord($encode)); $key = $encode.$key; } return 'crypt:'.$encoded; } sub echodevice_decrypt($) { my ($encoded) = @_; my $key = getUniqueId(); my $decoded; return $encoded if( $encoded !~ /crypt:/ ); $encoded = $1 if( $encoded =~ /crypt:(.*)/ ); for my $char (map { pack('C', hex($_)) } ($encoded =~ /(..)/g)) { my $decode = chop($key); $decoded .= chr(ord($char)^ord($decode)); $key = $decode.$key; } return $decoded; } sub echodevice_setState($$) { my ($hash,$State) = @_; my $name = $hash->{NAME}; Log3 $name, 3, "[$name] [echodevice_setState] to $State" if($hash->{STATE} ne $State) ; foreach my $DeviceID (sort keys %{$modules{$hash->{TYPE}}{defptr}}) { my $echohash = $modules{$hash->{TYPE}}{defptr}{$DeviceID}; readingsBeginUpdate($echohash); readingsBulkUpdateIfChanged( $echohash, "state" , $State,1); readingsEndUpdate($echohash,1); } readingsBeginUpdate($hash); readingsBulkUpdateIfChanged($hash, "state", $State, 1); readingsEndUpdate($hash,1); return; } sub echodevice_getsequenceJson($$$) { my ($hash,$Bereich,$Parameter) = @_; my $ResultString ; my $BereichString; my $BereichValue = ""; my $Optionals = ""; if (lc($Bereich) eq "kalender_heute") { $BereichString = '\"type\":\"Alexa.Calendar.PlayToday\"'; } elsif(lc($Bereich) eq "kalender_morgen") { $BereichString = '\"type\":\"Alexa.Calendar.PlayTomorrow\"'; } elsif(lc($Bereich) eq "kalender_naechstes_ereignis") { $BereichString = '\"type\":\"Alexa.Calendar.PlayNext\"' } elsif(lc($Bereich) eq "nachrichten") { $BereichString = '\"type\":\"Alexa.FlashBriefing.Play\"'; } elsif(lc($Bereich) eq "verkehr") { $BereichString = '\"type\":\"Alexa.Traffic.Play\"'; } elsif(lc($Bereich) eq "wetter") { $BereichString = '\"type\":\"Alexa.Weather.Play\"'; } elsif(lc($Bereich) eq "volume") { $BereichString = '\"type\":\"Alexa.DeviceControls.Volume\"'; $BereichValue = '\"value\":\"'.$Parameter.'\",'; } elsif(lc($Bereich) eq "speak") { $BereichString = '\"type\":\"Alexa.Speak\"'; $BereichValue = '\"textToSpeak\":\"'.$Parameter.'\",'; } elsif(lc($Bereich) eq "erzaehle_geschichte") {$BereichString = '\"type\":\"Alexa.TellStory.Play\"';} elsif(lc($Bereich) eq "erzaehle_witz") {$BereichString = '\"type\":\"Alexa.Joke.Play\"';} elsif(lc($Bereich) eq "erzaehle_was_neues") {$BereichString = '\"type\":\"Alexa.GoodMorning.Play\"';} elsif(lc($Bereich) eq "singe_song") {$BereichString = '\"type\":\"Alexa.SingASong.Play\"';} elsif(lc($Bereich) eq "beliebig_auf_wiedersehen") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-goodbye/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_bestaetigung") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-confirmations/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_geburtstag") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-birthday/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_gute_nacht") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-goodnight/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_guten_morgen") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-goodmorning/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_ich_bin_zuhause") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-iamhome/alexa.cannedtts.speak.curatedtts-random\",';} elsif(lc($Bereich) eq "beliebig_kompliment") {$BereichString = '\"type\":\"Alexa.CannedTts.Speak\"';$BereichValue = '\"cannedTtsStringId\":\"alexa.cannedtts.speak.curatedtts-category-compliments/alexa.cannedtts.speak.curatedtts-random\",';} # elsif(lc($Bereich) eq "glocken") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"bell_02\",';} elsif(lc($Bereich) eq "kirchenglocke") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_church_bell_1x_02\",';} elsif(lc($Bereich) eq "summer") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"buzzers_pistols_01\",';} elsif(lc($Bereich) eq "tuerklingel_1") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_doorbell_01\",';} elsif(lc($Bereich) eq "tuerklingel_2") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_doorbell_chime_01\",';} elsif(lc($Bereich) eq "tuerklingel_3") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_doorbell_chime_02\",';} # elsif(lc($Bereich) eq "jubelnde_menschenmenge") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_large_crowd_cheer_01\",';} elsif(lc($Bereich) eq "publikumsapplaus") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_crowd_applause_01\",';} # elsif(lc($Bereich) eq "flugzeug") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"futuristic_10\",';} elsif(lc($Bereich) eq "katastrophenalarm") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_scifi_alarm_04\",';} elsif(lc($Bereich) eq "motoren_an") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_scifi_engines_on_02\",';} elsif(lc($Bereich) eq "schilde_hoch") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_scifi_sheilds_up_01\",';} elsif(lc($Bereich) eq "sirenen") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_scifi_alarm_01\",';} elsif(lc($Bereich) eq "zappen") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"zap_01\",';} # elsif(lc($Bereich) eq "boing_1") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"boing_01\",';} elsif(lc($Bereich) eq "boing_2") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"boing_03\",';} elsif(lc($Bereich) eq "kamera") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"camera_01\",';} elsif(lc($Bereich) eq "lufthupe") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"air_horn_03\",';} elsif(lc($Bereich) eq "quitschende_tuer") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"squeaky_12\",';} elsif(lc($Bereich) eq "tickende_uhr") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"clock_01\",';} elsif(lc($Bereich) eq "trompete") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_trumpet_bugle_04\",';} # elsif(lc($Bereich) eq "hahn") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_rooster_crow_01\",';} elsif(lc($Bereich) eq "hundegebell") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_dog_med_bark_1x_02\",';} elsif(lc($Bereich) eq "katzenmauzen") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_cat_meow_1x_01\",';} elsif(lc($Bereich) eq "loewengebruell") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_lion_roar_02\",';} elsif(lc($Bereich) eq "wolfsgeheul") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"amzn_sfx_wolf_howl_02\",';} # elsif(lc($Bereich) eq "gruselig_quitschende_tuer") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"horror_10\",';} elsif(lc($Bereich) eq "weihnachtsglocken") {$BereichString = '\"type\":\"Alexa.Sound\"';$BereichValue = '\"soundStringId\":\"christmas_05\",';} $ResultString = '{"behaviorId":"PREVIEW","sequenceJson":"{\"@type\":\"com.amazon.alexa.behaviors.model.Sequence\",\"startNode\":{\"@type\":\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\",' . $BereichString . ',\"operationPayload\":{\"deviceType\":\"' . $hash->{helper}{DEVICETYPE} . '\",\"deviceSerialNumber\":\"' . $hash->{helper}{".SERIAL"} . '\",'.$BereichValue .'\"locale\":\"de-DE\",\"customerId\":\"' . $hash->{IODev}->{helper}{".CUSTOMER"} .'\"}}}","status":"ENABLED"}'; return $ResultString; } sub echodevice_refreshvoice($) { my ($hash) = @_; echodevice_SendCommand($hash,"activities",""); } ########################## # NPM HELPER ########################## sub echodevice_NPMInstall($){ my ($hash) = @_; my $name = $hash->{NAME}; my $InstallResult = '

Installationsergebnis


'; my $npm_bin = AttrVal($name,"npm_bin","/usr/bin/npm"); # Prüfen ob npm installiert ist if (!(-e $npm_bin)) { $InstallResult .= '

Das Bin ' . $npm_bin . ' wurde nicht gefunden. Bitte zuerst das Linux Paket NPM installieren. Folgenden Befehl koennt Ihr hier verwenden:

'; $InstallResult .= '

sudo apt-get install npm


'; $InstallResult .= '

Sollte das Linux Paket NPM schon installiert sein, muesst Ihr ggf. das Attribut "npm_bin" entsprechend anpassen. Standard=/usr/bin/npm

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMInstall] " . $npm_bin . " not found" ; return $InstallResult; } # Verzeichnis anlegen mkdir("cache", 0777) unless(-d "cache" ); mkdir("cache/alexa-cookie", 0777) unless(-d "cache/alexa-cookie" ); mkdir("cache/alexa-cookie/node_modules", 0777) unless(-d "cache/alexa-cookie/node_modules" ); # Prüfen ob schon eine Installation vorhanden ist ggf. Modul löschen if (-e "cache/alexa-cookie/node_modules/alexa-cookie2/alexa-cookie.js") { $InstallResult .= "Vorhandene Installation wird aktualisiert
"; unlink "cache/alexa-cookie/node_modules/alexa-cookie2/alexa-cookie.js"; } else {$InstallResult .= "Installation wird angestartet
";} open CMD,'-|','sudo ' . $npm_bin . ' install --prefix ./cache/alexa-cookie alexa-cookie2' or die $@; my $line; while (defined($line=)) {$InstallResult .= $line. "
";} close CMD; # Prüfen ob das alexa-cookie Modul vorhanden ist if (-e "cache/alexa-cookie/node_modules/alexa-cookie2/alexa-cookie.js") {$InstallResult .= '

Installation erfolgreich durchgefuehrt

';} else {$InstallResult .= '

!!Installation fehlgeschlagen!!

';} # Zurückbutton $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; return $InstallResult; } sub echodevice_NPMLoginNew($){ my ($hash) = @_; my $name = $hash->{NAME}; my $number = $hash->{NR}; my $InstallResult = '

Login Ergebnis


'; my $npm_bin_node = AttrVal($name,"npm_bin_node","/usr/bin/node"); $NPMLoginTyp = "NPM Login New " . localtime(); # Prüfen ob node installiert ist if (!(-e $npm_bin_node)) { $InstallResult .= '

Das Bin ' . $npm_bin_node . ' wurde nicht gefunden. Bitte zuerst das Linux Paket NPM installieren. Folgenden Befehl koennt Ihr hier verwenden:

'; $InstallResult .= '

sudo apt-get install npm


'; $InstallResult .= '

Sollte das Linux Paket NPM schon installiert sein, muesst Ihr ggf. das Attribut "npm_bin_node" entsprechend anpassen. Standard=/usr/bin/node

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] " . $npm_bin_node . " not found" ; return $InstallResult; } # Node Version prüfen close NODEVER; open NODEVER,'-|', 'node -v' or die $@; my $NodeResult; my $NodeLoop = "2"; do { $NodeResult=; $NodeResult =~ s/v//g; #Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Node Version $NodeResult"; if (version->declare($NodeResult)->numify < version->declare('8.10')->numify ) { $InstallResult .= '

Die installierte Node Version ' . $NodeResult . ' ist zu alt. Bitte zuerst die Node Version auf Minimum 8.12 aktualisieren. Folgende Befehle koennt Ihr hier verwenden:

'; $InstallResult .= '

sudo apt-get install curl

'; $InstallResult .= '

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

'; $InstallResult .= '

sudo apt-get update

'; $InstallResult .= '

sudo apt-get install nodejs


'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Node Version " . $NodeResult . " is to old! Pleas make an update"; return $InstallResult; } else {Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Node Version " . $NodeResult;} } while ($NodeLoop eq "1"); # Prüfen ob das alexa-cookie Mdoul vorhanden ist if (!(-e "cache/alexa-cookie/node_modules/alexa-cookie2/alexa-cookie.js")) { $InstallResult .= '

Das alexa-cookie Modul wurde nicht gefunden. Bitte fuehrt am Amazon Account Device einen set "NPM_install" durch

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] alexa-cookie modul not found" ; return $InstallResult; } # Version prüfen; echodevice_NPMCheckVersion($hash,"cache/alexa-cookie/node_modules/alexa-cookie2/package.json","echodevice_NPMLoginNew"); my $ProxyPort = AttrVal($name,"npm_proxy_port","3002"); my $OwnIP = "127.0.0.1"; # Eigene IP-Adresse ermitteln my $cmdLine = 'ip -o addr show | awk \'/inet/ {print $2, $3, $4}\''; my @ips = `$cmdLine`; foreach my $ipLine (@ips) { my ($interface, undef, $ipParts) = split(' ', $ipLine); my ($ip) = split('/', $ipParts); Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Check Interface=$interface IP=$ip"; if ($interface ne 'lo' and (!(index($interface, "tun") != -1))) { if (!(index($ip, ":") != -1)) { $OwnIP = $ip ; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result Interface=$interface IP=$ip"; } } else { Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Ignor Interface=$interface IP=$ip"; } } my $ProxyIP = AttrVal($name,"npm_proxy_ip",$OwnIP); if ($ProxyIP eq "127.0.0.1") { $InstallResult .= '

Die Ermittlung der IP-Adresse ' . $ProxyIP . ' des FHEM Servers hat nicht funktioniert, bitte das Attribut "npm_proxy_ip" entsprechend anpassen.

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] wrong IP-Address" ; return $InstallResult; } # Proxy Listen IP my $ProxyListenIP = AttrVal($name,"npm_proxy_listen_ip",$OwnIP); # Prüfen ob der Port belegt ist my $PORTLoop = "1"; my $NetstatFound = "1"; close PORT; open PORT,'-|', 'netstat -a' or do {$NetstatFound= "0"}; if ($NetstatFound eq "1") { my $PORTResult; do { $PORTResult=; Log3 $name, 4, "[$name] [echodevice_NPMLoginNew] Result Proxy Port $PORTResult" if ($PORTResult ne ""); if (index($PORTResult, ":" . $ProxyPort . " " ) != -1) { $PORTLoop = "2"; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result Proxy Port $PORTResult"; } $PORTLoop = "3" if ($PORTResult eq ""); } while ($PORTLoop eq "1"); close PORT; } if ($PORTLoop eq "2") { $InstallResult .= '

Der angegebene Proxy Port ' . $ProxyPort . ' ist in Benutzung, bitte das Attribut "npm_proxy_port" entsprechend anpassen.

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Proxy Port $ProxyPort is in use" ; return $InstallResult; } elsif ($PORTLoop eq "1") { $InstallResult .= '

Die Pruefung des angegebenen Proxy Ports ' . $ProxyPort . ' hat nicht funktioniert. Zum Pruefen des Proxy Ports wird die Anwendung "netstat" benoetigt. Folgende Befehle koennt Ihr hier verwenden:

'; $InstallResult .= '

sudo apt-get update

'; $InstallResult .= '

sudo apt-get install net-tools


'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Proxy Port netstat not found" ; return $InstallResult; } else { Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Proxy Port $ProxyPort is free"; } Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Proxy IP $ProxyIP"; my $SkriptContent = "alexaCookie = require('alexa-cookie2');" . "\n"; $SkriptContent .= "fs = require('fs');" . "\n"; $SkriptContent .= "" . "\n"; $SkriptContent .= "const config = {" . "\n"; $SkriptContent .= " logger: console.log," . "\n"; $SkriptContent .= " setupProxy: true," . "\n"; $SkriptContent .= " proxyOwnIp: '$ProxyIP'," . "\n"; $SkriptContent .= " proxyPort: $ProxyPort," . "\n"; $SkriptContent .= " proxyListenBind: '$ProxyListenIP'," . "\n"; $SkriptContent .= " proxyLogLevel: 'info'" . "\n"; $SkriptContent .= "};" . "\n"; $SkriptContent .= "" . "\n"; $SkriptContent .= "alexaCookie.generateAlexaCookie('LoginFHEM', 'xxxx', config, (err, result) => {" . "\n"; $SkriptContent .= " console.log('RESULT: ' + err + ' / ' + JSON.stringify(result));" . "\n"; $SkriptContent .= " fs.writeFileSync('./cache/alexa-cookie/" . $number . "result.json', JSON.stringify(result) , 'utf-8'); " . "\n"; $SkriptContent .= " if (result && result.csrf) {" . "\n"; $SkriptContent .= " alexaCookie.stopProxyServer();" . "\n"; $SkriptContent .= " }" . "\n"; $SkriptContent .= "});" . "\n"; $SkriptContent .= "" . "\n"; my $filename = "cache/alexa-cookie/" . $number . "create-cookie.js"; # Altes Skript löschen if ((-e $filename)) {unlink $filename}; # Neues Skript anlegen open(FH, ">$filename"); print FH $SkriptContent; close(FH); # Prüfen ob das alexa-cookie Mdoul vorhanden ist if (!(-e $filename)) { $InstallResult .= '

Das Skript zum Amazon Login konnte nicht gefunden werden!

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] create-cookie.js not found" ; return $InstallResult; } my $CreatCookie; # Infos festhalten readingsSingleUpdate( $hash, "amazon_refreshtoken", "wird erzeugt",1 ); # Skript ausführen close CMD; open CMD,'-|', $npm_bin_node . ' ./' . $filename or die $@; my $line; my $Loop = "1"; my $LoopCount = 0; do { $line=; $CreatCookie .= $line. "
"; if ($line ne "") {Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result $line"} else {$LoopCount +=1;} $Loop = "2" if (index($line, "Please check credentials") != -1) ; $Loop = "2" if (index($line, "Proxy-Server listening on port") != -1) ; $Loop = "3" if (index($line, "Final Registraton Result") != -1) ; $Loop = "4" if ($line eq "" && $LoopCount > 100); } while ($Loop eq "1"); if ($Loop eq "2") { $InstallResult .= 'Bitte den Link anklicken und die Amazonanmeldung durchfuehren.
'; $InstallResult .= '
http://' . $ProxyIP . ':' . $ProxyPort . '
'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result: Bitte den Link anklicken und die Amazonanmeldung durchfuehren."; InternalTimer(gettimeofday() + 3 , "echodevice_NPMWaitForCookie" , $hash, 0); return $InstallResult; } elsif($Loop eq "3") { $InstallResult .= '

Refreshtoken wurde erfolgreich erstellt

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result: Refreshtoken wurde erfolgreich erstellt."; InternalTimer(gettimeofday() + 3 , "echodevice_NPMWaitForCookie" , $hash, 0); return $InstallResult; } elsif($Loop eq "4") { $InstallResult .= '

Es ist ein Fehler aufgetreten!! Bitte das FHEM Log pruefen.

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result: Es ist ein Fehler aufgetreten!! Bitte das FHEM Log pruefen."; return $InstallResult; } else { Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] Result: Es ist ein Fehler aufgetreten!! Bitte das FHEM Log pruefen.!!!!!!"; return $InstallResult; } } sub echodevice_NPMLoginRefresh($){ my ($hash) = @_; my $name = $hash->{NAME}; my $number = $hash->{NR}; my $RefreshCookie = ReadingsVal($name , ".COOKIE", "0"); my $InstallResult = '

Login Ergebnis


'; my $npm_bin_node = AttrVal($name,"npm_bin_node","/usr/bin/node"); $NPMLoginTyp = "NPM Login Refresh " . localtime(); # Prüfen ob npm installiert ist Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] check $npm_bin_node" ; if (!(-e $npm_bin_node)) { $InstallResult .= '

Das Bin ' . $npm_bin_node . ' wurde nicht gefunden. Bitte zuerst das Linux Paket NPM installieren. Folgenden Befehl koennt Ihr hier verwenden:

'; $InstallResult .= '

sudo apt-get install npm


'; $InstallResult .= '

Sollte das Linux Paket NPM schon installiert sein, muesst Ihr ggf. das Attribut "npm_bin_node" entsprechend anpassen. Standard=/usr/bin/node

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginRefresh] " . $npm_bin_node . " not found" ; return $InstallResult; } # Prüfen ob das alexa-cookie Mdoul vorhanden ist Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] check alexa-cookie.js" ; if (!(-e "cache/alexa-cookie/node_modules/alexa-cookie2/alexa-cookie.js")) { $InstallResult .= '

Das alexa-cookie Modul wurde nicht gefunden. Bitte fuehrt am Amazon Account Device einen set "NPM_install" durch

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginRefresh] alexa-cookie modul not found" ; return $InstallResult; } # Version prüfen; echodevice_NPMCheckVersion($hash,"cache/alexa-cookie/node_modules/alexa-cookie2/package.json","echodevice_NPMLoginRefresh"); # Prüfen ob das Refresh Cookie gültig ist! Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] check Refresh Cookie String" ; if (substr($RefreshCookie,0,1) ne "{") { $InstallResult .= '

Das angegebene Refreshtoken Cookie ist ungeueltig! Refreshtoken="' . $RefreshCookie . '"

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginRefresh] refreshtoken unkown!! refreshtoken=" . $RefreshCookie; return $InstallResult; } Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] build " . $number . "refresh-cookie.js"; my $SkriptContent = "alexaCookie = require('alexa-cookie2');" . "\n"; $SkriptContent .= "fs = require('fs');" . "\n"; $SkriptContent .= "" . "\n"; $SkriptContent .= "const config = {" . "\n"; $SkriptContent .= " logger: console.log," . "\n"; $SkriptContent .= " formerRegistrationData: " . $RefreshCookie . "\n"; $SkriptContent .= "};" . "\n"; $SkriptContent .= "" . "\n"; $SkriptContent .= "alexaCookie.refreshAlexaCookie(config, (err, result) => {" . "\n"; $SkriptContent .= " console.log('RESULT: ' + err + ' / ' + JSON.stringify(result));" . "\n"; $SkriptContent .= " fs.writeFileSync('./cache/alexa-cookie/" . $number . "result.json', JSON.stringify(result) , 'utf-8'); " . "\n"; $SkriptContent .= "});" . "\n"; $SkriptContent .= "" . "\n"; my $filename = "cache/alexa-cookie/" . $number . "refresh-cookie.js"; #$InstallResult .= '

'; # Altes Skript löschen if ((-e $filename)) {unlink $filename}; # Neues Skript anlegen open(FH, ">$filename"); print FH $SkriptContent; close(FH); # Prüfen ob das alexa-cookie Mdoul vorhanden ist Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] check " . $number . "refresh-cookie.js"; if (!(-e $filename)) { $InstallResult .= '

Das Skript zum Amazon Login konnte nicht gefunden werden!

'; $InstallResult .= '
'; $InstallResult .= ""; $InstallResult =~ s/'/'/g; Log3 $name, 3, "[$name] [echodevice_NPMLoginNew] refresh-cookie.js not found" ; return $InstallResult; } # Skript ausführen close CMD; Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] start $npm_bin_node $filename"; open CMD,'-|',$npm_bin_node . ' ./' . $filename . ' &'; Log3 $name, 4, "[$name] [echodevice_NPMLoginRefresh] start InternalTimer echodevice_NPMWaitForCookie"; InternalTimer(gettimeofday() + 1 , "echodevice_NPMWaitForCookie" , $hash, 0); return ;#$InstallResult; } sub echodevice_NPMWaitForCookie($){ my ($hash) = @_; my $name = $hash->{NAME}; my $number = $hash->{NR}; my $filename = "cache/alexa-cookie/" . $number . "result.json"; my $CanDelete = 0; my $ExistSkript = "false"; my $CookieResult; if ($NPMLoginTyp =~ m/Refresh/) { $ExistSkript = $number . "refresh-cookie.js = true" if (-e "cache/alexa-cookie/" . $number . "refresh-cookie.js"); } else { $ExistSkript = $number . "create-cookie.js = true" if (-e "cache/alexa-cookie/" . $number . "create-cookie.js"); } if (-e $filename) { # Informationen eintragen open(MAILDAT, "<$filename") || die "Datei wurde nicht gefunden\n"; while(){ if (index($_, "{") != -1) { $CookieResult = $_; Log3 $name, 3, "[$name] [echodevice_NPMWaitForCookie] [$NPMLoginTyp] write new refreshtoken"; # Log3 $name, 3, "[$name] [echodevice_NPMWaitForCookie] [$NPMLoginTyp] $CookieResult"; readingsSingleUpdate( $hash, "amazon_refreshtoken", "vorhanden",1 ); readingsSingleUpdate( $hash, ".COOKIE", $CookieResult,1 ); readingsSingleUpdate( $hash, "COOKIE_TYPE", "NPM_Login",1 ); $hash->{helper}{".COOKIE"} = $CookieResult; $hash->{helper}{".COOKIE"} =~ /"localCookie":".*session-id=(.*)","?/; $hash->{helper}{".COOKIE"} = "session-id=" . $1; $hash->{helper}{".COOKIE"} =~ /csrf=([-\w]+)[;\s]?(.*)?$/ if(defined($hash->{helper}{".COOKIE"})); $hash->{helper}{".CSRF"} = $1 if(defined($hash->{helper}{".COOKIE"})); # result.json & Skripte löschen if (-e $filename) {unlink $filename;} if (-e "cache/alexa-cookie/" . $number . "create-cookie.js") {unlink "cache/alexa-cookie/" . $number . "create-cookie.js";} if (-e "cache/alexa-cookie/" . $number . "refresh-cookie.js") {unlink "cache/alexa-cookie/" . $number . "refresh-cookie.js";} echodevice_setState($hash,"connected"); } else { readingsSingleUpdate( $hash, "amazon_refreshtoken", "wait for refreshtoken",1 ); Log3 $name, 3, "[$name] [echodevice_NPMWaitForCookie] [$NPMLoginTyp] wait for refreshtoken / refreshtoken unkown!! refreshtoken=" . $_ . " EXIST " . $ExistSkript; InternalTimer(gettimeofday() + 1 , "echodevice_NPMWaitForCookie" , $hash, 0); } } close(MAILDAT); } else { Log3 $name, 4, "[$name] [echodevice_NPMWaitForCookie] [$NPMLoginTyp] wait for refreshtoken exist " . $ExistSkript ; InternalTimer(gettimeofday() + 1 , "echodevice_NPMWaitForCookie" , $hash, 0); } } sub echodevice_NPMCheckVersion ($$$) { my ($hash,$filename,$LogBereich) = @_; my $name = $hash->{NAME}; my $Modulversion = "unknown"; if (-e $filename) { open(VERSION, "<$filename"); while(){ if ($_ =~ m/version/) { my @Result = split(/"/,$_); $Modulversion = @Result[3]; Log3 $name, 4, "[$name] [$LogBereich] Version alexa-cookie.js = $Modulversion";} } close(VERSION); } else {Log3 $name, 4, "[$name] [$LogBereich] Version alexa-cookie.js = unknown";} return $Modulversion; } ########################## # TTS/POM ########################## sub echodevice_AWSPython($$) { my ($hash,$Skriptfile) = @_; my $name = $hash->{NAME}; #Verzeichnis anlegen my $filedir ="cache"; mkdir($filedir, 0777) unless(-d $filedir ); my $filename = $filedir ."/". $Skriptfile; # Prüfen ob die Datei erstezt werden muss if ($AWSPythonVersion eq ReadingsVal($hash->{IODev}->{NAME} , lc("AWS_PythonVersion"), "0") && (-e $filename)) { Log3 "echodevice", 4, "[$name] [echodevice_AWSPython] file $filename exist"; return; } Log3 "echodevice", 3, "[$name] [echodevice_AWSPython] file $filename not exist or to old"; readingsBeginUpdate($hash->{IODev}); readingsBulkUpdate($hash->{IODev}, lc("AWS_PythonVersion"), $AWSPythonVersion, 1); readingsEndUpdate($hash->{IODev},1); #altes Skript löschen if ((-e $filename)) {unlink $filename}; #Skript Inhalt my $SkriptContent = "# IAM API (CreateUser) adapted to Polly service\n"; $SkriptContent .= "import sys, os, base64, datetime, hashlib, hmac, urllib\n"; $SkriptContent .= "sys.path.append('../')\n"; $SkriptContent .= "# Parameter\n"; $SkriptContent .= "MessageText = sys.argv[1]\n"; $SkriptContent .= "MessageVoice = sys.argv[2]\n"; $SkriptContent .= "access_key = sys.argv[3]\n"; $SkriptContent .= "secret_key = sys.argv[4]\n"; $SkriptContent .= "OutputFormat = sys.argv[7]\n"; $SkriptContent .= "# DEFs\n"; $SkriptContent .= "def sign(key, msg):\n"; $SkriptContent .= " return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()\n"; $SkriptContent .= "\n"; $SkriptContent .= "def getSignatureKey(key, dateStamp, regionName, serviceName):\n"; $SkriptContent .= " kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)\n"; $SkriptContent .= " kRegion = sign(kDate, regionName)\n"; $SkriptContent .= " kService = sign(kRegion, serviceName)\n"; $SkriptContent .= " kSigning = sign(kService, 'aws4_request')\n"; $SkriptContent .= " return kSigning\n"; $SkriptContent .= "# ************* REQUEST VALUES *************\n"; $SkriptContent .= "method = 'POST'\n"; $SkriptContent .= "service = 'polly'\n"; $SkriptContent .= "region = 'eu-west-1'\n"; $SkriptContent .= "host = service+'.'+region+'.amazonaws.com'\n"; $SkriptContent .= "api = '/v1/speech'\n"; $SkriptContent .= "endpoint = 'https://'+host+api\n"; $SkriptContent .= "content_type = 'application/json'\n"; $SkriptContent .= "# ************* REQUEST CONTENT ************\n"; $SkriptContent .= "request_parameters = '{'\n"; $SkriptContent .= "request_parameters += '" . '"' . "OutputFormat" . '"' . ": " . '"' . "' + OutputFormat + '" . '"' . ",'\n"; $SkriptContent .= "request_parameters += '" . '"' . "Text" . '"' . ": " . '"' . "' + MessageText + '" . '"' . ",'\n"; $SkriptContent .= "request_parameters += '" . '"' . "TextType" . '"' . ": " . '"' . "text" . '"' . ",'\n"; $SkriptContent .= "request_parameters += '" . '"' . "VoiceId" . '"' . ": " . '"' . "' + MessageVoice + '" . '"' . "'\n"; $SkriptContent .= "request_parameters += '}'\n"; $SkriptContent .= "# Create a date for headers and the credential string\n"; $SkriptContent .= "t = datetime.datetime.utcnow()\n"; $SkriptContent .= "amz_date = t.strftime('%Y%m%dT%H%M%SZ')\n"; $SkriptContent .= "date_stamp = t.strftime('%Y%m%d')\n"; $SkriptContent .= "# ************* TASK 1: CREATE A CANONICAL REQUEST *************\n"; $SkriptContent .= "canonical_uri = api\n"; $SkriptContent .= "canonical_querystring = ''\n"; $SkriptContent .= "canonical_headers = 'content-type:' + content_type + '\\n' + 'host:' + host + '\\n' + 'x-amz-date:' + amz_date + '\\n'\n"; $SkriptContent .= "signed_headers = 'content-type;host;x-amz-date'\n"; $SkriptContent .= "payload_hash = hashlib.sha256(request_parameters).hexdigest()\n"; $SkriptContent .= "canonical_request = method + '\\n' + canonical_uri + '\\n' + canonical_querystring + '\\n' + canonical_headers + '\\n' + signed_headers + '\\n' + payload_hash\n"; $SkriptContent .= "# ************* TASK 2: CREATE THE STRING TO SIGN*************\n"; $SkriptContent .= "algorithm = 'AWS4-HMAC-SHA256'\n"; $SkriptContent .= "credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request'\n"; $SkriptContent .= "string_to_sign = algorithm + '\\n' + amz_date + '\\n' + credential_scope + '\\n' + hashlib.sha256(canonical_request).hexdigest()\n"; $SkriptContent .= "# ************* TASK 3: CALCULATE THE SIGNATURE *************\n"; $SkriptContent .= "signing_key = getSignatureKey(secret_key, date_stamp, region, service)\n"; $SkriptContent .= "signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()\n"; $SkriptContent .= "# ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************\n"; $SkriptContent .= "authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature\n"; $SkriptContent .= "# ************* TASK 5: ADD SIGNING INFORMATION TO FHEM *************\n"; $SkriptContent .= "f=open(sys.argv[5],'wb')\n"; $SkriptContent .= "f.write(amz_date)\n"; $SkriptContent .= "f.close()\n"; $SkriptContent .= "f=open(sys.argv[6],'wb')\n"; $SkriptContent .= "f.write(authorization_header)\n"; $SkriptContent .= "f.close()\n"; open(FH, ">$filename"); print FH $SkriptContent; close(FH); } sub echodevice_Amazon($$$) { my ($hash,$parameter,$type) = @_; my $name = $hash->{NAME}; my $AWS_Access_Key = ReadingsVal($hash->{IODev}->{NAME} , lc(".AWS_Access_Key"), "none"); my $AWS_Secret_Key = ReadingsVal($hash->{IODev}->{NAME} , lc(".AWS_Secret_Key"), "none"); if ( $AWS_Access_Key eq "none" ) { readingsSingleUpdate( $hash, "tts_error", "No AWS_Access_Key Value",1 ); return "No AWS_Access_Key Value" ; } if ( $AWS_Secret_Key eq "none" ){ readingsSingleUpdate( $hash, "tts_error", "No AWS_Secret_Key Value",1 ); return "No AWS_Secret_Key Value"; } #Defaults my $TTS_Voice = AttrVal($name,"TTS_Voice","German_Female_Marlene"); my $AWS_Format = ReadingsVal($hash->{IODev}->{NAME} , lc("AWS_OutputFormat"), "mp3"); my @VoiceName = split("_",$TTS_Voice); #Verzeichnis anlegen my $filedir = "cache"; mkdir($filedir, 0777) unless(-d $filedir ); #Temp Dateien my $AWS_File_AMZDate = $filedir . "/amzdate"; my $AWS_File_Header = $filedir . "/header"; if ((-e "$AWS_File_AMZDate")) {unlink "$AWS_File_AMZDate"} if ((-e "$AWS_File_Header")) {unlink "$AWS_File_Header"} # Länge der Nachricht prüfen. Bei kleiner 3 wird noch ein " ," angehängt! $parameter .= " ," if (length($parameter) < 3) ; # Python Skript ausführen echodevice_AWSPython($hash,"pollyspeech.py"); my $ret = system('python ' . $filedir . '/pollyspeech.py "' . $parameter .'" ' . @VoiceName[2] .' '. echodevice_decrypt($AWS_Access_Key) . " " . echodevice_decrypt($AWS_Secret_Key) . " " . $AWS_File_AMZDate . " " . $AWS_File_Header . " " . $AWS_Format); # Infos auswerten my $AWSDate; open FILE, $AWS_File_AMZDate or do { Log3 $name, 3, "[$name] [echodevice_Amazon] Could not read from $AWS_File_AMZDate"; readingsSingleUpdate( $hash, "tts_error", "Could not read from $AWS_File_AMZDate",1 ); return; }; chomp(my $AWSDate = ); close FILE; my $AWSHeader; open FILE, $AWS_File_Header or do { Log3 $name, 3, "[$name] [echodevice_Amazon] Could not read from $AWS_File_Header"; readingsSingleUpdate( $hash, "tts_error", "Could not read from $AWS_File_Header",1 ); return; }; chomp(my $AWSHeader = ); close FILE; # MP3 Download starten my $params = { url => "https://polly.eu-west-1.amazonaws.com/v1/speech", header => "Authorization: ".$AWSHeader."\r\nX-Amz-Date: ".$AWSDate."\r\nContent-Type: application/json", method => "POST", data => '{"OutputFormat": "' . $AWS_Format . '","Text": "' . $parameter .'","TextType": "text","VoiceId": "' . @VoiceName[2] . '"}', hash => $hash, type => $type, callback => \&echodevice_ParseTTSMP3 }; HttpUtils_NonblockingGet($params); } sub echodevice_Google($$$) { my ($hash,$parameter,$type) = @_; my $name = $hash->{NAME}; # ersetze Sonderzeichen die Google nicht auflösen kann my $MessageText = decode_utf8($parameter); $MessageText =~ s/ä/ae/g; $MessageText =~ s/ö/oe/g; $MessageText =~ s/ü/ue/g; $MessageText =~ s/Ä/Ae/g; $MessageText =~ s/Ö/Oe/g; $MessageText =~ s/Ü/Ue/g; $MessageText =~ s/ß/ss/g; Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] TTS Message length=" . length($MessageText) ; if (length($MessageText) > 200) { Log3 $name, 3, "[$name] [echodevice_ParseTTSMP3] TTS Message to long!"; readingsSingleUpdate( $hash, "tts_error", "TTS Message to long",1 ); $MessageText = substr($MessageText,0,202) } # MP3 Download starten my $params = { url => "http://translate.google.com/translate_tts?tl=de&client=tw-ob&q=" . uri_escape($MessageText), method => "GET", hash => $hash, type => $type, callback => \&echodevice_ParseTTSMP3 }; HttpUtils_NonblockingGet($params); } sub echodevice_AmazonVoiceMP3($) { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $type = $param->{type}; Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] [$type] URL = " . $param->{url}; Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] [$type] HEADER = " . $param->{header}; Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] [$type] KEY = " . $param->{type}; Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] [$type] ERROR = " . $err if ($err); Log3 $name, 5, "[$name] [echodevice_AmazonVoiceMP3] [$type] DATA = " . $data; #ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_Filename"), "") my $MP3Filename = $param->{type}; Log3 $name, 4, "[$name] [echodevice_AmazonVoiceMP3] [$type] MP3File = " . $MP3Filename; if ($data eq "") { Log3 $name, 3, "[$name] [echodevice_AmazonVoiceMP3] [$type] no data received"; readingsSingleUpdate( $hash, "tts_error", "no data received",1 ); return; } #Verzeichnis echodevice anlegen mkdir($FW_dir . "/echodevice", 0777) unless(-d $FW_dir . "/echodevice" ); mkdir($FW_dir . "/echodevice/VoiceRecords", 0777) unless(-d $FW_dir . "/echodevice/VoiceRecords" ); # MP3 Datei anlegen open(FH, ">$FW_dir/echodevice/VoiceRecords/$MP3Filename"); print FH $data; close(FH); } sub echodevice_ParseTTSMP3($) { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $type = $param->{type}; Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] URL = " . $param->{url}; Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] DATA = " . $param->{data}; Log3 $name, 5, "[$name] [echodevice_ParseTTSMP3] [$type] HEADER = " . $param->{header}; Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] ERROR = " . $err if ($err); Log3 $name, 5, "[$name] [echodevice_ParseTTSMP3] [$type] DATA = " . $data; #ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_Filename"), "") my $MP3Filename = $name . ".mp3"; my $M3UFilename = ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_Filename"), "live18-hq.aac.m3u"); Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] MP3File = " . $MP3Filename; Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] M3UFile = " . $M3UFilename; if ($data eq "") { Log3 $name, 3, "[$name] [echodevice_ParseTTSMP3] [$type] no data received"; readingsSingleUpdate( $hash, "tts_error", "no data received",1 ); return; } #Verzeichnis echodevice anlegen mkdir($FW_dir . "/echodevice", 0777) unless(-d $FW_dir . "/echodevice" ); # MP3/M3U Datei löschen if ((-e $FW_dir . "/echodevice/". $MP3Filename)) {unlink $FW_dir . "/echodevice/". $MP3Filename} if ((-e $FW_dir . "/echodevice/". $M3UFilename)) {unlink $FW_dir . "/echodevice/".$M3UFilename} # MP3 Datei anlegen open(FH, ">$FW_dir/echodevice/$MP3Filename"); print FH $data; close(FH); # MP3 TTS_normalize my $TTS_normalize_value = 200; $TTS_normalize_value = AttrVal($hash->{IODev}->{NAME}, "TTS_normalize", 100) if (AttrVal($hash->{IODev}->{NAME}, "TTS_normalize", 100) != 100 ) ; $TTS_normalize_value = AttrVal($name, "TTS_normalize", 100) if (AttrVal($name, "TTS_normalize", 100) != 100 ) ; if ($TTS_normalize_value != 200 ) { Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] MP3 dBFS = " . $TTS_normalize_value ; system("normalize-mp3 -a " . $TTS_normalize_value . "dBFS " . $FW_dir . "/echodevice/" . $MP3Filename . " > /dev/null 2>&1"); if (!-e "/usr/bin/normalize-mp3") {Log3 $name, 3, "[$name] [echodevice_ParseTTSMP3] [$type] Missing /usr/bin/normalize-mp3";} if (!-e "/usr/bin/mpg123") {Log3 $name, 3, "[$name] [echodevice_ParseTTSMP3] [$type] Missing /usr/bin/mpg123";} if (!-e "/usr/bin/lame") {Log3 $name, 3, "[$name] [echodevice_ParseTTSMP3] [$type] Missing /usr/bin/lame";} } # M3U Datei erzeugen open(FH, ">$FW_dir/echodevice/$M3UFilename"); print FH "http://" . ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_IPAddress"), "live18-hq.aac.m3u") . "/" . $MP3Filename; close(FH); # Länge feststellen my $MP3_Info = get_mp3info("$FW_dir/echodevice/$MP3Filename"); my $MP3_Time; if ($MP3_Info && defined($MP3_Info->{SECS})) { $MP3_Time = int($MP3_Info->{SECS}+1); readingsSingleUpdate( $hash, "tts_lenght", $MP3_Time ,1 ); Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] MP3 len = $MP3_Time Sekunden."; } $hash->{helper}{lasttuneindelay} = $MP3_Time; $hash->{helper}{lastvolume} = ReadingsVal($name , "volume", 50); $hash->{helper}{lasttuneinid} = ReadingsVal($name , "currentTuneInID", "-"); $hash->{helper}{lasttype} = $type; if(ReadingsVal($name , "volume", 50) < ReadingsVal($name , "volume_alarm", 50)) { echodevice_SendMessage($hash,"pause",""); echodevice_SendCommand($hash,"volume",ReadingsVal($name , "volume_alarm", 50)); Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] [$type] set volume to " . ReadingsVal($name , "volume_alarm", 50); } InternalTimer(gettimeofday() + 1.5 , "echodevice_StartTTSMessage" , $hash, 0); Log3 $name, 4, "[$name] [echodevice_ParseTTSMP3] Setze echodevice_StartTTSMessage Timer"; } sub echodevice_PlayOwnMP3($$) { my ($hash,$M3UContent) = @_; my $name = $hash->{NAME}; Log3 $name, 4, "[$name] [echodevice_PlayOwnMP3] M3UContent = " . $M3UContent; #ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_Filename"), "") my $M3UFilename = ReadingsVal($hash->{IODev}->{NAME} , lc("POM_Filename"), "stream.m3u"); Log3 $name, 4, "[$name] [echodevice_PlayOwnMP3] M3UFile= " . $M3UFilename; if ($M3UContent eq "") { Log3 $name, 3, "[$name] [echodevice_PlayOwnMP3] no data received"; return; } # M3U Datei löschen if ((-e $FW_dir . "/echodevice/". $M3UFilename)) {unlink $FW_dir . "/echodevice/".$M3UFilename} # Content zusammenbauen my $M3UContentWR; my @M3UConntentArray = split /\n+/, $M3UContent; foreach (@M3UConntentArray) { if (lc(substr($_,0,4)) eq 'http') { $M3UContentWR .= $_ . "\n"; Log3 $name, 4, "[$name] [echodevice_SaveOwnMP3] " . $_; } } # M3U Datei erzeugen open(FH, ">$FW_dir/echodevice/$M3UFilename"); print FH $M3UContentWR; close(FH); echodevice_SendCommand($hash,"tunein",ReadingsVal($hash->{IODev}->{NAME} , lc("POM_TuneIn"), "s167655")); } sub echodevice_SaveOwnPlaylist($$) { my ($hash,$M3UContent) = @_; my $name = $hash->{NAME}; my $M3UFilename; my $M3UContentWR; Log3 $name, 4, "[$name] [echodevice_SaveOwnMP3] M3UContent = " . $M3UContent; Log3 $name, 4, "[$name] [echodevice_SaveOwnMP3] M3UFile = " . $M3UFilename; if ($M3UContent eq "") { Log3 $name, 3, "[$name] [echodevice_SaveOwnMP3] no content received"; return; } # Playlist Name auslesen my @M3UConntentArray = split /\n+/, $M3UContent; $M3UFilename = @M3UConntentArray[0]; # Div. Zeichen ersetzen $M3UFilename =~ s/\s+/_/g; $M3UFilename =~ s/\W/_/g; $M3UFilename .= ".m3u"; Log3 $name, 4, "[$name] [echodevice_SaveOwnMP3] M3UFile = " . $M3UFilename; # Content zusammenbauen foreach (@M3UConntentArray) { if (lc(substr($_,0,4)) eq 'http') { $M3UContentWR .= $_ . "\n"; Log3 $name, 4, "[$name] [echodevice_SaveOwnMP3] " . $_; } } #Verzeichnis echodevice anlegen mkdir($FW_dir . "/echodevice", 0777) unless(-d $FW_dir . "/echodevice" ); #Verzeichnis playlists anlegen mkdir($FW_dir . "/echodevice/playlists", 0755) unless(-d $FW_dir . "/echodevice/playlists" ); # M3U Datei löschen if ((-e $FW_dir . "/echodevice/playlists/". $M3UFilename)) {unlink $FW_dir . "/echodevice/playlists/".$M3UFilename} # M3U Datei erzeugen open(FH, ">$FW_dir/echodevice/playlists/$M3UFilename"); print FH $M3UContentWR; close(FH); } sub echodevice_GetOwnPlaylist($) { my ($hash) = @_; my $name = $hash->{NAME}; # startownplaylist if (-d $FW_dir . "/echodevice/playlists") { my $M3UFiles; opendir(DH, $FW_dir . "/echodevice/playlists"); my @files = readdir(DH); closedir(DH); foreach my $file (@files){ # skip . and .. next if($file =~ /^\.$/); next if($file =~ /^\.\.$/); Log3 $name, 5, "[$name] [echodevice_GetOwnPlaylist] found filename = $file"; if ($M3UFiles eq "") { $M3UFiles = $file; } else { $M3UFiles .= ",".$file; } } Log3 $name, 5, "[$name] [echodevice_GetOwnPlaylist] return = " . "playownplaylist:" . $M3UFiles ." "; if ($M3UFiles ne "") { return "playownplaylist:" . $M3UFiles ." " . "deleteownplaylist:" . $M3UFiles ." "; } else { return ; } } else { return; } } sub echodevice_StartLastMedia($) { my ($hash) = @_; my $name = $hash->{NAME}; my $type = $hash->{helper}{lasttype}; if($hash->{helper}{lastvolume} < ReadingsVal($name , "volume_alarm", 50)) { echodevice_SendCommand($hash,"volume",$hash->{helper}{lastvolume}); Log3 $name, 4, "[$name] [echodevice_StartLastMedia] [$type] set volume to " . $hash->{helper}{lastvolume}; } if ($hash->{helper}{lasttuneinid} ne "-" && $hash->{helper}{lasttuneinid} ne "s237481" && $hash->{helper}{lasttuneinid} ne "s204188") { echodevice_SendCommand($hash,"tunein",$hash->{helper}{lasttuneinid}) ; Log3 $name, 4, "[$name] [echodevice_StartLastMedia] [$type] starte TuneIN ID " . $hash->{helper}{lasttuneinid}; } # Settings erneut einlesen InternalTimer(gettimeofday() + 5, "echodevice_GetSettings", $hash, 0); } sub echodevice_StartTTSMessage($) { my ($hash) = @_; my $name = $hash->{NAME}; # TTS starten echodevice_SendCommand($hash,"ttstunein",ReadingsVal($hash->{IODev}->{NAME} , lc("TTS_TuneIn"), "s237481")); } 1; =pod =item device =item summary Amazon Echo remote control =item summary_DE Amazon Echo remote control =begin html

echodevice

    Basic remote control for Amazon Echo devices. You can find the complete documentation here.

    https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/

    Define
      define <name> echodevice <DeviceID> [DeviceType]
      Example: define <Name> echodevice <Amazon account> <Amazon Kennwort>
      Example: define <Name> echodevice

    Set
    • ...
      ...

    Get
    • settings
      Manually reload setings (dnd, bluetooth, wakeword)

    • devices
      Displays a list of Amazon devices connected to your account

    Readings
    • ...
      ...


    Attributes
    • interval
      Poll interval in seconds (300)

    • cookie
      Amazon access cookie, has to be entered for the module to work

    • server
      Amazon server used for controlling the Echo

=end html =begin html_DE

echodevice

    Basic remote control fuer Amazon Echo devices. Die komplette Dokumentation findet Ihr hier.

    https://mwinkler.jimdo.com/smarthome/eigene-module/echodevice/

    Define
      define <name> echodevice <DeviceID> [DeviceType]
      Example: define <Name> echodevice <Amazon account> <Amazon Kennwort>
      Example: define <Name> echodevice

    Set
    • ...
      ...

    Get
    • settings
      Manually reload setings (dnd, bluetooth, wakeword)

    • devices
      Displays a list of Amazon devices connected to your account

    Readings
    • ...
      ...


    Attributes
    • interval
      Poll interval in seconds (300)

    • cookie
      Amazon access cookie, has to be entered for the module to work

    • server
      Amazon server used for controlling the Echo

=end html_DE =cut