Esp8266 (Wemos d1 mini). Управление телевизорами по сети в Яндекс умный дом (4DUK)

Основной скетч управления.
File - wifitvpult.ino

#include <FS.h>
#include <LittleFS.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <ESP8266mDNS.h>
#define DEBUG
#define DEBUG_WS
#include <ESP8266HTTPClient.h>
#include "4duk.h"
#include "WebSocketClient.h"


#include "fsio.h"
#define SSDP_MCAST 239,255,255,250
#define SSDP_PORT 1900

#define STASSID "MYWIFI"

#define STAPSK "topsecret"

long timezone = 2;
byte daysavetime = 1;
unsigned char buf[2048];
char g_id[128]="i000000000000000000000000000000d";
char room[128]="Настроечная";
char prod[128]="Alex Software";
char mail[128]="alex@ilona.su";
char d_name[128];
char a_name[128];

const char sony_remote_js[]="http://4duk.ilona.su/staticdownload/esp8266/sonyremote.js";
const char sony_js[]="/sonyremote.js";
const char root_page_b[] = "<html><head>\
<meta charset=\"utf8\">\
<title>Пульт TV</title>\
</head>\
<body>\
<h1>Пульт TV</h1>\
<a href=\"/updatetvs\">Обновить список телевизоров</a>\
<br>";
const char root_page_e[]="<form>IP address TV<input id=\"tvip\" type=\"text\">\
</form><br>\
<button onclick=\"sendcommand(\'test\');\">ON</button>\
<button>OFF</button>\
<script type=\"text/javascript\" src=\"/sonyremote.js\"></script>\
</body></html>";

gate4duk gate;
WiFiUDP Udp;
IPAddress ip_mcast(SSDP_MCAST);
IPAddress bcast;
String  mmc;
IPAddress mip;
char buffer[2048];
ESP8266WebServer HTTP(80);
char gateid[128];
char mdns_name[]="PULT4DUK";
bool is_rcmd;
String r_cmd;

static void proci_cmd(String pp_cmd) {
  is_rcmd=true;
  r_cmd=pp_cmd;
  r_cmd.replace("\n","");
  r_cmd.replace("\r","");
}

static void proca_cmd(String pp_cmd) {
  String p_cmd=pp_cmd;
  p_cmd.replace("\n","");
  Serial.println(p_cmd);
  String f_s="device:tv";
  if (p_cmd.indexOf(f_s)>-1) {
    String ss_mac=p_cmd.substring(p_cmd.indexOf("device:tv")+9,p_cmd.indexOf(":",p_cmd.indexOf("device:tv")+9));
    
    Serial.printf("Raw mac %s\n",ss_mac.c_str());
    String s_mac=String(ss_mac.substring(0,2)+String(":")+ss_mac.substring(2,4)+String(":")+ss_mac.substring(4,6)+String(":")+ss_mac.substring(6,8)+String(":")+ss_mac.substring(8,10)+String(":")+ss_mac.substring(10,12));
    Serial.printf("Detected mac %s\n",s_mac.c_str());
    Serial.printf("From Alice Send %s to %s\n",p_cmd.c_str(),s_mac.c_str());
    if (p_cmd.indexOf("action:on_off:value:on")>-1) {
      //Serial.println("ALICE POWERON");
      tv_ctl(String(s_mac+"-POWERON"));
    } else if (p_cmd.indexOf("action:on_off:value:off")>-1) {
      tv_ctl(String(s_mac+"-POWEROFF"));
    } else if (p_cmd.indexOf("action:volume:value:+")>-1) {
      tv_ctl(String(s_mac+"-VOLUP"));
    } else if (p_cmd.indexOf("action:volume:value:-")>-1) {
      tv_ctl(String(s_mac+"-VOLDOWN"));
    } else if (p_cmd.indexOf("action:volume:")>-1) {
      String s_l=p_cmd.substring(p_cmd.indexOf("value:")+6);
      tv_ctl(String(s_mac+"-SETVOL "+s_l));
    } else if (p_cmd.indexOf("action:channel:value:+")>-1) {
      tv_ctl(String(s_mac+"-CHANUP"));
    } else if (p_cmd.indexOf("action:channel:value:-")>-1) {
      tv_ctl(String(s_mac+"-CHANDOWN"));
    } else if (p_cmd.indexOf("action:channel:")>-1) {
      String s_l=p_cmd.substring(p_cmd.indexOf("value:")+6);
      tv_ctl(String(s_mac+"-SETCHAN "+s_l));
    } else if (p_cmd.indexOf("action:input_source:value:1")>-1) {
      tv_ctl(String(s_mac+"-TV"));
    } else if (p_cmd.indexOf("action:input_source:value:2")>-1) {
      tv_ctl(String(s_mac+"-HDMI"));
    } else if (p_cmd.indexOf("action:input_source:value:3")>-1) {
      tv_ctl(String(s_mac+"-AV"));
    } else if (p_cmd.indexOf("action:mute:")>-1) {
      tv_ctl(String(s_mac+"-MUTE"));
    }
  }
}

String proca_stat(String p_cmd) {
  Serial.println(p_cmd);
  String $tv_mac_raw=p_cmd.substring(p_cmd.indexOf("tv")+2,p_cmd.indexOf(":",p_cmd.indexOf("tv")+2));
  String $tv_mac=$tv_mac_raw.substring(0,2)+":"+$tv_mac_raw.substring(2,4)+":"+$tv_mac_raw.substring(4,6)+":"+$tv_mac_raw.substring(6,8)+":"+$tv_mac_raw.substring(8,10)+":"+$tv_mac_raw.substring(10,12);
  if (LittleFS.exists(String($tv_mac+".on"))) {
    return p_cmd+"on_off:on";
  } else {
    return p_cmd+"on_off:off";
  }
  return p_cmd;
  
}

