/***********************
* Shelly Method Explorer v1 (lite)
* Maintained by: Shelly Nordics - Ronni
***********************/
let EP_UI="start",EP_CSS="start_css",EP_APP1="start_app1",EP_APP2="start_app2",EP_APP3="start_app3";
let HTML=null,CSS=null,APP1=null,APP2=null,APP3=null;
function send(res,body,ct){
res.code=200;
res.headers=[["Content-Type",(ct||"text/plain")+"; charset=utf-8"],["Cache-Control","no-store"]];
res.body=body;
res.send();
}
function buildHTML(){
return '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">'+
'<title>Shelly Method Explorer</title>'+
'<link rel="icon" type="image/png" href="data:image/png;base64,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">'+
'<link rel="stylesheet" href="start_css">'+
'</head><body>'+
'<div class="topbar"><div class="lefttop"><span class="title">Shelly Method Explorer</span><span id="dev" class="dev">Device: ...</span></div><span id="st" class="status">Loading...</span></div>'+
'<div class="toolbar">'+
' <button id="fav" class="btn">★</button>'+
' <button id="ac" class="btn">⇅</button>'+
' <button id="t" class="btn">≡</button>'+
' <input id="f" class="in" placeholder="Filter..." style="width:100px">'+
' <input id="i" class="in" placeholder="id" style="width:50px">'+
' <button id="r" class="btn">↻</button>'+
' <select id="m" class="btn" style="cursor:pointer;margin-left:auto">'+
' <option value="" selected disabled hidden>Links...</option>'+
' <option value="https://shelly-api-docs.shelly.cloud/gen2/">API Docs</option>'+
' <option value="https://shelly-api-docs.shelly.cloud/gen2/Scripts/Overview">Script language</option>'+
' <option value="https://shelly.guide/">Shelly Guide</option>'+
' <option value="https://control.shelly.cloud/">Shelly Cloud</option>'+
' <option value="https://kb.shelly.cloud/">Shelly KB</option>'+
' <option value="https://support.shelly.cloud/">Shelly Support</option>'+
' <option value="https://installers-portal.shelly.cloud/">Installers Portal</option>'+
' </select>'+
'</div>'+
'<div class="wrap">'+
' <div class="list" id="l"></div>'+
' <div class="right">'+
' <div class="reshead"><span class="resh">Result</span><br><span id="sel" class="ress">Klik en metode...</span></div>'+
' <pre id="o" class="out"></pre>'+
' </div>'+
'</div>'+
'<div class="foot"><span class="brand">Shelly Method Explorer</span> v1.0 - Shelly Nordics</div>'+
'<script src="start_app1"></script><script src="start_app2"></script><script src="start_app3"></script>'+
'</body></html>';
}
function buildCSS(){
return 'body{font-family:system-ui;background:#0d0e10;color:#e0e0e0;margin:0 auto;padding:8px;display:flex;flex-direction:column;gap:8px;height:100vh;box-sizing:border-box;max-width:960px}'+
'html{background:#0d0e10}'+
'.topbar{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;background:#1a1c1f;border-radius:12px}'+
'.title{font-size:18px;font-weight:900}.dev{font-size:12px;opacity:.75}.lefttop{display:flex;flex-direction:column;gap:2px}.status{font-size:12px;opacity:.7}'+
'.toolbar{display:flex;gap:8px;flex-wrap:wrap;background:#1a1c1f;padding:10px 12px;border-radius:12px;align-items:center}'+
'.in{padding:7px 10px;background:#111214;border:1px solid #333;color:#eee;border-radius:8px;font-weight:600;outline:none}'+
'.btn{padding:7px 12px;background:#252627;border:1px solid #444;color:#eee;border-radius:8px;font-weight:700;cursor:pointer}.btn.on{background:#141414;border-color:#777}'+
'.wrap{flex:1;display:flex;gap:10px;min-height:0}'+
'.list{width:300px;min-width:240px;background:#1a1c1f;border:1px solid #333;border-top-left-radius:12px;border-bottom-left-radius:12px;border-top-right-radius:0;border-bottom-right-radius:0;overflow:auto}'+
'.right{flex:1;background:#1a1c1f;border:1px solid #333;border-radius:12px;overflow:auto;display:flex;flex-direction:column}'+
'.reshead{padding:10px 12px;border-bottom:1px solid #333;background:#2a2c2f;border-radius:12px 12px 0 0}'+
'.resh{font-weight:800}.ress{font-size:12px;opacity:.7}'+
'.out{margin:0;padding:12px;white-space:pre-wrap;word-break:break-word;font:13px monospace;color:#ddd;flex:1;overflow:auto;background:#303235}'+
'.foot{text-align:center;font-size:11px;opacity:.55;padding:6px;background:#1a1c1f;border-radius:12px}'+
'.brand{color:#9fb7ff;font-weight:800}'+
'.grp{padding:9px 12px;border-bottom:1px solid #333;cursor:pointer;font-weight:700;background:#252627;display:flex;align-items:center;font-family:monospace;font-size:16px}'+
'.grp:hover{background:#2d2f32}.grp.open{background:#2b2d31}'+
'.gn{flex:1}.cnt{opacity:.6;font-size:12px;margin-left:5px}.doc{color:#9bf;text-decoration:none;font-weight:700;margin-right:6px}'+
'.it{padding:7px 12px 7px 22px;border-bottom:1px solid #333;cursor:pointer;background:#1f1f1f;position:relative;font-family:monospace;font-size:13px}'+
'.it:hover{background:#2a2a2a}'+
'.it:before{content:"";position:absolute;left:9px;top:0;bottom:0;width:3px;background:#3a3a3a;border-radius:3px}'+
'.it.disabled{opacity:.4;cursor:not-allowed}'+
'::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#1b1b1b;border-radius:10px}'+
'::-webkit-scrollbar-thumb{background:#3a3a3a;border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#4a4a4a}'+
'html{scrollbar-color:#3a3a3a #1b1b1b;scrollbar-width:thin}'+
'@media(max-width:720px){.wrap{flex-direction:column}.list{width:auto!important;min-width:0!important;max-height:35vh}}';
}
function buildAPP1(){
return '(function(){'+
'function E(x){return document.getElementById(x)}'+
'function esc(s){return(""+s).replace(/[&<>"]/g,function(c){return{"&":"&","<":"<",">":">"}[c]||c})}'+
'function pj(t){try{return JSON.stringify(JSON.parse(t),null,2)}catch(e){return t}}'+
'var st=E("st"),f=E("f"),id=E("i"),tog=E("t"),rel=E("r"),menu=E("m"),favBtn=E("fav"),acBtn=E("ac"),l=E("l"),sel=E("sel"),o=E("o");'+
'var all=[],open={},grouped=true,autoCollapse=true,favOn=false;'+
'var FAV=["Shelly","Switch","Light","Sys","EM"];'+
'var DIS={"Shelly.FactoryReset":1,"Shelly.ResetWiFiConfig":1};'+
'function S(h){st.innerHTML=h}'+
'function setT(){tog.classList.toggle("on",!grouped)}'+
'function setACBtn(){acBtn.classList.toggle("on",!autoCollapse)}'+
'function setFavBtn(){favBtn.classList.toggle("on",favOn)}'+
'function key(x){var p=x.indexOf("."),g=p>0?x.slice(0,p):"(other)";return g==="Wifi"?"WiFi":g==="WS"?"Ws":g}'+
'function it(x){return\'<div class="it\'+(DIS[x]?" disabled":"")+\'" data-m="\'+esc(x)+\'">\'+esc(x)+\'</div>\'}'+
'var D="https://shelly-api-docs.shelly.cloud/gen2/";'+
'var B1=D+"ComponentsAndServices/",B2=D+"DynamicComponents/",B3=B2+"Virtual/",B4=B2+"BTHome/";'+
'function docUrl(g){'+
' var V={"Boolean":1,"Number":1,"Text":1,"Enum":1,"Button":1,"Group":1};'+
' if(V[g])return B3+encodeURIComponent(g);if(g==="Virtual")return B3;'+
' if(g==="OTA")return D+"ComponentsAndServices/Shelly/#http-endpoint-ota";'+
' if(g==="KNX")return D+"Integrations/KNX/";if(g==="BTHome")return B2+"BTHome";'+
' if(g.indexOf("BTHome")>=0)return B4+encodeURIComponent(g);return B1+encodeURIComponent(g);'+
'}'+
'function grp(g,n,op){'+
' var star=favOn&&FAV.indexOf(g)>=0?"★ ":"";'+
' return\'<div class="grp\'+(op?" open":"")+\'" data-g="\'+esc(g)+\'"><span class="gn">\'+star+esc(g)+\'<span class="cnt">(\'+n+\')</span> \'+(op?"▾":"▸")+\'</span><a class="doc" href="\'+docUrl(g)+\'" target="_blank" data-doc="1">↗</a></div>\';'+
'}'+
'function render(){'+
' var q=(f.value||"").trim().toLowerCase();'+
' if(!all.length){l.innerHTML="No methods";S("0 methods");return}'+
' if(!grouped){'+
' var a=all.filter(function(x){return !q||x.toLowerCase().indexOf(q)>=0}).sort();'+
' l.innerHTML=a.map(it).join("")||"No matches";S(a.length+" methods");return;'+
' }'+
' var map={},h="",groups=0,shown=0;'+
' all.forEach(function(x){if(!q||x.toLowerCase().indexOf(q)>=0){var g=key(x);(map[g]=map[g]||[]).push(x)}});'+
' var keys=Object.keys(map);'+
' if(favOn){keys.sort(function(a,b){var ia=FAV.indexOf(a),ib=FAV.indexOf(b);if(ia>=0&&ib>=0)return ia-ib;if(ia>=0)return -1;if(ib>=0)return 1;return a.localeCompare(b)})}else keys.sort();'+
' keys.forEach(function(g){var a=map[g];if(!a||!a.length)return;groups++;shown+=a.length;if(open[g]===undefined)open[g]=0;h+=grp(g,a.length,open[g]);if(open[g])a.sort().forEach(function(x){h+=it(x)})});'+
' l.innerHTML=h||"No matches";'+
' S(groups+" groups / "+shown+" methods");'+
'}'+
'function call(m){if(DIS[m])return;var v=id.value.trim();var url="/rpc/"+encodeURIComponent(m)+(v?"?id="+encodeURIComponent(v):"");'+
' sel.textContent=url;o.textContent="Loading...";'+
' fetch(url,{cache:"no-store"}).then(function(r){return r.text().then(function(t){return{c:r.status,t:t}})})'+
' .then(function(x){o.textContent="HTTP "+x.c+"\\n\\n"+pj(x.t)})'+
' .catch(function(e){o.textContent="Error: "+e});'+
'}'+
'function load(){S("Loading...");l.innerHTML="";o.textContent="";sel.textContent="Klik en metode...";'+
' fetch("/rpc/Shelly.ListMethods",{cache:"no-store"}).then(function(r){return r.json()})'+
' .then(function(j){all=j.methods||[];setT();setACBtn();setFavBtn();render()})'+
' .catch(function(e){S("Load failed: "+e)});'+
'}'+
'window.__SME={l:l,f:f,id:id,tog:tog,rel:rel,menu:menu,favBtn:favBtn,acBtn:acBtn,sel:sel,o:o,open:open,'+
' getG:function(){return grouped},setG:function(v){grouped=v},'+
' getAC:function(){return autoCollapse},setAC:function(v){autoCollapse=v;setACBtn()},'+
' getFav:function(){return favOn},setFav:function(v){favOn=v;setFavBtn()},'+
' setT:setT,render:render,call:call,load:load};'+
'})();';
}
function buildAPP2(){
return '(function(){var X=window.__SME;if(!X)return;'+
'X.l.onclick=function(ev){'+
' var el=ev.target;if(el&&el.dataset&&el.dataset.doc)return;'+
' while(el&&el!==X.l&&(!el.dataset||(!el.dataset.g&&!el.dataset.m)))el=el.parentNode;'+
' if(!el||el===X.l)return;'+
' if(el.dataset.g){if(X.getAC()&&!X.open[el.dataset.g])for(var k in X.open)X.open[k]=0;X.open[el.dataset.g]=!X.open[el.dataset.g];X.render();return}'+
' if(el.dataset.m)X.call(el.dataset.m);'+
'};'+
'X.f.oninput=X.render;X.rel.onclick=X.load;'+
'X.tog.onclick=function(){X.setG(!X.getG());X.setT();X.render()};'+
'X.acBtn.onclick=function(){X.setAC(!X.getAC())};'+
'X.favBtn.onclick=function(){X.setFav(!X.getFav());X.render()};'+
'X.menu.onchange=function(){var v=X.menu.value;if(v)window.open(v,"_blank");X.menu.value=""};'+
'})();';
}
function buildAPP3(){
return '(function(){'+
'var X=window.__SME;'+
'function esc(s){return(""+s).replace(/[&<>"]/g,function(c){return{"&":"&","<":"<",">":">"}[c]||c})}'+
'var dev=document.getElementById("dev");'+
'if(dev)fetch("/rpc/Shelly.GetDeviceInfo",{cache:"no-store"}).then(function(r){return r.json()})'+
' .then(function(j){dev.innerHTML="Device: "+esc(j.app||"?")+" - "+esc(j.model||"?")+" - "+esc(j.ver||j.fw_id||"?")})'+
' .catch(function(){dev.innerHTML="Device: (failed)"});'+
'if(X){X.setG(true);X.setT();X.load()}'+
'})();';
}
function ui(req,res){if(HTML===null)HTML=buildHTML();send(res,HTML,"text/html");}
function css(req,res){if(CSS===null)CSS=buildCSS();send(res,CSS,"text/css");}
function app1(req,res){if(APP1===null)APP1=buildAPP1();send(res,APP1,"text/plain");}
function app2(req,res){if(APP2===null)APP2=buildAPP2();send(res,APP2,"text/plain");}
function app3(req,res){if(APP3===null)APP3=buildAPP3();send(res,APP3,"text/plain");}
HTTPServer.registerEndpoint(EP_UI,ui);
HTTPServer.registerEndpoint(EP_CSS,css);
HTTPServer.registerEndpoint(EP_APP1,app1);
HTTPServer.registerEndpoint(EP_APP2,app2);
HTTPServer.registerEndpoint(EP_APP3,app3);
print("Shelly Method Explorer ready");
print("http://" + (Shelly.getComponentStatus("wifi").sta_ip ?? "192.168.33.1") + "/script/" + Shelly.getCurrentScriptId() + "/start");