//handles for HTTP
void handleNotFound() {
  String s_uri=HTTP.uri();
  String o_m;
  s_uri=s_uri.substring(1);
 if (s_uri.indexOf("/rctv")>-1) {
    //o_m=show_rctv(HTTP.arg(0));
    HTTP.send(200, "text/plain", o_m);
 } else if (LittleFS.exists(s_uri.c_str())) {
    
   if ((s_uri.indexOf(".dsc")>-1) && (s_uri.indexOf(".dsc/")==-1)) {
    HTTP.send(200, "text/html", listDir(WiFi.localIP().toString(),s_uri));
   } else if (s_uri.indexOf(".html")>-1) {
      
      File html=LittleFS.open(s_uri.c_str(),"r");
      HTTP.stream(html,"text/html");
      html.close();
   } else if (s_uri.indexOf(".js")>-1) {
      
      File js=LittleFS.open(s_uri.c_str(),"r");
      HTTP.stream(js,"application/javascript");
      js.close();
   } else {
      
      File txt=LittleFS.open(s_uri.c_str(),"r");
      HTTP.stream(txt,"text/plain");
      txt.close();
   }
  
 } else {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += HTTP.uri();
  message += "\nMethod: ";
  message += (HTTP.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += HTTP.args();
  message += "\n";
  for (uint8_t i = 0; i < HTTP.args(); i++) { message += " " + HTTP.argName(i) + ": " + HTTP.arg(i) + "\n"; }
  HTTP.send(404, "text/plain", message);
  Serial.println(message);
 }
}

void tvcontrol() {
  if (HTTP.method() == HTTP_POST) {
    String a1=HTTP.arg("plain");
    Serial.println("Get name 0 "+a1);
    HTTP.send(200,"text/plain",a1);
    //tv_up(a1);
    tv_ctl(a1);
  } else {
    HTTP.send(404, "text/plain", "Denied");
  }
}

void waketv() {
  if (HTTP.method() == HTTP_POST) {
    String a1=HTTP.arg("plain");
    Serial.println("Get name 0 "+a1);
    HTTP.send(200,"text/plain",a1);
    tv_up(a1);
  } else {
    HTTP.send(404, "text/plain", "Denied");
  }
}

void deletetv() {
  if (HTTP.method() == HTTP_POST) {
    String a1=HTTP.arg("plain");
    Serial.println("Get name 0 "+a1);
    HTTP.send(200,"text/plain",a1);
    removetv(a1);
  } else {
    HTTP.send(404, "text/plain", "Denied");
  }
}

void gettvvolume() {
  if (HTTP.method() == HTTP_POST) {
    String a1=HTTP.arg("plain");
    Serial.println("Get name 0 "+a1);
    
    HTTP.send(200,"text/plain",gtvvol(a1));
    //gtvvol(a1);
  } else {
    HTTP.send(404, "text/plain", "Denied");
  }
}

void addth() {
  if (HTTP.method() == HTTP_POST) {
    String a1=HTTP.arg("plain");
    Serial.println("Get name 0 "+a1);
    HTTP.send(200,"text/plain",a1);
    tvtoth(a1);
  } else {
    HTTP.send(404, "text/plain", "Denied");
  }
}

void handleRoot() {
  
  String root_page=String(root_page_b)+getlistth()+String(root_page_e);
  HTTP.send(200,"text/html",root_page);
}

void updatetvs() {
  
  parse_tvs();
  String p_head="<html><head><meta charset=\"utf8\"><title>Обновлено</title></head><body><h1>Список телевизоров</h1><br>";
  String p_foot="</body></html>";
  String all=p_head+String("Обновлено.<a href=\"/\">На главную</a>")+p_foot;

  HTTP.send(200,"text/html",all);
}

void listtv() {
  
  String p_head="<html><head><meta charset=\"utf8\"><title>Список телевизоров</title></head><body><h1>Список телевизоров</h1><br>";
  String p_foot="</body></html>";
  String p_body="";

  HTTP.send(200,"text/html",p_head+listtvname("")+p_foot);
}

void hlistDir() {
  String pc=listDir(WiFi.localIP().toString(),"/");
  HTTP.send(200,"text/html",pc);
}

void handlejsload() {
  
  
    File file = LittleFS.open(sony_js, "r");
    HTTP.streamFile(file, "text/javascript");
    file.close();
  
  
}


void setup() {
  
  r_cmd=false;
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.println("Boot");
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println(WiFi.broadcastIP());
  
  bcast=WiFi.broadcastIP();
  mip=WiFi.localIP();
  mmc=WiFi.macAddress();
  mmc.replace(":","");
  configTime(3600 * timezone, daysavetime * 3600, "0.pool.ntp.org", "1.pool.ntp.org");
  struct tm tmstruct;
  delay(2000);
  
  
  tmstruct.tm_year = 0;
  
  getLocalTime(&tmstruct, 5000);
  Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
  Udp.beginMulticast(WiFi.localIP(),ip_mcast,SSDP_PORT);
  
  
  HTTP.on("/", handleRoot);
  HTTP.on("/sonyremote.js", handlejsload);
  HTTP.on("/listtv", listtv);
  HTTP.on("/waketv", waketv);
  HTTP.on("/deletetv", deletetv);
  HTTP.on("/tvcommand", tvcontrol);
  HTTP.on("/tvtoth", addth);
  HTTP.on("/ls", hlistDir);
  HTTP.on("/getvolume", gettvvolume);
  HTTP.on("/updatetvs", updatetvs);
  HTTP.onNotFound(handleNotFound);
  HTTP.begin();
  if (!LittleFS.begin()) {
    Serial.println("LittleFS mount failed. Formatting...");
    LittleFS.format();
  }
  
  if (!LittleFS.begin()) {
    Serial.println("LittleFS remount failed.");

  }

  
  
    download_sfile("https://4duk.su/downloads/tvremote/esp8266/sonyremote.js","sonyremote.js",false);
    download_sfile("https://4duk.su/downloads/tvremote/esp8266/tvcontrol.js","tvcontrol.js",false);
    //download_sfile("https://4duk.su/downloads/tvremote/esp8266/tvcontrol.html","tvcontrol.html",false);
    download_sfile("https://4duk.su/downloads/tvremote/esp8266/tvcontrol1.html","tvcontrol1.html",false);
    download_sfile("https://4duk.su/downloads/tvremote/esp8266/tvcontrol2.html","tvcontrol2.html",false);
    download_sfile("https://4duk.su/idgate?inpip="+mip.toString()+",mac="+mmc,"gateid.txt",false);
    if (LittleFS.exists("gateid.txt")) {
      File ff=LittleFS.open("gateid.txt","r");
      uint8_t gg_id[128];
      int rr=ff.read(gg_id,80);
      gg_id[rr]=0;
      ff.close();
      for (int i=0;i<=rr;i++) {
        g_id[i]=gg_id[i];
      }
      //strncpy(g_id,gg_id,rr);
    }
    gate.setid(g_id);
    gate.setname(mdns_name);
    gate.setbcast(bcast);
    gate.connect(true);
    Dir rr=LittleFS.openDir("");
    while (rr.next()) {
    String rr_fn=rr.fileName();
      if (rr_fn.indexOf(".th")>-1) {
      String p_mac=rr_fn.substring(0,rr_fn.indexOf(".th"));
      String n_mac=getFile(String("/"+p_mac+".macname").c_str());
      p_mac.replace(":","");
      //p_mac="tv"+p_mac;
      //n_mac="TV-"+n_mac;
      sprintf(d_name,"tv%s",p_mac.c_str());
      sprintf(a_name,"TV %s",n_mac.c_str());
      //const char room[]="Настроечная";
      //const char mail[]="slex@ilona.su";
      //const char prod[]="Alex Software";
      //gate.addrealdev(DEV_MEDIA_DEVICE_TV,p_mac,n_mac,"Настроечная","Alex Software","alex@ilona.su",0.1,proca_cmd,proca_stat);
      Serial.printf("Add device %s,%s\n",d_name,a_name);
      gate.addrealdev(DEV_MEDIA_DEVICE_TV,d_name,a_name,room,prod,mail,0.1,proci_cmd,proca_stat);
      //download_sfile("https://4duk.su/idgate?adddev=yes,mac="+mmc+",dtype="+String(DEV_MEDIA_DEVICE_TV).toInt()+",dname="+d_name+",aname="+a_name,String(p_mac+".txt"),false);
      }
    }
    
    
}

void loop() {
  // put your main code here, to run repeatedly:
  int plen=0;
  int i;
  if (!gate.is_connected()) {
    gate.reconnect(true);
  }
  gate.processmqtt();
  gate.processudp();
  if (Udp.available()>0) {
    plen=Udp.parsePacket();
    memset(buffer,0,2048);
    Udp.read(buffer,2048);
    buffer[plen]=0;
    String bb=String(buffer);
    
      if (bb.indexOf("ssdp:alive")>0) {
        Serial.print("Add ");
        Serial.println(Udp.remoteIP());
        int pos_loc=bb.indexOf("http://");
        int pos_loc_end=bb.indexOf("\r\n",pos_loc);
        String loc_url_avtransport=bb.substring(pos_loc,pos_loc_end);
        Serial.print("==");
        Serial.print(loc_url_avtransport);
        
        Serial.println("==");
        get_url_content(loc_url_avtransport);
        String rip="/"+Udp.remoteIP().toString()+".url";
        String d_mac=get_mac(Udp.remoteIP().toString());
        writeFile(String("/"+d_mac+".macip").c_str(),Udp.remoteIP().toString().c_str());
        writeFile(String("/"+Udp.remoteIP().toString()+".ipmac").c_str(),d_mac.c_str());
        writeFile(String("/"+d_mac+".on").c_str(),"0");
        writeFile(String("/"+Udp.remoteIP().toString()+".on").c_str(),"0");
        if (bb.indexOf(":service:")>-1) {
          if (!LittleFS.exists(String(d_mac+".dsc").c_str())) {
            if(!LittleFS.mkdir(String(d_mac+".dsc").c_str())) {
              Serial.printf("Error create %s\n",String(d_mac+".dsc").c_str());
            }
          }
          String s_service=bb.substring(bb.indexOf(":service:")+9,bb.indexOf("\r\n",bb.indexOf(":service:")+9));
          if (s_service.length()>30) {
            s_service=s_service.substring(0,30);
          }
          writeFile(String(d_mac+".dsc/"+s_service).c_str(),loc_url_avtransport.c_str());
        }
        
      } else if (bb.indexOf("ssdp:byebye")>0) {
        Serial.print("Remove ");
        Serial.println(Udp.remoteIP());
        String mac_ip=getFile(String(String(Udp.remoteIP().toString())+".ipmac").c_str());
        LittleFS.remove(String(mac_ip+".on").c_str());
        LittleFS.remove(String(String(Udp.remoteIP().toString())+".on").c_str());
      } else {
        //Serial.print(plen);
        //Serial.print(" ");
        //Serial.println(bb);
      }
    
    Udp.flush();
    Udp.stop();
    Udp.beginMulticast(WiFi.localIP(),ip_mcast,SSDP_PORT);
  } else {
    delay(50);
  }
  if (is_rcmd) {
    is_rcmd=false;
    proca_cmd(r_cmd);
  }
  gate.sendstatus();
  //delay(1000);
  //Serial.println("Step .");
  HTTP.handleClient();
  MDNS.update();
  delay(2000);
  
  
}

Управление телевизорами.
File - tvctl.ino

void addtocontrol(String mac,String nametv) {
//  writeFile("/"+mac+".tv",nametv);
}

void delcontrol(String mac) {
//  deleteFile("/"+mac+".tv");
}

String gtvvol(String mac) {
  if (LittleFS.exists(String(mac+".irccurl").c_str())) {
    return String(get_volume(mac));
  } else if (LittleFS.exists(String(mac+".macwebos").c_str())) {
    return String(lg_getvol(mac));
  } else if (LittleFS.exists(String(mac+".macsmhub").c_str())) {
    return String(get_volume(mac));
  } else {
    
  }
  return String("0");
}

static void tv_ctl(String str_ctl) {
  String mac;
  String s_cmd;
  if (str_ctl.indexOf("-")>-1) {
    mac=str_ctl.substring(0,str_ctl.indexOf("-"));
    s_cmd=str_ctl.substring(str_ctl.indexOf("-")+1);
    Serial.printf("Command %s to %s\n",s_cmd.c_str(),mac.c_str());
    if (LittleFS.exists(String(mac+".irccurl").c_str())) {
      String ip_sony=getFile(String(mac+".macip").c_str());
      if (s_cmd.indexOf("POWERON")>-1) {
        tv_up(mac);
      } else if (s_cmd.indexOf("POWEROFF")>-1) {
        sonybravia_poweroff(mac);
      } else if (s_cmd.indexOf("VOLDOWN")>-1) {
        sonybravia_voldec(mac);
      } else if (s_cmd.indexOf("VOLUP")>-1) {
        sonybravia_volinc(mac);
      } else if (s_cmd.indexOf("CHANUP")>-1) {
        sonybravia_chanup(mac);
      } else if (s_cmd.indexOf("CHANDOWN")>-1) {
        sonybravia_chandown(mac);
      } else if (s_cmd.indexOf("MUTE")>-1) {
        sonybravia_mute(mac);
      } else if (s_cmd.indexOf("HDMI")>-1) {
        sonybravia_set_input(mac,"hdmi2");
      } else if (s_cmd.indexOf("TV")>-1 && s_cmd.indexOf("SETVOL")==-1) {
        sonybravia_set_tvmode(mac,true);
      } else if (s_cmd.indexOf("AV")>-1) {
        sonybravia_set_tvmode(mac,false);
      } else if (s_cmd.indexOf("SETVOL")>-1) {
        String vl=s_cmd.substring(s_cmd.indexOf("SETVOL")+7);
        set_volume(mac,vl.toInt());
      } else if (s_cmd.indexOf("SETCHAN")>-1) {
        String vl=s_cmd.substring(s_cmd.indexOf("SETCHAN")+8);
        sonybravia_set_channel(mac,vl);
      } else {
        //sonybravia_set_channel(mac,s_cmd);
      }
    } else if (LittleFS.exists(String(mac+".macwebos").c_str())) {
      String ip_sony=getFile(String(mac+".macip").c_str());
      if (s_cmd.indexOf("POWERON")>-1) {
        
        tv_up(mac);
        return;
      }
        if (!LittleFS.exists(String(mac+".ckey").c_str())) {
        if (lg_pairing(mac)==1) {
          Serial.println("Paring done"); 
        } else {
          Serial.println("Paring failed"); 
        }
        }
      
      /*if (s_cmd.indexOf("POWERON")>-1) {
        
        tv_up(mac);
      } else*/ 
      if (s_cmd.indexOf("POWEROFF")>-1) {
        Serial.println("LGWEBOS poweroff");
        //sonybravia_poweroff(mac);
        lg_command(mac,s_cmd);
      } else if (s_cmd.indexOf("VOLDOWN")>-1) {
        //sonybravia_voldec(mac);
      } else if (s_cmd.indexOf("VOLUP")>-1) {
        //sonybravia_volinc(mac);
      } else if (s_cmd.indexOf("CHANUP")>-1) {
        //sonybravia_chanup(mac);
      } else if (s_cmd.indexOf("CHANDOWN")>-1) {
        //sonybravia_chandown(mac);
      } else if (s_cmd.indexOf("MUTE")>-1) {
        lg_command(mac,s_cmd);
        //sonybravia_mute(mac);
      } else if (s_cmd.indexOf("SETCHAN")>-1) {
        lg_command(mac,s_cmd);
      } else if (s_cmd.indexOf("SETVOL")>-1) {
        lg_command(mac,s_cmd);
      } else if (s_cmd.indexOf("HDMI")>-1) {
        lg_command(mac,s_cmd);
        //sonybravia_set_input(mac,"hdmi2");
      } else if (s_cmd.indexOf("TV")>-1 && (s_cmd.indexOf("SETVOL")==-1)) {
        
      } else if (s_cmd.indexOf("AV")>-1) {
        //sonybravia_set_tvmode(mac,false);
      } else {
        //sonybravia_set_channel(mac,s_cmd);
      }
    } else if (LittleFS.exists(String(mac+".macsmhub").c_str())) {
      String ip_sams=getFile(String(mac+".macip").c_str());
      int last_connected=1;
      WiFiClient sams;
      String mip=WiFi.localIP().toString();
      String mmc=WiFi.macAddress();
      if (sams.connect(ip_sams,55000)) {
    //if (sams.connect("192.168.123.99",55000)) {
    //nn=0;
      last_connected=0;
      Serial.println("Samsung TCP connected!!!");
      samsung_tcp_auth(sams,mip,mmc);
      } else {
      Serial.println("Samsung TCP connection failed");
    //delay(5000);
      last_connected=1;
      return;
      }
      if (s_cmd.indexOf("POWERON")>-1) {
        
        tv_up(mac);

        //sam
        return;
      }
      
        /*if (lg_pairing(mac)==1) {
          Serial.println("Paring done"); 
        } else {
          Serial.println("Paring failed"); 
        }*/
      
      /*if (s_cmd.indexOf("POWERON")>-1) {
        
        tv_up(mac);
      } else*/ 
      Serial.println("SAMS process");
      if (s_cmd.indexOf("POWEROFF")>-1) {
        Serial.println("SAMS HUB poweroff");
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_poweroff(mac);
        
      } else if (s_cmd.indexOf("VOLDOWN")>-1) {
        Serial.println("SAMS VOLDOWN "+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_voldec(mac);
      } else if (s_cmd.indexOf("VOLUP")>-1) {
        Serial.println("SAMS VOLUP "+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_volinc(mac);
      } else if (s_cmd.indexOf("CHANUP")>-1) {
        Serial.println("SAMS CHANUP "+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_chanup(mac);
      } else if (s_cmd.indexOf("CHANDOWN")>-1) {
        Serial.println("SAMS CHANDOWN"+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_chandown(mac);
      } else if (s_cmd.indexOf("MUTE")>-1) {
        Serial.println("SAMS MUTE"+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_mute(mac);
      } else if (s_cmd.indexOf("HDMI")>-1) {
        Serial.println("SAMS HDMI"+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_set_input(mac,"hdmi2");
      } else if (s_cmd.indexOf("TV")>-1 && s_cmd.indexOf("SETVOL")==-1) {
        Serial.println("SAMS TV"+s_cmd);
        samsung_tcp_key(sams,s_cmd);
        //sonybravia_set_tvmode(mac,true);
      } else if (s_cmd.indexOf("AV")>-1) {
        Serial.println("SAMS "+s_cmd);
      } else if (s_cmd.indexOf("SETVOL")>-1) {
        Serial.println("SAMS "+s_cmd);
        Serial.println("SETVOL");
        int ss_vol=String(s_cmd.substring(s_cmd.indexOf("SETVOL")+7)).toInt();
        set_volume(mac,ss_vol);
        Serial.printf("Set SAMSUNG vol %d\n",ss_vol);
        //sonybravia_set_tvmode(mac,false);
      } else if (s_cmd.indexOf("SETCHAN")>-1) {
        Serial.println("SAMS "+s_cmd);
        Serial.println("SETCHAN");
        String channum=String(s_cmd.substring(s_cmd.indexOf("SETCHAN")+8));
        int jj;
        for(jj=0;jj<channum.length();jj++) {
          samsung_tcp_key(sams,String(channum.charAt(jj)));
          Serial.printf("Send key %s\n",String(channum.charAt(jj)).c_str());
          delay(300);
        }
        Serial.println("SEND ENTER");
        samsung_tcp_key(sams,"ENTER");
        //set_volume(mac,ss_vol);
        //Serial.printf("Set SAMSUNG vol %d\n",ss_vol);
      } else {
        Serial.println("SAMS OTHER "+s_cmd);
        //sonybravia_set_channel(mac,s_cmd);
      }
      sams.stop();
    }
  }
}

File - tvwol.ino

#include <WakeOnLan.h>

void tv_up(String tv_mac) {
WiFiUDP UDP;
WakeOnLan WOL(UDP);
  Serial.println("Wake "+tv_mac);
  //char MACAddress[30] = tv_mac.c_str();
  WOL.setRepeat(3, 100);
  WOL.calculateBroadcastAddress(WiFi.localIP(), WiFi.subnetMask()); // Optional  => To calculate the broadcast address, otherwise 255.255.255.255 is used (which is denied in some networks).
  WOL.sendMagicPacket(tv_mac);

}

File - sony_remote.ino

#include <LittleFS.h>
#include <ESP8266HTTPClient.h>

int sonybravia_get_volume_max(String mac) {
  return 100;
}

int sonybravia_get_volume(String mac) {
  return get_volume(mac);
}

void sonybravia_set_volume(String mac,int v_level) {
  set_volume(mac,v_level);
}

void sonybravia_poweroff(String mac) {
  String soap_auth="0000";
  String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  
  xml_ir_cmd=xml_ir_cmd+"AAAAAQAAAAEAAAAvAw=="+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_chanup(String mac) {
  String soap_auth="0000";
  String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  
  xml_ir_cmd=xml_ir_cmd+"AAAAAQAAAAEAAAAQAw=="+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_chandown(String mac) {
  String soap_auth="0000";
  String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  
  xml_ir_cmd=xml_ir_cmd+"AAAAAQAAAAEAAAARAw=="+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_mute(String mac) {
  String soap_auth="0000";
  String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  
  xml_ir_cmd=xml_ir_cmd+"AAAAAQAAAAEAAAAUAw=="+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_volinc(String mac) {
  int t_vol=get_volume(mac);
  if ((t_vol+1)>100) {
    set_volume(mac,100);
  } else {
    set_volume(mac,t_vol+1);
  }
}

void sonybravia_voldec(String mac) {
  int t_vol=get_volume(mac);
  if ((t_vol-1)<0) {
    set_volume(mac,0);
  } else {
    set_volume(mac,t_vol-1);
  }
}

void sonybravia_set_tvmode(String mac,bool m_ode=true) {
  String m_digital="AAAAAgAAAJcAAAAyAw==";
  String m_analog="AAAAAgAAAHcAAAANAw==";
  String soap_auth="0000";
  String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  if (m_ode) {
  xml_ir_cmd=xml_ir_cmd+m_digital+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  } else {
  xml_ir_cmd=xml_ir_cmd+m_digital+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  }
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_set_input(String mac,String in_put) {
  String hdmi1="AAAAAgAAABoAAABaAw==";
  String hdmi2="AAAAAgAAABoAAABbAw==";
  String hdmi3="AAAAAgAAABoAAABcAw==";
  String hdmi4="AAAAAgAAABoAAABdAw==";
  String soap_auth="0000";
  //String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  if (in_put.indexOf("hdmi1")>-1) {
  xml_ir_cmd=xml_ir_cmd+hdmi1+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  } else if (in_put.indexOf("hdmi2")>-1) {
  xml_ir_cmd=xml_ir_cmd+hdmi2+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  } else if (in_put.indexOf("hdmi3")>-1) {
  xml_ir_cmd=xml_ir_cmd+hdmi3+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  } else if (in_put.indexOf("hdmi4")>-1) {
  xml_ir_cmd=xml_ir_cmd+hdmi4+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  }
  soap_send_sonybravia(mac,soap_action,xml_ir_cmd,soap_auth);
}

void sonybravia_set_channel(String mac, String n_channel) {
  String ncode[10];
  ncode[0]="AAAAAQAAAAEAAAAJAw==";
  ncode[1]="AAAAAQAAAAEAAAAAAw==";
  ncode[2]="AAAAAQAAAAEAAAABAw==";
  ncode[3]="AAAAAQAAAAEAAAACAw==";
  ncode[4]="AAAAAQAAAAEAAAADAw==";
  ncode[5]="AAAAAQAAAAEAAAAEAw==";
  ncode[6]="AAAAAQAAAAEAAAAFAw==";
  ncode[7]="AAAAAQAAAAEAAAAGAw==";
  ncode[8]="AAAAAQAAAAEAAAAHAw==";
  ncode[9]="AAAAAQAAAAEAAAAIAw==";
  String enter="AAAAAQAAAAEAAABlAw==";
  String soap_auth="0000";
  //String url_ircc=getFile(String("/"+mac+".irccurl").c_str());
  String soap_action="urn:schemas-sony-com:service:IRCC:1#X_SendIRCC";
  String xml_ir_cmd="<?xml version=\"1.0\"?>\
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\
  <s:Body><u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\
  <IRCCCode>";
  String xml_ir_cmdr;
  int len_c=n_channel.length();
  int i=0;
  for(i=0;i<len_c;i++) {
    char nn=n_channel.charAt(i);
    switch (nn) {
      case '1':
      xml_ir_cmdr=xml_ir_cmd+ncode[1]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '2':
      xml_ir_cmdr=xml_ir_cmd+ncode[2]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '3':
      xml_ir_cmdr=xml_ir_cmd+ncode[3]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '4':
      xml_ir_cmdr=xml_ir_cmd+ncode[4]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '5':
      xml_ir_cmdr=xml_ir_cmd+ncode[5]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '6':
      xml_ir_cmdr=xml_ir_cmd+ncode[6]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '7':
      xml_ir_cmdr=xml_ir_cmd+ncode[7]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '8':
      xml_ir_cmdr=xml_ir_cmd+ncode[8]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '9':
      xml_ir_cmdr=xml_ir_cmd+ncode[9]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
      case '0':
      xml_ir_cmdr=xml_ir_cmd+ncode[0]+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
      break;
    }
    soap_send_sonybravia(mac,soap_action,xml_ir_cmdr,soap_auth);
  }
  xml_ir_cmdr=xml_ir_cmd+enter+"</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>";
  soap_send_sonybravia(mac,soap_action,xml_ir_cmdr,soap_auth);
}

File - samsung_crypto.ino

//#include <FS.h>
//#include <LittleFS.h>
//#include <ESP8266WiFi.h>
//#include <ESP8266WebServer.h>
//#include <WiFiUdp.h>
//#include <ESP8266mDNS.h>
//#include <ESP8266HTTPClient.h>
#include <WString.h>
#include <base64.h>
#include <HardwareSerial.h>
#include <WiFiClient.h>
//String rem_app="iphone..iapp.samsung";
//String rem_name="rc.4duk.su";

void samsung_tcp_auth(WiFiClient sams,String mip,String mmc) {
  String b64mip="";
  String b64mmc="";
  String b64rn="";
  String msg1="";
  String part1="";
  String rem_app="iphone..iapp.samsung";
String rem_name="rc.4duk.su";
  char buff[1024];
  char buf[1024];
  //int i;
  b64mip=base64::encode(mip);
  //b64mip=base64::encode("192.168.123.99");
  //b64mmc=base64::encode("EC-FA-BC-C2-30-DC");
  //b64mmc=base64::encode("ec-fa-bc-c2-30-dc");
  //b64mmc=base64::encode("ba-be-53-b2-b0-4a");
  b64mmc=base64::encode(mmc);
  b64rn=base64::encode(rem_name);
  //msg1=String(char(64)+char(0x00)+char(b64mip.length())+char(0x00)+b64mip+char(b64mmc.length())+char(0x00)+b64mmc+char(b64rn.length())+char(0x00)+b64rn);
  //msg1='d'+char(0x00)+char(b64mip.length())+char(0x00)+b64mip+char(b64mmc.length())+char(0x00)+b64mmc+char(b64rn.length())+char(0x00)+b64rn;
  //part1=char(0x0)+char(rem_app.length())+char(0x0)+rem_app+char(msg1.length())+char(0x0)+msg1;
  //part1=char(0x0)+char(rem_app.length())+char(0x0)+rem_app+char(msg1.length())+char(0x0)+msg1;
  //part1=char(0x00)+char(rem_app.length())+char(0x00)+rem_app+char(80)+char(0x00)+msg1;
  int i=0;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  buf[i]=char(rem_app.length());
  //buf[i]='#';
  i++;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  rem_app.toCharArray(buf+i,rem_app.length()+1);
  i=i+rem_app.length();
  //Serial.println(2+2+b64mip.length()+2+b64mmc.length()+2+b64rn.length(),HEX);
  buf[i]=char(2+2+b64mip.length()+2+b64mmc.length()+2+b64rn.length());
  //buf[i]='#';
  //i=i+2+2+b64mip.length()+2+b64mmc.length()+2+b64rn.length()+1;
  i++;
  buf[i]=char(0x00);
  //Serial.println(0x64,HEX);
  //buf[i]='#';
  i++;
  buf[i]=char(0x64);
  //buf[i]='#';
  i++;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  buf[i]=char(b64mip.length());
  //Serial.println(b64mip.length(),HEX);
  //buf[i]='#';
  i++;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  b64mip.toCharArray(buf+i,b64mip.length()+1);
  i=i+b64mip.length();
  buf[i]=char(b64mmc.length());
  //buf[i]='#';
  i++;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  b64mmc.toCharArray(buf+i,b64mmc.length()+1);
  i=i+b64mmc.length();
  buf[i]=char(b64rn.length());
  //buf[i]='#';
  i++;
  buf[i]=char(0x00);
  //buf[i]='#';
  i++;
  b64rn.toCharArray(buf+i,b64rn.length()+1);
  i=i+b64rn.length();
  //buf[i]=char(10);
  int ps1=i;
  //Serial.println(i);
  //int rrr=samsungtv_authenticate("192.168.123.99",0);
  //Serial.println(b64mip);
  //Serial.println(b64mip.length());
  //Serial.println(b64mmc);
  //Serial.println(b64mmc.length());
  //Serial.println(b64rn);
  //Serial.println(b64rn.length());
  //Serial.println(msg1.length());
  //Serial.println(msg1);
  //Serial.println(part1);
  //sams.print(part1);
  //buf
  //sams.write(part1.c_str(),part1.length());
  buf[ps1+1]=char(0x00);
  Serial.write(buf,ps1);
  sams.write(buf,ps1);
}

void samsung_tcp_key(WiFiClient sams,String p_cmd) {
  //String msg3;
  //String part3;
  String s_cmd="KEY_"+p_cmd;
  String b64key;
  String key_menu="KEY_TV";
  String rem_app="iphone..iapp.samsung";
String rem_name="rc.4duk.su";
String tv_app="iphone.UE32C6500.iapp.samsung";
char buff[1024];
  int i;
  //b64key=base64::encode(key_menu);
  b64key=base64::encode(s_cmd);
  //msg2=char(0xc8)+char(0x0);
  //part2=char(0x0)+char(rem_app.length())+char(0x0)+rem_app+char(msg2.length())+char(0x0)+msg2;
  //msg3=char(0x0)+char(0x0)+char(0x0)+char(b64key.length())+char(0x0)+b64key;
  //part3=char(0x0)+char(tv_app.length())+char(0x0)+tv_app+char(msg3.length())+char(0x0)+msg3;
  i=0;
  buff[i]=char(0x00);
  i++;
  buff[i]=rem_app.length();
  i++;
  buff[i]=char(0x00);
  i++;
  rem_app.toCharArray(buff+i,rem_app.length()+1);
  i=i+rem_app.length();
  buff[i]=char(0x02);
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(0xc8);
  i++;
  buff[i]=char(0x00);
  // end msg2
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(tv_app.length());
  i++;
  buff[i]=char(0x00);
  i++;
  tv_app.toCharArray(buff+i,tv_app.length()+1);
  i=i+tv_app.length();
  buff[i]=5+b64key.length();
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(0x00);
  i++;
  buff[i]=char(b64key.length());
  i++;
  buff[i]=char(0x00);
  i++;
  b64key.toCharArray(buff+i,b64key.length()+1);
  i=i+b64key.length();
  int ps2=i;
  //Serial.print(part3);
  //sams.print(part3);
  sams.write(buff,ps2);
  Serial.write(buff,ps2);
}

File - samsung_crypto.h

void samsung_tcp_auth(WiFiClient sams,String mip,String mmc);
void samsung_tcp_key(WiFiClient sams,String s_cmd);

File - lgwebos.ino

int lg_pair(String ip,String url, String lfs, bool fdebug = false) {
  String str_auth="{\"id\": \"register_0\", \"payload\": {\"forcePairing\": false, \"manifest\": {\"appVersion\": \"1.1\", \"manifestVersion\": 1, \"permissions\": [\"LAUNCH\", \"LAUNCH_WEBAPP\", \"APP_TO_APP\", \"CLOSE\", \"TEST_OPEN\", \"TEST_PROTECTED\", \"CONTROL_AUDIO\", \"CONTROL_DISPLAY\", \"CONTROL_INPUT_JOYSTICK\", \"CONTROL_INPUT_MEDIA_RECORDING\", \"CONTROL_INPUT_MEDIA_PLAYBACK\", \"CONTROL_INPUT_TV\", \"CONTROL_POWER\", \"READ_APP_STATUS\", \"READ_CURRENT_CHANNEL\", \"READ_INPUT_DEVICE_LIST\", \"READ_NETWORK_STATE\", \"READ_RUNNING_APPS\", \"READ_TV_CHANNEL_LIST\", \"WRITE_NOTIFICATION_TOAST\", \"READ_POWER_STATE\", \"READ_COUNTRY_INFO\", \"READ_SETTINGS\", \"CONTROL_TV_SCREEN\", \"CONTROL_TV_STANBY\", \"CONTROL_FAVORITE_GROUP\", \"CONTROL_USER_INFO\", \"CHECK_BLUETOOTH_DEVICE\", \"CONTROL_BLUETOOTH\", \"CONTROL_TIMER_INFO\", \"STB_INTERNAL_CONNECTION\", \"CONTROL_RECORDING\", \"READ_RECORDING_STATE\", \"WRITE_RECORDING_LIST\", \"READ_RECORDING_LIST\", \"READ_RECORDING_SCHEDULE\", \"WRITE_RECORDING_SCHEDULE\", \"READ_STORAGE_DEVICE_LIST\", \"READ_TV_PROGRAM_INFO\", \"CONTROL_BOX_CHANNEL\", \"READ_TV_ACR_AUTH_TOKEN\", \"READ_TV_CONTENT_STATE\", \"READ_TV_CURRENT_TIME\", \"ADD_LAUNCHER_CHANNEL\", \"SET_CHANNEL_SKIP\", \"RELEASE_CHANNEL_SKIP\", \"CONTROL_CHANNEL_BLOCK\", \"DELETE_SELECT_CHANNEL\", \"CONTROL_CHANNEL_GROUP\", \"SCAN_TV_CHANNELS\", \"CONTROL_TV_POWER\", \"CONTROL_WOL\"], \"signatures\": [{\"signature\": \"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\", \"signatureVersion\": 1}], \"signed\": {\"appId\": \"com.lge.test\", \"created\": \"20140509\", \"localizedAppNames\": {\"\": \"LG Remote App\", \"ko-KR\": \"\\ub9ac\\ubaa8\\ucee8 \\uc571\", \"zxx-XX\": \"\\u041b\\u0413 R\\u044d\\u043cot\\u044d A\\u041f\\u041f\"}, \"localizedVendorNames\": {\"\": \"LG Electronics\"}, \"permissions\": [\"TEST_SECURE\", \"CONTROL_INPUT_TEXT\", \"CONTROL_MOUSE_AND_KEYBOARD\", \"READ_INSTALLED_APPS\", \"READ_LGE_SDX\", \"READ_NOTIFICATIONS\", \"SEARCH\", \"WRITE_SETTINGS\", \"WRITE_NOTIFICATION_ALERT\", \"CONTROL_POWER\", \"READ_CURRENT_CHANNEL\", \"READ_RUNNING_APPS\", \"READ_UPDATE_INFO\", \"UPDATE_FROM_REMOTE_APP\", \"READ_LGE_TV_INPUT_EVENTS\", \"READ_TV_CURRENT_TIME\"], \"serial\": \"2f930e2d2cfe083771f68e4fe7bb07\", \"vendorId\": \"com.lge\"}}, \"pairingType\": \"PROMPT\"}, \"type\": \"register\"}";
  //WiFiClient hclient;
  std::unique_ptr<BearSSL::WiFiClientSecure> hclient(new BearSSL::WiFiClientSecure);

    //bool mfln = hclient->probeMaxFragmentLength("tls.mbed.org", 3001, 1024);
    bool mfln = hclient->probeMaxFragmentLength(url, 3001, 1024);
    Serial.printf("\nConnecting to %s\n",url.c_str());
    Serial.printf("Maximum fragment Length negotiation supported: %s\n", mfln ? "yes" : "no");
    if (mfln) { hclient->setBufferSizes(1024, 1024); }

    Serial.print("[HTTPS] begin...\n");
    //const uint8_t fingerprint[20] = { 0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a };
    const uint8_t fingerprint[20] = { 0x0D, 0x98, 0xC0, 0x73, 0x7F, 0xAB, 0xBD, 0xBD, 0xD9, 0x47, 0x4B, 0x49, 0xAD, 0x0A, 0x4A, 0x0C, 0xAC, 0x3E, 0xC7, 0x7C};

    //hclient->setFingerprint(fingerprint);
    hclient->setInsecure();
  HTTPClient httpclient;
  if (httpclient.begin(*hclient,url)) {
    int httpCode = httpclient.GET();
    //int httpCode = httpclient.POST(str_auth.c_str());
    if (httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          LittleFS.remove(lfs.c_str());
          File file = LittleFS.open(lfs.c_str(), "w");
          //String payload = httpclient.getString();
          //Serial.println(payload);
          //writeFile(lfs,payload.c_str());
          int len = httpclient.getSize();
          if (len==-1) {
            hclient->write(str_auth.c_str());
          }
          Serial.printf("File size download: %d\n",len);
          static uint8_t buff[1024]={0};
          while (httpclient.connected() && (len > 0 || len == -1)) {
            //size_t size = hclient.available();
            //int size = hclient.available();
            size_t size = hclient->available();
            Serial.print("Avalible size :");
            Serial.println(size);
            if (size) {
              int c = hclient->readBytes(buff,((size > sizeof(buff)) ? sizeof(buff) :size));
              file.write(buff,c);
              Serial.print("Readbyte count :");
              Serial.println(c);
              if (len > 0) {len -= c;}
            } else {
              //hclient->write(str_auth.c_str());
            }
            
            delay(1);
          }
          file.close();
          httpclient.end();
          if (fdebug) {
            debugFile(lfs.c_str());
          }
        } else {
          Serial.println("Error get "+url+" "+lfs);
          httpclient.end();
        }
    } else {
      Serial.println("Error get connect");
      httpclient.end();
    }
  } else {
    Serial.println("Error get begin");
    httpclient.end();
  }
  return 1;
}


int lg_pairing(String mac) {
  String tv_ip=getFile(String(mac+".macip").c_str());
  //String str_auth=String("{\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"manifest\":{\"appVersion\":\"1.1\",\"manifestVersion\": 1,\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\", \"CONTROL_DISPLAY\", \"CONTROL_INPUT_JOYSTICK\", \"CONTROL_INPUT_MEDIA_RECORDING\", \"CONTROL_INPUT_MEDIA_PLAYBACK\", \"CONTROL_INPUT_TV\", \"CONTROL_POWER\", \"READ_APP_STATUS\", \"READ_CURRENT_CHANNEL\", \"READ_INPUT_DEVICE_LIST\", \"READ_NETWORK_STATE\", \"READ_RUNNING_APPS\", \"READ_TV_CHANNEL_LIST\", \"WRITE_NOTIFICATION_TOAST\", \"READ_POWER_STATE\", \"READ_COUNTRY_INFO\", \"READ_SETTINGS\", \"CONTROL_TV_SCREEN\", \"CONTROL_TV_STANBY\", \"CONTROL_FAVORITE_GROUP\", \"CONTROL_USER_INFO\", \"CHECK_BLUETOOTH_DEVICE\", \"CONTROL_BLUETOOTH\", \"CONTROL_TIMER_INFO\", \"STB_INTERNAL_CONNECTION\", \"CONTROL_RECORDING\", \"READ_RECORDING_STATE\", \"WRITE_RECORDING_LIST\", \"READ_RECORDING_LIST\", \"READ_RECORDING_SCHEDULE\", \"WRITE_RECORDING_SCHEDULE\", \"READ_STORAGE_DEVICE_LIST\", \"READ_TV_PROGRAM_INFO\", \"CONTROL_BOX_CHANNEL\", \"READ_TV_ACR_AUTH_TOKEN\", \"READ_TV_CONTENT_STATE\", \"READ_TV_CURRENT_TIME\", \"ADD_LAUNCHER_CHANNEL\", \"SET_CHANNEL_SKIP\", \"RELEASE_CHANNEL_SKIP\", \"CONTROL_CHANNEL_BLOCK\", \"DELETE_SELECT_CHANNEL\", \"CONTROL_CHANNEL_GROUP\", \"SCAN_TV_CHANNELS\", \"CONTROL_TV_POWER\", \"CONTROL_WOL\"], \"signatures\": [{\"signature\": \"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\", \"signatureVersion\": 1}], \"signed\": {\"appId\": \"com.lge.test\", \"created\": \"20140509\", \"localizedAppNames\": {\"\": \"LG Remote App\", \"ko-KR\": \"\\ub9ac\\ubaa8\\ucee8 \\uc571\", \"zxx-XX\": \"\\u041b\\u0413 R\\u044d\\u043cot\\u044d A\\u041f\\u041f\"}, \"localizedVendorNames\": {\"\": \"LG Electronics\"}, \"permissions\": [\"TEST_SECURE\", \"CONTROL_INPUT_TEXT\", \"CONTROL_MOUSE_AND_KEYBOARD\", \"READ_INSTALLED_APPS\", \"READ_LGE_SDX\", \"READ_NOTIFICATIONS\", \"SEARCH\", \"WRITE_SETTINGS\", \"WRITE_NOTIFICATION_ALERT\", \"CONTROL_POWER\", \"READ_CURRENT_CHANNEL\", \"READ_RUNNING_APPS\", \"READ_UPDATE_INFO\", \"UPDATE_FROM_REMOTE_APP\", \"READ_LGE_TV_INPUT_EVENTS\", \"READ_TV_CURRENT_TIME\"], \"serial\": \"2f930e2d2cfe083771f68e4fe7bb07\", \"vendorId\": \"com.lge\"}}, \"pairingType\": \"PROMPT\"}, \"type\": \"register\"}");
  String str_auth=String("{\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"manifest\":{\"appVersion\":\"1.1\",\"manifestVersion\": 1,\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\", \"CONTROL_DISPLAY\", \"CONTROL_INPUT_JOYSTICK\", \"CONTROL_INPUT_MEDIA_RECORDING\", \"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\", \"CONTROL_POWER\", \"READ_APP_STATUS\", \"READ_CURRENT_CHANNEL\", \"READ_INPUT_DEVICE_LIST\", \"READ_NETWORK_STATE\", \"READ_RUNNING_APPS\", \"READ_TV_CHANNEL_LIST\", \"WRITE_NOTIFICATION_TOAST\", \"READ_POWER_STATE\",\"READ_COUNTRY_INFO\", \"READ_SETTINGS\", \"CONTROL_TV_SCREEN\", \"CONTROL_TV_STANBY\", \"CONTROL_FAVORITE_GROUP\", \"CONTROL_USER_INFO\", \"CHECK_BLUETOOTH_DEVICE\", \"CONTROL_BLUETOOTH\", \"CONTROL_TIMER_INFO\", \"STB_INTERNAL_CONNECTION\", \"CONTROL_RECORDING\", \"READ_RECORDING_STATE\", \"WRITE_RECORDING_LIST\", \"READ_RECORDING_LIST\", \"READ_RECORDING_SCHEDULE\", \"WRITE_RECORDING_SCHEDULE\", \"READ_STORAGE_DEVICE_LIST\", \"READ_TV_PROGRAM_INFO\", \"CONTROL_BOX_CHANNEL\", \"READ_TV_ACR_AUTH_TOKEN\", \"READ_TV_CONTENT_STATE\", \"READ_TV_CURRENT_TIME\", \"ADD_LAUNCHER_CHANNEL\", \"SET_CHANNEL_SKIP\", \"RELEASE_CHANNEL_SKIP\", \"CONTROL_CHANNEL_BLOCK\", \"DELETE_SELECT_CHANNEL\", \"CONTROL_CHANNEL_GROUP\", \"SCAN_TV_CHANNELS\", \"CONTROL_TV_POWER\", \"CONTROL_WOL\"], \"signatures\": [{\"signature\": \"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\", \"signatureVersion\": 1}], \"signed\": {\"appId\": \"com.lge.test\", \"created\": \"20140509\", \"localizedAppNames\": {\"\": \"LG Remote App\", \"ko-KR\": \"\\ub9ac\\ubaa8\\ucee8 \\uc571\", \"zxx-XX\": \"\\u041b\\u0413 R\\u044d\\u043cot\\u044d A\\u041f\\u041f\"}, \"localizedVendorNames\": {\"\": \"LG Electronics\"}, \"permissions\": [\"TEST_SECURE\", \"CONTROL_INPUT_TEXT\", \"CONTROL_MOUSE_AND_KEYBOARD\", \"READ_INSTALLED_APPS\", \"READ_LGE_SDX\", \"READ_NOTIFICATIONS\", \"SEARCH\", \"WRITE_SETTINGS\", \"WRITE_NOTIFICATION_ALERT\", \"CONTROL_POWER\", \"READ_CURRENT_CHANNEL\", \"READ_RUNNING_APPS\", \"READ_UPDATE_INFO\", \"UPDATE_FROM_REMOTE_APP\", \"READ_LGE_TV_INPUT_EVENTS\", \"READ_TV_CURRENT_TIME\"], \"serial\": \"2f930e2d2cfe083771f68e4fe7bb07\", \"vendorId\": \"com.lge\"}}, \"pairingType\": \"PROMPT\"}, \"type\": \"register\"}");
  Serial.printf("Pairing %s\n",tv_ip.c_str());
  WebSocketClient ws;
  ws.setAuthorizationHeader("");
  if (!ws.isConnected()) {
    ws.connect(tv_ip, "/", 3001);
  }
  //wsclient->setInsecure();
  //delay(2000);
  Serial.println("Send auth");
  //String str_auth="{\"id\":\"register_0\"}";
  //Serial.println(str_auth);
  if (ws.isConnected()) {
  ws.send(str_auth);
  } else {
    Serial.println("SEND not connected");
  }
  //delay(5000);
  //ws.send("test");
  String msg;
  if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
  } else {
    Serial.println("Error get msg");
  }
  //lg_pair(tv_ip,String("https://"+tv_ip+":3001/"),mac+".tmp",true);
  delay(10000);
  if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    if (msg.indexOf("{\"client-key\":\"")>-1) {
      Serial.printf("Got client key %s\n",msg.substring(msg.indexOf("{\"client-key\":\"")+15,msg.indexOf("\"",msg.indexOf("{\"client-key\":\"")+16)).c_str());
      writeFile(String(mac+".ckey").c_str(),msg.substring(msg.indexOf("{\"client-key\":\"")+15,msg.indexOf("\"",msg.indexOf("{\"client-key\":\"")+16)).c_str());
    }
  } else {
    Serial.println("Error get msg");
  }
  ws.disconnect();
  return 1;
}

int lg_getvol(String mac) {
  String tv_ip=getFile(String(mac+".macip").c_str());
  String lg_key=getFile(String(mac+".ckey").c_str());
  String str_auth=String("{\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"manifest\":{\"appVersion\":\"1.1\",\"manifestVersion\": 1,\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\", \"CONTROL_DISPLAY\", \"CONTROL_INPUT_JOYSTICK\", \"CONTROL_INPUT_MEDIA_RECORDING\", \"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\", \"CONTROL_POWER\", \"READ_APP_STATUS\", \"READ_CURRENT_CHANNEL\", \"READ_INPUT_DEVICE_LIST\", \"READ_NETWORK_STATE\", \"READ_RUNNING_APPS\", \"READ_TV_CHANNEL_LIST\", \"WRITE_NOTIFICATION_TOAST\", \"READ_POWER_STATE\",\"READ_COUNTRY_INFO\", \"READ_SETTINGS\", \"CONTROL_TV_SCREEN\", \"CONTROL_TV_STANBY\", \"CONTROL_FAVORITE_GROUP\", \"CONTROL_USER_INFO\", \"CHECK_BLUETOOTH_DEVICE\", \"CONTROL_BLUETOOTH\", \"CONTROL_TIMER_INFO\", \"STB_INTERNAL_CONNECTION\", \"CONTROL_RECORDING\", \"READ_RECORDING_STATE\", \"WRITE_RECORDING_LIST\", \"READ_RECORDING_LIST\", \"READ_RECORDING_SCHEDULE\", \"WRITE_RECORDING_SCHEDULE\", \"READ_STORAGE_DEVICE_LIST\", \"READ_TV_PROGRAM_INFO\", \"CONTROL_BOX_CHANNEL\", \"READ_TV_ACR_AUTH_TOKEN\", \"READ_TV_CONTENT_STATE\", \"READ_TV_CURRENT_TIME\", \"ADD_LAUNCHER_CHANNEL\", \"SET_CHANNEL_SKIP\", \"RELEASE_CHANNEL_SKIP\", \"CONTROL_CHANNEL_BLOCK\", \"DELETE_SELECT_CHANNEL\", \"CONTROL_CHANNEL_GROUP\", \"SCAN_TV_CHANNELS\", \"CONTROL_TV_POWER\", \"CONTROL_WOL\"], \"signatures\": [{\"signature\": \"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\", \"signatureVersion\": 1}], \"signed\": {\"appId\": \"com.lge.test\", \"created\": \"20140509\", \"localizedAppNames\": {\"\": \"LG Remote App\", \"ko-KR\": \"\\ub9ac\\ubaa8\\ucee8 \\uc571\", \"zxx-XX\": \"\\u041b\\u0413 R\\u044d\\u043cot\\u044d A\\u041f\\u041f\"}, \"localizedVendorNames\": {\"\": \"LG Electronics\"}, \"permissions\": [\"TEST_SECURE\", \"CONTROL_INPUT_TEXT\", \"CONTROL_MOUSE_AND_KEYBOARD\", \"READ_INSTALLED_APPS\", \"READ_LGE_SDX\", \"READ_NOTIFICATIONS\", \"SEARCH\", \"WRITE_SETTINGS\", \"WRITE_NOTIFICATION_ALERT\", \"CONTROL_POWER\", \"READ_CURRENT_CHANNEL\", \"READ_RUNNING_APPS\", \"READ_UPDATE_INFO\", \"UPDATE_FROM_REMOTE_APP\", \"READ_LGE_TV_INPUT_EVENTS\", \"READ_TV_CURRENT_TIME\"], \"serial\": \"2f930e2d2cfe083771f68e4fe7bb07\", \"vendorId\": \"com.lge\"}}, \"pairingType\": \"PROMPT\",\"client-key\":\""+lg_key+"\"}, \"type\": \"register\"}");
  WebSocketClient ws;
  ws.setAuthorizationHeader("");
  if (!ws.isConnected()) {
    ws.connect(tv_ip, "/", 3001);
  }
  if (ws.isConnected()) {
    ws.send(str_auth);
  } else {
    Serial.println("SEND not connected");
    ws.disconnect();
    return 0;
  }
  String msg;
  if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    String au_req=String("{\"id\":\"status_0\",\"type\":\"request\",\"uri\":\"ssap://audio/getVolume\"}");
    ws.send(au_req);
    String au_status;
    ws.getMessage(au_status);
    String t_vol=au_status.substring(au_status.indexOf("\"volume\"")+9,au_status.indexOf(",",au_status.indexOf("\"volume\"")+9));
    return t_vol.toInt();
  } else {
    Serial.println("Error get msg");
    ws.disconnect();
    return 0;
  }
 

  ws.disconnect();
  return 0;
}

void lg_command(String mac,String s_cmd) {
  String tv_ip=getFile(String(mac+".macip").c_str());
  String lg_key=getFile(String(mac+".ckey").c_str());
  String str_auth=String("{\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"manifest\":{\"appVersion\":\"1.1\",\"manifestVersion\": 1,\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\", \"CONTROL_DISPLAY\", \"CONTROL_INPUT_JOYSTICK\", \"CONTROL_INPUT_MEDIA_RECORDING\", \"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\", \"CONTROL_POWER\", \"READ_APP_STATUS\", \"READ_CURRENT_CHANNEL\", \"READ_INPUT_DEVICE_LIST\", \"READ_NETWORK_STATE\", \"READ_RUNNING_APPS\", \"READ_TV_CHANNEL_LIST\", \"WRITE_NOTIFICATION_TOAST\", \"READ_POWER_STATE\",\"READ_COUNTRY_INFO\", \"READ_SETTINGS\", \"CONTROL_TV_SCREEN\", \"CONTROL_TV_STANBY\", \"CONTROL_FAVORITE_GROUP\", \"CONTROL_USER_INFO\", \"CHECK_BLUETOOTH_DEVICE\", \"CONTROL_BLUETOOTH\", \"CONTROL_TIMER_INFO\", \"STB_INTERNAL_CONNECTION\", \"CONTROL_RECORDING\", \"READ_RECORDING_STATE\", \"WRITE_RECORDING_LIST\", \"READ_RECORDING_LIST\", \"READ_RECORDING_SCHEDULE\", \"WRITE_RECORDING_SCHEDULE\", \"READ_STORAGE_DEVICE_LIST\", \"READ_TV_PROGRAM_INFO\", \"CONTROL_BOX_CHANNEL\", \"READ_TV_ACR_AUTH_TOKEN\", \"READ_TV_CONTENT_STATE\", \"READ_TV_CURRENT_TIME\", \"ADD_LAUNCHER_CHANNEL\", \"SET_CHANNEL_SKIP\", \"RELEASE_CHANNEL_SKIP\", \"CONTROL_CHANNEL_BLOCK\", \"DELETE_SELECT_CHANNEL\", \"CONTROL_CHANNEL_GROUP\", \"SCAN_TV_CHANNELS\", \"CONTROL_TV_POWER\", \"CONTROL_WOL\"], \"signatures\": [{\"signature\": \"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\", \"signatureVersion\": 1}], \"signed\": {\"appId\": \"com.lge.test\", \"created\": \"20140509\", \"localizedAppNames\": {\"\": \"LG Remote App\", \"ko-KR\": \"\\ub9ac\\ubaa8\\ucee8 \\uc571\", \"zxx-XX\": \"\\u041b\\u0413 R\\u044d\\u043cot\\u044d A\\u041f\\u041f\"}, \"localizedVendorNames\": {\"\": \"LG Electronics\"}, \"permissions\": [\"TEST_SECURE\", \"CONTROL_INPUT_TEXT\", \"CONTROL_MOUSE_AND_KEYBOARD\", \"READ_INSTALLED_APPS\", \"READ_LGE_SDX\", \"READ_NOTIFICATIONS\", \"SEARCH\", \"WRITE_SETTINGS\", \"WRITE_NOTIFICATION_ALERT\", \"CONTROL_POWER\", \"READ_CURRENT_CHANNEL\", \"READ_RUNNING_APPS\", \"READ_UPDATE_INFO\", \"UPDATE_FROM_REMOTE_APP\", \"READ_LGE_TV_INPUT_EVENTS\", \"READ_TV_CURRENT_TIME\"], \"serial\": \"2f930e2d2cfe083771f68e4fe7bb07\", \"vendorId\": \"com.lge\"}}, \"pairingType\": \"PROMPT\",\"client-key\":\""+lg_key+"\"}, \"type\": \"register\"}");
  WebSocketClient ws;
  ws.setAuthorizationHeader("");
  if (!ws.isConnected()) {
    ws.connect(tv_ip, "/", 3001);
  }
  if (ws.isConnected()) {
  ws.send(str_auth);
  } else {
    Serial.println("SEND not connected");
    ws.disconnect();
    return;
  }
  String msg;
  if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    String au_status=String("{\"id\":\"status_0\",\"type\":\"request\",\"uri\":\"ssap://audio/getStatus\"}");
    ws.send(au_status);
  } else {
    Serial.println("Error get msg");
    ws.disconnect();
    return;
  }
  
  if (s_cmd.indexOf("MUTE")>-1) {
    String au_status=String("{\"id\":\"status_0\",\"type\":\"request\",\"uri\":\"ssap://audio/getStatus\"}");
    ws.send(au_status);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
      String mute="";
        if (msg.indexOf("\"mute\":false")>-1) {
        mute=String("{\"id\":\"0\",\"type\":\"request\",\"uri\":\"ssap://audio/setMute\",\"payload\":\"{\\\"mute\\\": 1}\"}");
        } else {
        mute=String("{\"id\":\"0\",\"type\":\"request\",\"uri\":\"ssap://audio/setMute\",\"payload\":\"{\\\"mute\\\": 0}\"}");
        }
      ws.send(mute);
      ws.getMessage(msg);
      Serial.println(msg);
      } else {
      Serial.println("Error get msg");
      ws.disconnect();
      return;
      }
  } else if (s_cmd.indexOf("VOLUP")>-1) {
    
    String au_status=String("{\"id\": \"status_0\", \"type\": \"request\", \"uri\": \"ssap://audio/getVolume\"}");
    ws.send(au_status);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
      String mute="";
        if (msg.indexOf("\"mute\":false")>-1) {
        mute=String("{\"id\":\"0\",\"type\":\"request\",\"uri\":\"ssap://audio/setMute\",\"payload\":\"{\\\"mute\\\": 1}\"}");
        } else {
        mute=String("{\"id\":\"0\",\"type\":\"request\",\"uri\":\"ssap://audio/setMute\",\"payload\":\"{\\\"mute\\\": 0}\"}");
        }
      ws.send(mute);
      ws.getMessage(msg);
      Serial.println(msg);
      } else {
      Serial.println("Error get msg");
      ws.disconnect();
      return;
      }
  } else if (s_cmd.indexOf("SETCHAN")>-1) {
    String chan_set=s_cmd.substring(s_cmd.indexOf("SETCHAN")+8);
    String lgset_chan=String("{\"id\": \"0\", \"type\": \"request\", \"uri\": \"ssap://tv/openChannel\", \"payload\": \"{\\\"channelId\\\": \\\"1_0_"+chan_set+"_0_0_0_0\\\"}\"}");
    ws.send(lgset_chan);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    }
  } else if (s_cmd.indexOf("SETVOL")>-1) {
    String chan_set=s_cmd.substring(s_cmd.indexOf("SETVOL")+7);
    String lgvol_set=String("{\"id\": \"0\", \"type\": \"request\", \"uri\": \"ssap://audio/setVolume\", \"payload\": \"{\\\"volume\\\": "+chan_set+"}\"}");
    ws.send(lgvol_set);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    }
  } else if (s_cmd.indexOf("HDMI")>-1) {
    String lgset_input=String("{\"id\": \"0\", \"type\": \"request\", \"uri\": \"ssap://tv/switchInput\", \"payload\": \"{\\\"inputId\\\": \\\"HDMI_1\\\"}\"}");
    ws.send(lgset_input);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    }
  } else if (s_cmd.indexOf("POWEROFF")>-1) {
    String au_status=String("{\"id\": \"0\", \"type\": \"request\", \"uri\": \"ssap://system/turnOff\"}");
    ws.send(au_status);
    if (ws.getMessage(msg)) {
    Serial.println("Get message");
    Serial.println(msg);
    }
  }
  ws.disconnect();
  return;
}

File - fsio.ino

//#include <FS.h>
#include <LittleFS.h>
#include <WString.h>
// IO Operations

int readFile(const char *path,char *dst) {
  Serial.printf("Reading file: %s\n", path);
  int t_r=0;
  int c_r=0;
  uint8_t buf[1024];
  int i;
  for (i=0;i<1024;i++) {
    buf[i]=0;
  }

  File file = LittleFS.open(path, "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return 0;
  }

  //Serial.print("Read from file: ");
  while (file.available()) {
    //Serial.write(file.read()); 
    c_r=file.read(buf,1024);
    t_r=t_r+c_r;
    }
    buf[t_r]=0;
    strcpy(dst,(char*)buf);
  file.close();
  return t_r;
}

String getFile(const char *path) {
  Serial.printf("Reading file: %s\n", path);
  int t_r=0;
  int c_r=0;
  uint8_t buf[1024];
  int i;
  for (i=0;i<1024;i++) {
    buf[i]=0;
  }

  File file = LittleFS.open(path, "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return "";
  }

  //Serial.print("Read from file: ");
  while (file.available()) {
    //Serial.write(file.read()); 
    c_r=file.read(buf,1024);
    t_r=t_r+c_r;
    }
    buf[t_r]=0;
    //strcpy(dst,(char*)buf);
  file.close();
  return String((char*)buf);
}

void debugFile(const char *path) {
  Serial.printf("Reading file: %s\n", path);

  File file = LittleFS.open(path, "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while (file.available()) { Serial.write(file.read()); }
  Serial.println("=== ");
  file.close();
}


void writeFile(const char *path, const char *message) {
  //Serial.printf("Writing file: %s\n", path);

  File file = LittleFS.open(path, "w");
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    //Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  //delay(2000); // Make sure the CREATE and LASTWRITE times are different
  file.close();
}

String listDir(String ip,String pp) {
  String lf=String("<html><body>");
  String dpp="";
  //Serial.printf("Listing directory: %s\n", dirname);

  Dir root = LittleFS.openDir(pp);
  if (pp.length()>1) {
    dpp=pp+"/";
  }
  

  while (root.next()) {
    //File file = root.openFile("r");
    //Serial.print("  FILE: ");
    String ff_n=String(root.fileName());
    Serial.println(ff_n);
    lf=lf+String("<a href=\"http://"+ip+"/"+dpp+ff_n+"\">"+ff_n+"</a><br>");
    //Serial.print("  SIZE: ");
    //Serial.print(file.size());
    //time_t cr = file.getCreationTime();
    //time_t lw = file.getLastWrite();
    //file.close();
    //struct tm *tmstruct = localtime(&cr);
    //Serial.printf("    CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
    //tmstruct = localtime(&lw);
    //Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
  }
  //root.close();
  Serial.println(lf+"</body></html>");
  return lf+"</body></html>";
  
}

String listtvname(const char *dirname) {
  String lf="";
  String name_anal="";
  String name_tv="";
  char file_anal[256];
//  char file_anal[256];
  char tv_name[256];
  Serial.printf("Listing directory: %s\n", dirname);

  Dir root = LittleFS.openDir(dirname);

  lf="<table border=\"1px\">\n<tr><th>Имя</th><th>MAC</th><th>IP</th><th>Avtrctl</th><th>Avtrdesc</th><th>Rendctl</th><th>Renddesc</th><th>Action</th></tr>\n";
  while (root.next()) {
    File file = root.openFile("r");
    //Serial.print("  FILE: ");
    //Serial.print(root.fileName());
    //file_anal=root.fileName();
    name_anal=root.fileName();
    //Serial.println("Found "+String(name_anal)+"...");
    //lf=lf+String(root.fileName())+"<br>";
      if (name_anal.indexOf(".macname")>0) {
        String tv_mac=name_anal.substring(0,name_anal.indexOf(".macname"));
        String tv_ip=getFile(String("/"+tv_mac+".macip").c_str());
        String tv_avtrurl="";//getFile(String("/"+tv_mac+".avtrurl").c_str());
        String tv_avtrurls="";//getFile(String("/"+tv_mac+".avtrurls").c_str());
        String tv_rendurl="";//getFile(String("/"+tv_mac+".rendurl").c_str());
        String tv_rendurls="";//getFile(String("/"+tv_mac+".rendurls").c_str());
        String removetv="<button onclick=\"deletetv('"+tv_mac+"');\">Удалить</button>";
        String tvtth="<button onclick=\"tvtoth('"+tv_mac+"');\">В УД</button>";
        //readFile(String("/"+name_anal).c_str(),tv_name);
        //name_tv=String(tv_name);
        name_tv=getFile(String("/"+name_anal).c_str());
        lf=lf+"<tr><td>"+name_tv+"&nbsp;"+"<button onclick=\"waketv('"+tv_mac+"');\">Включить</button>"+"</td><td>"+tv_mac+"</td><td>"+tv_ip+"</td><td><a href=\""+tv_avtrurl+"\">Click</a></td><td><a href=\""+tv_avtrurls+"\">Click</a></td><td><a href=\""+tv_rendurl+"\">Click</a></td><td><a href=\""+tv_rendurls+"\">Click</a></td><td>"+removetv+tvtth+"</td></tr>\n";
      }
    //Serial.print("  SIZE: ");
    //Serial.print(file.size());
    //time_t cr = file.getCreationTime();
    //time_t lw = file.getLastWrite();
    file.close();

    //struct tm *tmstruct = localtime(&cr);
    //Serial.printf("    CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
    //tmstruct = localtime(&lw);
    //Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
  }
  lf=lf+"</table>\n";
  lf=lf+"<a href=\"/\">На главную</a>\n";
    lf=lf+"<script type=\"text/javascript\" src=\"/sonyremote.js\"></script>\n";
  return lf;
}

void removetv(String mac) {
  Dir root = LittleFS.openDir("/");
  String ip=getFile(String("/"+mac+".macip").c_str())+".";
  while (root.next()) {
    String fn_name=root.fileName();
    if (fn_name.indexOf(mac)>-1) {
      LittleFS.remove(String("/"+fn_name).c_str());
    } else if ((fn_name.indexOf(ip)>-1)) {
      LittleFS.remove(String("/"+fn_name).c_str());
    }
  }
}

String show_rctv(String mac) {
  String o_m="<html><body></body></html>";
  return o_m;
}

String getlistth() {
  int n=0;
  String s_str;
  Dir root = LittleFS.openDir("/");
  while (root.next()) {
    String fn_name=root.fileName();
    if (fn_name.indexOf(".th")>-1) {
      String p_mac=fn_name.substring(0,fn_name.indexOf(".th"));
      String n_mac=getFile(String("/"+p_mac+".macname").c_str());
      s_str=s_str+"<br><a href=\"/"+p_mac+".html\">"+n_mac+"</a><br>";
      n++;
    }
  }
  if (n==0) {
    s_str=s_str+"У Вас нет выбранных телевизоров.";
  }
  s_str=s_str+"<br><br><a href=\"/listtv\">К выбору телевизоров</a>";
  return s_str;
}

void tvtoth(String mac) {
  String p1="<script>(function() {document.getElementById(\"mac\").value=\""+mac+"\";}());</script>";
  String p2=String("<script>(function() {\n")+
  String("var aliceid;\n")+
  String("const xhr = new XMLHttpRequest();\n")+
    String("xhr.open(\"POST\", \"/getvolume\",true);\n")+
    String("xhr.setRequestHeader('Content-Type', 'text/plain');\n")+
    String("xhr.send(\""+mac+"\");\n")+
    String("xhr.responseType = \"text\";\n")+
    String("xhr.onload = () => {\n")+
 String("if (xhr.readyState == 4 && xhr.status == 200) {\n")+
  String("document.getElementById(\"volume\").value=xhr.response;\n")+
  String("console.log(xhr.response);\n")+
  String("} else {\n")+
  String("console.log(`Error: ${xhr.status}`);\n")+
  String("}\n")+
    String("};\n")+
String("}());</script>\n");
  
  //if (!LittleFS.exists(String(mac+".html").c_str())) {
  File fp=LittleFS.open("tvcontrol1.html","r");
  File fe=LittleFS.open("tvcontrol2.html","r");
  File fo=LittleFS.open(String(mac+".html").c_str(),"w");
    int len=fp.size();
    uint8_t buff[128]={0};
    while (len>0) {
      int r=fp.read(buff,std::min((size_t)len, sizeof(buff)));
      fo.write(buff,r);
      if (len > 0) {len =len - r;}
    }
    fp.close();
    fo.write(p1.c_str());
    fo.write(p2.c_str());
    fo.write("\n");
    len=fe.size();
    while (len>0) {
      int r=fe.read(buff,std::min((size_t)len, sizeof(buff)));
      fo.write(buff,r);
      if (len > 0) {len =len - r;}
    }
    fe.close();
    fo.close();
  //}
  
  writeFile(String("/"+mac+".th").c_str(),getFile(String("/"+mac+".macname").c_str()).c_str());
}

int download_file(String url, String lfs, bool fdebug = false) {
  HTTPClient httpclient;
    WiFiClient hclient;
    if (httpclient.begin(hclient,url.c_str())) {
      int httpCode = httpclient.GET();
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          File file = LittleFS.open(lfs.c_str(), "w");
          int len = httpclient.getSize();
          //Serial.print("File len:");
          //Serial.println(len);
          static uint8_t buff[128]={0};
          //WiFiClient* stream=&hclient;
          WiFiClient stream=httpclient.getStream();
          while (httpclient.connected() && len > 0) {
            //size_t size = hclient.available();
            //int size = hclient.available();
            //size_t size = hclient.available();
            //Serial.print("Size :");
            //Serial.println(len);
            if (len>0) {
              //int c;
              /*if (len>128) {
                c = stream->readBytes(buff,128);
              } else {
                c = stream->readBytes(buff,len);
              }*/
              int c = stream.readBytes(buff, std::min((size_t)len, sizeof(buff)));
              file.write(buff,c);
              
              if (len > 0) {len =len - c;}
              
            }
            delay(1);
          }
          file.close();
          httpclient.end();
          //httpclient.end();
          if (fdebug) {
            //debugFile(lfs.c_str());
          }
          return 1;
        }
        httpclient.end();
        return 0;
      } else {
        Serial.printf("[HTTP] GET... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
        httpclient.end();
      }
    } else {
      Serial.println("Error get sonyremote.js");
      httpclient.end();
    }
  return 0;
}

int download_sfile(String url, String lfs, bool fdebug = false) {
  
  //WiFiClient hclient;
  std::unique_ptr<BearSSL::WiFiClientSecure> hclient(new BearSSL::WiFiClientSecure);

    //bool mfln = hclient->probeMaxFragmentLength("tls.mbed.org", 443, 1024);
    bool mfln = hclient->probeMaxFragmentLength(url, 443, 1024);
    Serial.printf("\nConnecting to %s\n",url.c_str());
    Serial.printf("Maximum fragment Length negotiation supported: %s\n", mfln ? "yes" : "no");
    if (mfln) { hclient->setBufferSizes(1024, 1024); }

    Serial.print("[HTTPS] begin...\n");
    //const uint8_t fingerprint[20] = { 0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a };
    const uint8_t fingerprint[20] = { 0x0D, 0x98, 0xC0, 0x73, 0x7F, 0xAB, 0xBD, 0xBD, 0xD9, 0x47, 0x4B, 0x49, 0xAD, 0x0A, 0x4A, 0x0C, 0xAC, 0x3E, 0xC7, 0x7C};

    //hclient->setFingerprint(fingerprint);
    hclient->setInsecure();
  HTTPClient httpclient;
  if (httpclient.begin(*hclient,url)) {
    int httpCode = httpclient.GET();
    if (httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          LittleFS.remove(lfs.c_str());
          File file = LittleFS.open(lfs.c_str(), "w");
          //String payload = httpclient.getString();
          //Serial.println(payload);
          //writeFile(lfs,payload.c_str());
          int len = httpclient.getSize();
          Serial.printf("File size download: %d\n",len);
          static uint8_t buff[1024]={0};
          while (httpclient.connected() && (len > 0 || len == -1)) {
            //size_t size = hclient.available();
            //int size = hclient.available();
            size_t size = hclient->available();
            Serial.print("Avalible size :");
            Serial.println(size);
            if (size) {
              int c = hclient->readBytes(buff,((size > sizeof(buff)) ? sizeof(buff) :size));
              file.write(buff,c);
              Serial.print("Readbyte count :");
              Serial.println(c);
              if (len > 0) {len -= c;}
            }
            delay(1);
          }
          file.close();
          httpclient.end();
          if (fdebug) {
            debugFile(lfs.c_str());
          }
        } else {
          Serial.println("Error get "+url+" "+lfs);
          httpclient.end();
        }
    } else {
      Serial.println("Error get connect");
      httpclient.end();
    }
  } else {
    Serial.println("Error get begin");
    httpclient.end();
  }
  return 1;
}

File - fsio.h

void writeFile(const char *path, const char *message);
void debugFile(const char *path);
String getFile(const char *path);
int readFile(const char *path,uint8_t * dst);
int download_file(String url,String path,bool fdebug);
int download_sfile(String url,String path,bool fdebug);

File - avtransport.ino

#include <LittleFS.h>
#include <ESP8266HTTPClient.h>

void get_transport_url(String ip) {
  char buf[1024];
  char mac[20];
  String sbuf;
  String pbuf;
  String host;
  String sport;
  uint16_t port;
  String urlpath;

  readFile(String("/"+ip+".url").c_str(),buf);
  sbuf=String(buf);
  if (sbuf.indexOf(":",6)>0) {
    host=sbuf.substring(0,sbuf.indexOf(":",6));
    sport=sbuf.substring(sbuf.indexOf(":",6)+1,sbuf.indexOf("/",sbuf.indexOf(":",6)+1));
    urlpath=sbuf.substring(sbuf.indexOf("/",sbuf.indexOf(":",6)+1));
  }
  port=sport.toInt();
  Serial.print("port=");
  Serial.println(port);
  Serial.println("host="+host.substring(7)+" port="+sport+" urlpath="+urlpath+"==");
  Serial.println("Download "+String(buf)+"==");
    HTTPClient httpclient;
    WiFiClient hclient;
    if (urlpath.indexOf("/dmr.xml")>-1) {
      pbuf="/dmr.xml";
    } else {
      pbuf=urlpath;
    }
    //delay(2000);
    if (httpclient.begin(hclient,buf)) {
    //if (httpclient.begin(hclient,"http://192.168.113.86:52323/dmr.xml")) {
    //if (httpclient.begin(hclient,host.substring(7),port,urlpath,false)) {
    //if (httpclient.begin(hclient,host.substring(7),port,pbuf.c_str(),false)) {
      int httpCode = httpclient.GET();
      find_arp(ip,mac);
      writeFile(String("/"+ip+".ipmac").c_str(),String(mac).c_str());
      writeFile(String("/"+ip+".on").c_str(),String(mac).c_str());
      writeFile(String("/"+String(mac)+".macip").c_str(),ip.c_str());
      writeFile(String("/"+String(mac)+".on").c_str(),ip.c_str());

      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = httpclient.getString();
          //debugFile();
          Serial.println(payload);
          writeFile(String("/"+String(mac)+".macinfo").c_str(),payload.c_str());
          String url_transport=payload.substring(payload.indexOf("<controlURL>",payload.indexOf("AVTransport:1")+2)+12,payload.indexOf("</controlURL>",payload.indexOf("<controlURL>",payload.indexOf("AVTransport:1")+2)+2));
          Serial.println("Control avtransport:"+url_transport);
          String url_transport_scdp=payload.substring(payload.indexOf("<SCPDURL>",payload.indexOf("AVTransport:1")+2)+9,payload.indexOf("</SCPDURL>",payload.indexOf("<SCPDURL>",payload.indexOf("AVTransport:1")+2)+2));
          Serial.println("Control avtransport:"+url_transport);
          String url_render=payload.substring(payload.indexOf("<controlURL>",payload.indexOf("RenderingControl:1")+2)+12,payload.indexOf("</controlURL>",payload.indexOf("<controlURL>",payload.indexOf("RenderingControl:1")+2)+2));
          Serial.println("Control render:"+url_render);
          String url_render_scdp=payload.substring(payload.indexOf("<SCPDURL>",payload.indexOf("RenderingControl:1")+2)+9,payload.indexOf("</SCPDURL>",payload.indexOf("<SCPDURL>",payload.indexOf("RenderingControl:1")+2)+2));
          Serial.println("Control render:"+url_render);
          String fr_name=payload.substring(payload.indexOf("<friendlyName>")+14,payload.indexOf("</friendlyName>"));
          writeFile(String("/"+ip+".ipname").c_str(),fr_name.c_str());
          writeFile(String("/"+String(mac)+".macname").c_str(),fr_name.c_str());
          if (payload.indexOf("IRCC:1") > 0) {
            String url_ircc_scdp=payload.substring(payload.indexOf("<SCPDURL>",payload.indexOf("IRCC:1")+2)+9,payload.indexOf("</SCPDURL>",payload.indexOf("<SCPDURL>",payload.indexOf("IRCC:1")+2)+2));
            String url_ircc=payload.substring(payload.indexOf("<controlURL>",payload.indexOf("IRCC:1")+2)+12,payload.indexOf("</controlURL>",payload.indexOf("<controlURL>",payload.indexOf("IRCC:1")+2)+2));
            Serial.println("IRCC:"+url_ircc);
            Serial.println("IRCC s:"+url_ircc_scdp);
            if (url_transport.indexOf("http://")>-1) {
              writeFile(String("/"+String(mac)+".irccurl").c_str(),url_ircc.c_str());
            } else {
              writeFile(String("/"+String(mac)+".irccurl").c_str(),String("http://"+ip+":"+sport+url_ircc).c_str());
            }
            if (url_transport_scdp.indexOf("http://")>-1) {
              writeFile(String("/"+String(mac)+".irccurls").c_str(),url_ircc_scdp.c_str());
            } else {
              writeFile(String("/"+String(mac)+".irccurls").c_str(),String("http://"+ip+":"+sport+url_ircc_scdp).c_str());
            }
          }
          if (url_transport.indexOf("http://")>-1) {
            writeFile(String("/"+String(mac)+".avtrurl").c_str(),url_transport.c_str());
          } else {
            writeFile(String("/"+String(mac)+".avtrurl").c_str(),String("http://"+ip+":"+sport+url_transport).c_str());
          }
          if (url_transport_scdp.indexOf("http://")>-1) {
            writeFile(String("/"+String(mac)+".avtrurls").c_str(),url_transport_scdp.c_str());
          } else {
            writeFile(String("/"+String(mac)+".avtrurls").c_str(),String("http://"+ip+":"+sport+url_transport_scdp).c_str());
          }
          if (url_render.indexOf("http://")>-1) {
            writeFile(String("/"+String(mac)+".rendurl").c_str(),url_render.c_str());
          } else {
            writeFile(String("/"+String(mac)+".rendurl").c_str(),String("http://"+ip+":"+sport+url_render).c_str());
          }
          if (url_render_scdp.indexOf("http://")>-1) {
            writeFile(String("/"+String(mac)+".rendurls").c_str(),url_render_scdp.c_str());
          } else {
            writeFile(String("/"+String(mac)+".rendurls").c_str(),String("http://"+ip+":"+sport+url_render_scdp).c_str());
          }
          Serial.println("Device name:"+fr_name);
          //writeFile("/"+ip+"_.url",payload.c_str());
          //debugFile();
        }
        httpclient.end();
      } else {
        Serial.println("Error get "+ip);
        httpclient.end();
      }
    } else {
      Serial.println("Error get "+ip);
      httpclient.end();
    }
}



int get_volume(String tv_mac) {
  char buf[1024];
  char mac[20];
  int vol_level=-1;
  String sbuf;
  String pbuf;
  String host;
  String sport;
  uint16_t port;
  String rendurl;
  String urlpath;
  String xml_getvol=String("<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">")+
"<s:Body>"+
"<u:GetVolume xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">"+
"<InstanceID>0</InstanceID>"+
"<Channel>Master</Channel>"+
"</u:GetVolume>"+
"</s:Body>"+
"</s:Envelope>";

  //readFile(String("/"+tv_mac+".rendurl").c_str(),buf);
  //rendurl=String(buf);
  rendurl=getFile(String("/"+tv_mac+".rendurl").c_str());
  Serial.println(String(String("Reading /")+tv_mac+".rendurl"));
  Serial.println("Get "+rendurl);
  HTTPClient httpclient;
  WiFiClient hclient;
  if (httpclient.begin(hclient,rendurl.c_str())) {
    httpclient.addHeader("Content-Type","text/xml");
    httpclient.addHeader("SOAPAction",String(String("\"urn:schemas-upnp-org:service:RenderingControl:1#")+String("GetVolume\"")).c_str());
    int httpCode = httpclient.POST(xml_getvol.c_str());
    if (httpCode >0) {
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = httpclient.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
        if (payload.indexOf("<CurrentVolume>") > -1) {
          String svol=payload.substring(payload.indexOf("<CurrentVolume>")+15,payload.indexOf("</CurrentVolume>"));
          vol_level=svol.toInt();
        }
      }
    } else {
      Serial.printf("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
    }
  }
  httpclient.end();
  return vol_level;
}

void set_volume(String tv_mac,int s_level) {
  char buf[1024];
  char mac[20];
  int vol_level=-1;
  String sbuf;
  String pbuf;
  String host;
  String sport;
  uint16_t port;
  String rendurl;
  String urlpath;
  String xml_getvol=String("<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">")+
"<s:Body>"+
"<u:SetVolume xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">"+
"<InstanceID>0</InstanceID>"+
"<Channel>Master</Channel>"+
"<DesiredVolume>"+String(s_level)+"</DesiredVolume>"+
"</u:SetVolume>"+
"</s:Body>"+
"</s:Envelope>";

  //readFile(String("/"+tv_mac+".rendurl").c_str(),buf);
  //rendurl=String(buf);
  rendurl=getFile(String("/"+tv_mac+".rendurl").c_str());
  Serial.println(String(String("Reading /")+tv_mac+".rendurl"));
  Serial.println("Get "+rendurl);
  HTTPClient httpclient;
  WiFiClient hclient;
  if (httpclient.begin(hclient,rendurl.c_str())) {
    httpclient.addHeader("Content-Type","text/xml");
    httpclient.addHeader("SOAPAction",String(String("\"urn:schemas-upnp-org:service:RenderingControl:1#")+String("SetVolume\"")).c_str());
    int httpCode = httpclient.POST(xml_getvol.c_str());
    if (httpCode >0) {
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = httpclient.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
        if (payload.indexOf("<CurrentVolume>") > -1) {
          String svol=payload.substring(payload.indexOf("<CurrentVolume>")+15,payload.indexOf("</CurrentVolume>"));
          vol_level=svol.toInt();
        }
      }
    } else {
      Serial.printf("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
    }
  }
  httpclient.end();
  //return vol_level;
}

void soap_send(String tv_mac,String soap_action,String soap_data) {
  char buf[1024];
  char mac[20];
  int vol_level=-1;
  String sbuf;
  String pbuf;
  String host;
  String sport;
  uint16_t port;
  String rendurl;
  String urlpath;
  

  
  rendurl=getFile(String("/"+tv_mac+".rendurl").c_str());
  Serial.println(String(String("Reading /")+tv_mac+".rendurl"));
  Serial.println("Get "+rendurl);
  HTTPClient httpclient;
  WiFiClient hclient;
  if (httpclient.begin(hclient,rendurl.c_str())) {
    httpclient.addHeader("Content-Type","text/xml");
    httpclient.addHeader("SOAPAction",soap_action.c_str());
    int httpCode = httpclient.POST(soap_data.c_str());
    if (httpCode >0) {
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = httpclient.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
      }
    } else {
      Serial.printf("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
    }
  }
  httpclient.end();
  //return vol_level;
}

void soap_send_sonybravia(String tv_mac,String soap_action,String soap_data,String sony_auth) {
  char buf[1024];
  char mac[20];
  int vol_level=-1;
  String sbuf;
  String pbuf;
  String host;
  String sport;
  uint16_t port;
  String rendurl;
  String urlpath;
  

  
  rendurl=getFile(String("/"+tv_mac+".irccurl").c_str());
  Serial.println(String(String("Reading /")+tv_mac+".rendurl"));
  Serial.println("Get "+rendurl);
  HTTPClient httpclient;
  WiFiClient hclient;
  if (httpclient.begin(hclient,rendurl.c_str())) {
    httpclient.addHeader("Content-Type","text/xml");
    httpclient.addHeader("SOAPAction",soap_action.c_str());
    httpclient.addHeader("X-Auth-PSK",sony_auth.c_str());
    int httpCode = httpclient.POST(soap_data.c_str());
    if (httpCode >0) {
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = httpclient.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
      }
    } else {
      Serial.printf("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
    }
  }
  httpclient.end();
  //return vol_level;
}

File - arputils.ino

#include <lwip/etharp.h>

int find_arp(String ip,char * pmac) {
  int ri=0;
  int i;
  char mac[20];

  IPAddress cc_ip_addr;
  String c_ip;
  ip4_addr_t * c_ipaddr;
  netif * c_netif;
  eth_addr * c_addr;
  for (i=0;i<10;i++) {
    ri=etharp_get_entry(i,&c_ipaddr,&c_netif,&c_addr);
    if (ri==1) {
      cc_ip_addr=IPAddress(c_ipaddr->addr);
      c_ip=cc_ip_addr.toString();
      if (c_ip.indexOf(ip)>-1) {
      Serial.print("Entry "+String(i)+" ");
      sprintf(mac,"%02X:%02X:%02X:%02X:%02X:%02X",c_addr->addr[0],c_addr->addr[1],c_addr->addr[2],c_addr->addr[3],c_addr->addr[4],c_addr->addr[5]);
      strcpy(pmac,mac);
      Serial.print(cc_ip_addr.toString()+" ");

      Serial.print(mac);
      Serial.println();
      return 1;
      }
    }
  }
  return 0;
}

String get_mac(String ip) {
  char mac[25];
  memset(mac,0,25);
  if (find_arp(ip,mac)==1) {
    return String(mac);
  } else {
    return "00:00:00:00:00:00";
  }
}

Для работы с LG WEBOS необходима библиотека - WebSocketClient

Ко всему этому нужна , как минимум , Яндекс Станция? Я правильно понимаю?

Достаточно приложения на смартфоне.

а 4DUK.h

File - 4duk.cpp

/*

4DUK think home



*/
#include <WiFiUdp.h>
#include <WiFiClient.h>

#if defined(ESP32)
#include <ESPmDNS.h>
#elif defined(ESP8266)
#include <ESP8266mDNS.h>
#endif

#include <4duk.h>

gate4duk::gate4duk() {
    connected=false;
//    WiFiUDP udp;
//    WiFiClient mqtt;
};

gate4duk::~gate4duk() {
    //cleandevs();
};

void gate4duk::addrealdev(device_type_t d_t,char *md_n,char *a_n,char *r_n,char *c_n,char *c_m,float d_v,callback cba,callstat cbs)
{
    device4duk newDev;
    newDev.device_type=d_t;
    newDev.mydev_name=strdup(md_n);
    newDev.alice_name=strdup(a_n);
    newDev.room_name=strdup(r_n);
    newDev.creator_name=strdup(c_n);
    newDev.creator_mail=strdup(c_m);
    newDev.dev_ver=d_v;
    newDev.is_real=true;
    newDev.callaction=cba;
    newDev.callstate=cbs;
    devices.push_back(newDev);
};

/*void gate4duk::addrealdev(device_type_t d_t,String *md_n,String *a_n,String *r_n,String *c_n,String *c_m,float d_v,callback cba,callstat cbs)
{
    device4duk newDev;
    newDev.device_type=d_t;
    newDev.mydev_name=strdup(md_n.c_str());
    newDev.alice_name=strdup(a_n.c_str());
    newDev.room_name=strdup(r_n.c_str());
    newDev.creator_name=strdup(c_n.c_str());
    newDev.creator_mail=strdup(c_m.c_str());
    newDev.dev_ver=d_v;
    newDev.is_real=true;
    newDev.callaction=cba;
    newDev.callstate=cbs;
    devices.push_back(newDev);
};*/

void gate4duk::addhiddev(device_type_t d_t,char *md_n,char *a_n,char *r_n,char *c_n,char *c_m,float d_v,callback cba,callstat cbs)
{
    device4duk newDev;
    newDev.device_type=d_t;
    newDev.mydev_name=strdup(md_n);
    newDev.alice_name=strdup(a_n);
    newDev.room_name=strdup(r_n);
    newDev.creator_name=strdup(c_n);
    newDev.creator_mail=strdup(c_m);
    newDev.dev_ver=d_v;
    newDev.is_real=false;
    newDev.callaction=cba;
    newDev.callstate=cbs;
    devices.push_back(newDev);
};

void gate4duk::setid(char *idgate)
{
    gateid=strdup(idgate);
};
void gate4duk::setid(String idgate)
{
    gateid=strdup(idgate.c_str());
};

void gate4duk::setname(char * name)
{
    mdns_name=strdup(name);
};
void gate4duk::sendstatus()
{
    unsigned int i;
    String s1;
    if ( (millis()-cmil)>110000 ) {
	for (i=0;i<devices.size();i++) {
	String sstate="devstate:"+String(devices[i].mydev_name)+":";
		if (devices[i].callstate) {
		s1=devices[i].callstate(sstate);
		Serial.print("Receive status - ");
		Serial.println(s1);
		    if (mqtt.connected()) {
			//mqtt.println(String("devstate:")+String(mydn)+String(":")+String(stpar)+String(":")+String(stval));
			mqtt.println(s1);
			Serial.println("Send to mqtt.");
		    } else {
		        connected=false;
			uint8_t sbuf[1024];
			//String cmd="devstate:"+String(mydn)+":"+String(stpar)+":"+String(stval);
			s1=s1+" ";
			s1.getBytes(sbuf,s1.length());
			udp.beginPacket(bcast,9009);
			udp.write(sbuf,s1.length());
			udp.endPacket();
			Serial.println("Send bcast.");
		    }
		}
	}
	cmil=millis();
	Serial.println("Update status");
    }
};
void gate4duk::sendstatus(char *mydn,char *stpar,char *stval)
{
    if (connected) {
    mqtt.println(String("devstate:")+String(mydn)+String(":")+String(stpar)+String(":")+String(stval));
    } else {
    uint8_t sbuf[1024];
    String cmd="devstate:"+String(mydn)+":"+String(stpar)+":"+String(stval)+" ";
    cmd.getBytes(sbuf,cmd.length());
    udp.beginPacket(bcast,9009);
    udp.write(sbuf,cmd.length());
    udp.endPacket();
    }
};
bool gate4duk::is_connected()
{
    if (connected) {
    //Serial.print("Connected");
    } else {
    Serial.print("NOT Connected");
    }
    return connected;
};
bool gate4duk::connect(bool to_mqtt) 
{
    int nreq=10;
    connected=false;
    if (to_mqtt) {
//    WiFiClient mqtt;
    Serial.println("Connecting mqtt.....");
	while ( mqtt.connect("4duk.su",9009)==0 && nreq>0) {
    Serial.println("Error connect to mqtt.....");
    nreq--;
	}
	if (nreq==0) {
	Serial.println("Error connect to mqtt. MQTT disable.");
	} else {
	connected=true;
	}
    mqtt.println(gateid);
    //mqtt.keepAlive(10,10,3);
    }
    Serial.println("Start MDNS "+String(mdns_name)+".local on this device.");
    MDNS.begin(mdns_name);
//    WiFiUDP udp;
    Serial.println("Start local udp on 9009 port this device.");
    udp.begin(9009);
    return connected;
};

bool gate4duk::reconnect(bool to_mqtt) 
{
    int nreq=10;
    connected=false;
    if (to_mqtt) {
//    WiFiClient mqtt;
    Serial.println("Connecting mqtt.....");
	while ( mqtt.connect("4duk.su",9009)==0 && nreq>0) {
    Serial.println("Error connect to mqtt.....");
    nreq--;
	}
	if (nreq==0) {
	Serial.println("Error connect to mqtt. MQTT disable.");
	} else {
	connected=true;
	}
    mqtt.println(gateid);
    //mqtt.keepAlive(10,10,3);
    }
    Serial.println("Start MDNS "+String(mdns_name)+".local on this device.");
    //MDNS.begin(mdns_name);
//    WiFiUDP udp;
    Serial.println("Start local udp on 9009 port this device.");
    //udp.begin(9009);
    return connected;
};

void gate4duk::sendcommand(IPAddress ip,char *md_n,char *act,char *act_val) 
{
    uint8_t sbuf[1024];
    String cmd="device:"+String(md_n)+":action:"+String(act)+":value:"+String(act_val);
    cmd.getBytes(sbuf,cmd.length());
    udp.beginPacket(ip,9009);
    udp.write(sbuf,cmd.length());
    udp.endPacket();
};

void gate4duk::setbcast(IPAddress bc)
{
    bcast=bc;
};

bool gate4duk::processmqtt()
{
    unsigned int i;
    bool r_val=false;
#if defined(ESP8266)
    MDNS.update();
#endif
    int pack_size=mqtt.available();
    if (pack_size) {
    mqtt.read(pack_buf,pack_size);
#if defined(ESP32)
    String r_pack=String(pack_buf,pack_size);
#elif defined(ESP8266)
    String r_pack=String(pack_buf);
#endif
    Serial.println(r_pack);
	for (i=0;i<devices.size();i++) {
	String cmp_str="device:"+String(devices[i].mydev_name)+":";
	    if (r_pack.indexOf(cmp_str)>-1) {
	    r_val=true;
		if (devices[i].callaction) {
		devices[i].callaction(r_pack);
		}
	    }
	}
    }
    return r_val;
};

bool gate4duk::processudp()
{
    unsigned int i;
    bool r_val=false;
#if defined(ESP8266)
    MDNS.update();
#endif
    int pack_size=udp.parsePacket();
    if (pack_size) {
    udp.read(pack_buf,pack_size);
#if defined(ESP32)
    String r_pack=String(pack_buf,pack_size);
#elif defined(ESP8266)
    String r_pack=String(pack_buf);
#endif
    Serial.println(r_pack);
	for (i=0;i<devices.size();i++) {
	String cmp_str="device:"+String(devices[i].mydev_name)+":";
	    if (r_pack.indexOf(cmp_str)>-1) {
	    r_val=true;
		if (devices[i].callaction) {
		devices[i].callaction(r_pack);
		}
	    }
	}
    }
    return r_val;
};

String gate4duk::getlocalaction() 
{
    return "1";
};
String gate4duk::getlocalname() 
{
    return "1";
};
String gate4duk::getlocalval()
{
    return "1";
};

File - 4duk.h

#include <vector>
#include <functional>
#include <IPAddress.h>
#include <WiFiUdp.h>
#include <WiFiClient.h>

typedef enum {
DEV_CAMERA=240,
DEV_COOKING=250,
DEV_COOKING_COFFEE_MAKER=251,
DEV_COOKING_KETTLE=252,
DEV_COOKING_MULTICOOKER=253,
DEV_DISHWASHER=260,
DEV_HIMIDIFIER=270,
DEV_IRON=280,
DEV_LIGHT=100,
DEV_LIGHT_CEILING=101,
DEV_LIGHT_LIGHT_STRIP=102,
DEV_MEDIA_DEVICE=110,
DEV_MEDIA_DEVICE_RECEIVER=111,
DEV_MEDIA_DEVICE_TV=112,
DEV_MEDIA_DEVICE_TV_BOX=113,
DEV_OPENABLE=120,
DEV_OPENABLE_CURTAIN=121,
DEV_OTHER=130,
DEV_PET_DRINKING_FONTAIN=140,
DEV_PET_FEEDER=141,
DEV_PURFIER=150,
DEV_SENSOR=160,
DEV_SENSOR_BUTTON=161,
DEV_SENSOR_CLIMATE=162,
DEV_SENSOR_GAS=163,
DEV_SENSOR_ILLUMINATION=164,
DEV_SENSOR_MOTION=165,
DEV_SENSOR_OPEN=166,
DEV_SENSOR_SMOKE=167,
DEV_SENSOR_VIBRATION=168,
DEV_SENSOR_WATER_LEAK=169,
DEV_SMART_METER=170,
DEV_SMART_METER_COLD_WATER=171,
DEV_SMART_METER_ELECTRICITY=172,
DEV_SMART_METER_GAS=173,
DEV_SMART_METER_HEAT=174,
DEV_SMART_METER_HOT_WATER=174,
DEV_SOCKET=180,
DEV_SWITCH=190,
DEV_TERMOSTAT=200,
DEV_TERMISTAT_AC=201,
DEV_VACUUM_CLEANER=210,
DEV_VENTILATION_FAN=220,
DEV_WASHING_MACHINE=230,
DEV_CUSTOM_SNT_DOOR_=300
} device_type_t;

//typedef void (*callback)(String s1);
typedef std::function<void(String &s1)> callback;
typedef std::function<String(String &s1)> callstat;

typedef struct {
    device_type_t	device_type;
    char *mydev_name;
    char *alice_name;
    char *room_name;
    char *creator_name;
    char *creator_mail;
    float dev_ver;
    bool is_real;
    callback callaction;
    callstat callstate;
} device4duk ;

typedef std::vector<device4duk> devices4duk;



class gate4duk
{
private:
devices4duk devices;
//class WiFiUDP udp;
WiFiUDP udp;
IPAddress bcast;
//class WiFiClient mqtt;
WiFiClient mqtt;
char *gateid;
char *mdns_name;
bool connected;
#if defined(ESP32)
uint8_t pack_buf[1024];
#elif defined(ESP8266)
char pack_buf[1024];
#endif
uint32_t cmil;
public:
    gate4duk();
    ~gate4duk();
    void setid(char *gateid);
    void setid(String gateid);
    void setname(char *name);
    void setbcast(IPAddress bc);
    void addrealdev(device_type_t d_t,char *md_n,char *a_n,char *r_n,char *c_n,char *c_m,float d_v,callback cba,callstat cbs);
    //void addrealdev(device_type_t d_t,String *md_n,String *a_n,String *r_n,String *c_n,String *c_m,float d_v,callback cba,callstat cbs);
    void addhiddev(device_type_t d_t,char *md_n,char *a_n,char *r_n,char *c_n,char *c_m,float d_v,callback cba,callstat cbs);
    void sendstatus();
    void sendstatus(char *mydn,char *stpar,char *stval);
    bool is_connected();
    bool connect(bool to_mqtt=true);
    bool reconnect(bool to_mqtt=true);
    void sendcommand(IPAddress ip,char *md_n,char *act,char *act_val);
    bool processmqtt();
    bool processudp();
    String getlocalaction();
    String getlocalname();
    String getlocalval();
};

Молодец!

Есть еще подводный камень, но хотелось бы, чтобы проверили в такой конфигурации пока.
Ошибка тянется очень давно во многих релизах, но возможно проявляется только у меня.

Помимо станции ещё схему нужно, это точно.

C:\ARDUINO\2025\wifitvpult\wifitvpult.ino: In function 'void updatetvs()':
C:\ARDUINO\2025\wifitvpult\wifitvpult.ino:236:3: error: 'parse_tvs' was not declared in this scope
  236 |   parse_tvs();
      |   ^~~~~~~~~
C:\ARDUINO\2025\wifitvpult\wifitvpult.ino: In function 'void loop()':
C:\ARDUINO\2025\wifitvpult\wifitvpult.ino:395:9: error: 'get_url_content' was not declared in this scope
  395 |         get_url_content(loc_url_avtransport);
      |         ^~~~~~~~~~~~~~~