Newer
Older
smart-home-server / webclient / dist / js / main.js
(()=>{var J=Object.defineProperty;var k=Object.getOwnPropertySymbols;var B=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var C=(n,e,t)=>e in n?J(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,v=(n,e)=>{for(var t in e||(e={}))B.call(e,t)&&C(n,t,e[t]);if(k)for(var t of k(e))Y.call(e,t)&&C(n,t,e[t]);return n};var H=(n,e,t)=>new Promise((s,i)=>{var a=c=>{try{o(t.next(c))}catch(l){i(l)}},r=c=>{try{o(t.throw(c))}catch(l){i(l)}},o=c=>c.done?s(c.value):Promise.resolve(c.value).then(a,r);o((t=t.apply(n,e)).next())});var p,b;function W(){p.dataset.navState="displayed",p.classList.remove("state-off"),p.classList.add("state-on"),b.classList.add("a-show")}function R(){p.dataset.navState="hidden",p.classList.remove("state-on"),p.classList.add("state-off"),b.classList.add("a-hide"),b.classList.remove("a-show"),setTimeout(()=>{b.classList.remove("a-hide")},300)}function N(){console.log("HUD init"),p=document.querySelector(".hud .nav-toggle"),b=document.querySelector(".hud .navigation"),p.addEventListener("click",n=>{n.currentTarget.dataset.navState!="displayed"?W():R()})}var y=class{constructor(e,t,s){this.screens={},this.routesMap={},this.currentScreen=null,this.eventsHandlers={switch:[]},this.screensContainer=document.querySelector(e),this.loader=document.querySelector(t),this.errorScreen=document.querySelector(s),console.log("Screens Init")}add(e,t){if(typeof t!="object")return console.error("Screens: screens must be object");if(typeof(t==null?void 0:t.alias)=="undefined")return console.error("Screens: undefined alias");if(typeof(t==null?void 0:t.renderer)!="function")return console.error("Screens: renderer must be function");this.screens[t.alias]=v({route:e},t),this.routesMap[e]=t.alias}switch(e){var s;if(this.runSwitchHandlers(e),this.hideErrorScreen(),this.showLoader(),(s=this.currentScreen)==null||s.DOMObject.remove(),typeof this.screens[e]=="undefined"){console.error(`Screens: "${e}" not found`);return}this.currentScreen=this.screens[e];let t=document.createElement("div");t.classList.add("screen"),t.id=e,t.dataset.alias=e,t.innerHTML=this.currentScreen.renderer(),this.currentScreen.DOMObject=t,this.screensContainer.append(this.currentScreen.DOMObject),this.currentScreen.initer(this)}routing(){setInterval(()=>{let e=document.location.hash.split("#!")[1];if(typeof e=="undefined"||e=="")return;let t=typeof this.routesMap[e]=="undefined"?"not-found-screen":this.routesMap[e];(!this.currentScreen||this.currentScreen.alias!=t)&&this.switch(t)},50)}ready(){this.currentScreen!=null&&(this.currentScreen.DOMObject||(this.currentScreen.DOMObject=document.getElementsById(this.currentScreen.alias)),this.hideLoader(),this.currentScreen.DOMObject.classList.add("a-show"))}error(e,t){var s;(s=this.currentScreen)==null||s.DOMObject.remove(),this.errorScreen.querySelector(".error-title").innerHTML=e,this.errorScreen.querySelector(".error-text").innerHTML=t,this.showErrorScreen()}hideLoader(){this.loader.classList.remove("a-show")}showLoader(){this.loader.classList.add("a-show")}showErrorScreen(){this.errorScreen.classList.add("a-show")}hideErrorScreen(){this.errorScreen.classList.remove("a-show")}getScreens(){return this.screens}getRoutesMap(){return this.routesMap}onSwitch(e){this.eventsHandlers.switch.push(e)}runSwitchHandlers(e){for(let t of this.eventsHandlers.switch)t(this,e)}};function G(n,e,t,s){return`
		<div class="toast toast-${n}" role="alert">
	    <div class="toast-content">
	      <h4 class="toast-title">${e} ${t}</h4>
	      <p class="toast-text">${s}</p>
	    </div>
	    <button class="btn-icon toast-close" type="button" aria-label="Close">\u2715</button>
	  </div>
	`}function K(n){return n.close=function(){this.classList.add("a-hide"),setTimeout(()=>{this.remove()},300)},n.querySelector(".toast-close").addEventListener("click",e=>{n.close()}),n.show=function(){document.querySelector("body").append(n),setTimeout(()=>{n.classList.add("a-show")},10)},n}function g(n,e,t,s){let i=document.createElement("div");return i.innerHTML=G(n,e,t,s),K(i.childNodes[1])}function Q(n,e){return g("success",'<i class="ph ph-check-circle"></i>',n,e)}function V(n,e){return g("info",'<i class="ph ph-info"></i>',n,e)}function X(n,e){return g("warning",'<i class="ph ph-warning"></i>',n,e)}function x(n,e){return g("danger",'<i class="ph ph-warning-octagon"></i>',n,e)}var j={create:g,createInfo:V,createSuccess:Q,createWarning:X,createError:x,createDanger:x};function Z(n){let e="";for(let t of n){let s="",i="";t.route&&(s=`<a class="list-action" href="${t.route}">`,i="</a>"),e+=`
			<li class="list-item ${t.is_active?"list-item-active":""}">
				${s}${t.content}${i}
			</li>
		`}return`
		<div class="sidebar block">
			<ul class="list list-nav">
				${e} 
			</ul>
		</div>
	`}function ee(n,e,t,s){let i='<tr class="table-row">';for(let o in e)i+=`<th scope="col">${e[o]}</th>`;i+="</tr>";let a="";for(let o of t){a+='<tr class="table-row">';for(let c in e)a+=`<td>${o[c]}</td>`;a+="</tr>"}let r="";return typeof s!="undefined"&&(r=`
			<tfoot class="table-foot">
				<tr class="table-row">
					<td colspan="${e.length}">
						${s}
					</td>
				</tr>
			</tfoot>
		`),`
		<table class="table devices-lists">
			<caption class="table-caption">${n}</caption>
			<thead class="table-head">${i}</thead>
			<tbody class="table-body">${a}</tbody>
			${r}
		</table>
	`}var q={template:{sidebarNav:Z,table:ee}};var w=class{constructor(e){this.core=e}actions_list(e){return this.core.api_get("/api/v1/scripts/actions/list",e)}scopes_list(e){return this.core.api_get("/api/v1/scripts/scopes/list",e)}regular_list(e){return this.core.api_get("/api/v1/scripts/regular/list",e)}scope_get_by_filename(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/scopes/name/${s}`,t,{})}scope_create(e,t){return this.core.api_post("/api/v1/scripts/scopes/new",e,t)}scope_update(e,t){return this.core.api_post("/api/v1/scripts/scopes/update",e,t)}action_enable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/alias/${s}/enable`,t)}action_disable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/alias/${s}/disable`,t)}regular_enable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/regular/${s}/enable`,t)}regular_disable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/regular/${s}/disable`,t)}scope_enable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/scope/${s}/enable`,t)}scope_disable(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/actions/scope/${s}/disable`,t)}scope_remove(e,t){let s=encodeURIComponent(String(e||""));return this.core.api_get(`/api/v1/scripts/scopes/name/${s}/remove`,t)}run(e,t){return this.core.api_post("/api/v1/scripts/actions/run",e,t)}};var $=class{constructor(e){this.core=e}list(e){return this.core.api_get("/api/v1/devices/list",e)}scanning_setup(e){return this.core.api_get("/api/v1/devices/scanning/setup",e)}scanning_all(e){return this.core.api_get("/api/v1/devices/scanning/all",e)}setup_new_device(e,t){return this.core.api_post("/api/v1/devices/setup/new-device",e,t)}info(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/devices/id/${s}/info`,t)}get(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/devices/id/${s}`,t)}status(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/devices/id/${s}/status`,t)}action(e,t){return this.core.api_post("/api/v1/devices/action",e,t)}remove(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/devices/id/${s}/remove`,t)}reboot(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/devices/id/${s}/reboot`,t)}};var L=class{constructor(e){this.core=e}list(e){return this.core.api_get("/api/v1/areas/list",e)}inner_list(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/areas/id/${s}/list`,t)}new_area(e,t){return this.core.api_post("/api/v1/areas/new-area",e,t)}remove(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/areas/id/${s}/remove`,t)}place_in_area(e,t){return this.core.api_post("/api/v1/areas/place-in-area",e,t)}update_display_name(e,t){return this.core.api_post("/api/v1/areas/update-display-name",e,t)}update_alias(e,t){return this.core.api_post("/api/v1/areas/update-alias",e,t)}devices(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/areas/id/${s}/devices`,t)}unassign_from_area(e,t){let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/areas/id/${s}/unassign-from-area`,t)}types_list(e){return this.core.api_get("/api/v1/areas/types/list",e)}reboot_devices(e,t){if(e==null)return this.core.api_get("/api/v1/areas/reboot_devices",t);let s=encodeURIComponent(String(e));return this.core.api_get(`/api/v1/areas/id/${s}/reboot_devices`,t)}};function U(n){if(!n||typeof n!="object")return"";let e=new URLSearchParams;Object.entries(n).forEach(([s,i])=>{i!=null&&e.append(s,String(i))});let t=e.toString();return t?`?${t}`:""}function te(n,e){let t=String(n||"").replace(/\/+$/,""),s=String(e||"").replace(/^\/+/,"");return`${t}/${s}`}function se(n){try{return{ok:!0,data:JSON.parse(n)}}catch(e){return{ok:!1,error:e}}}var T=class{constructor(e){this.base_url=(e==null?void 0:e.base_url)||"",this.token=(e==null?void 0:e.token)||"",this.timeout_ms=Number.isFinite(e==null?void 0:e.timeout_ms)?e.timeout_ms:15e3,this.default_headers=(e==null?void 0:e.default_headers)||{},this.on_unauthorized=typeof(e==null?void 0:e.on_unauthorized)=="function"?e.on_unauthorized:null,this.proxy_path=(e==null?void 0:e.proxy_path)||"",this.scripts=new w(this),this.devices=new $(this),this.areas=new L(this)}set_base_url(e){this.base_url=e||""}set_token(e){this.token=e||""}set_proxy_path(e){this.proxy_path=e||""}_wrap_path(e,t){if(!this.proxy_path)return t?`${e}${U(t)}`:e;let s=v({path:e},t||{});return`${this.proxy_path}${U(s)}`}request(e,t,s,i,a){let r=typeof i=="function"?i:()=>{},o=te(this.base_url,t),c=new AbortController,l=Number.isFinite(a==null?void 0:a.timeout_ms)?a.timeout_ms:this.timeout_ms,h=setTimeout(()=>c.abort(),l),_=v(v({},this.default_headers),(a==null?void 0:a.headers)||{});this.token&&(_.Authorization=`Bearer ${this.token}`);let E;s!=null&&(_["Content-Type"]="application/json",E=JSON.stringify(s)),fetch(o,{method:e,headers:_,body:E,signal:c.signal}).then(d=>H(this,null,function*(){clearTimeout(h);let m={url:o,method:e,status_code:d.status,headers:d.headers},S=yield d.text(),O=se(S),u=O.ok?O.data:S;if(!d.ok){let f={type:"http_error",message:`HTTP ${d.status}`,status_code:d.status,raw:u};if((d.status===401||d.status===403)&&this.on_unauthorized)try{this.on_unauthorized({error:f,meta:m})}catch(D){}return r(f,null,m)}if(O.ok&&u&&typeof u=="object"){let f=u.status;if(f===!1||f==="error"){let D={type:"api_error",message:u.message||"API error",status_code:d.status,raw:u,field:u.field};return r(D,null,m)}}return r(null,u,m)})).catch(d=>{clearTimeout(h);let S=d&&(d.name==="AbortError"||String(d).includes("AbortError"))?{type:"timeout",message:`Timeout after ${l}ms`}:{type:"network_error",message:(d==null?void 0:d.message)||"Network error",details:d};return r(S,null,{url:o,method:e,status_code:0,headers:null})})}get(e,t,s){return this.request("GET",e,null,t,s)}post(e,t,s,i){return this.request("POST",e,t,s,i)}api_get(e,t,s,i){return this.get(this._wrap_path(e,s),t,i)}api_post(e,t,s,i,a){return this.post(this._wrap_path(e,i),t,s,a)}};function A(n,e){return console.log(n),Modals.create("device-popup",{title:`Device ${n.name}`,body:t=>{let s="";for(let i in n)s+=`
					<tr class="table-row">
						<th>${i}: </th>
						<td>${n[i]}</td>
					</tr>
				`;return`
				<div class="block">
					<table class="table" style="border: 0">
						<tbody class="table-body">
							${s}
						</tbody>
					</table>
				</div>
			`},actions:t=>{let s=document.createElement("button");s.classList.add("btn"),s.classList.add("btn-primary"),s.innerHTML="Closed",s.addEventListener("click",r=>{t.close()});let i=document.createElement("button");i.classList.add("btn"),i.classList.add("btn-warning"),i.classList.add("with-icon"),i.innerHTML='<i class="ph ph-arrow-clockwise"></i> Reboot',i.addEventListener("click",r=>{e.devices.reboot(n.id,(o,c,l)=>{console.log("Reboot done"),t.close(),setTimeout(()=>{Toasts.createSuccess("Reboot successful",`Device: ${n.name}<br>
								Alias: <b>${n.alias}</b>`).show()},300)})});let a=document.createElement("button");return a.classList.add("btn"),a.classList.add("btn-danger"),a.classList.add("with-icon"),a.innerHTML='<i class="ph ph-trash"></i> Remove',a.addEventListener("click",r=>{confirmPopup("Are you sure you want to remove this device?",()=>{e.devices.remove(n.id,(o,c,l)=>{console.log("Was removed"),t.close(),setTimeout(()=>{Toasts.createSuccess("Removed",`
										Device: ${n.name}<br>
										Alias: <b>${n.alias}</b><br>
										IP: <b>${n.device_ip}</b>
										`).show()},300)})},()=>{console.log("CANCELED")})}),[s,i,a]}})}function P(n){return Helper.template.sidebarNav([{content:'<span class="list-label"><i class="ph ph-cpu"></i> Devices</span>',route:"/#!/devices",is_active:n=="devices"},{content:'<span class="list-label"><i class="ph ph-magnifying-glass"></i> Scanning</span>',route:"/#!/devices/scanning",is_active:n=="scanning"}])}function ne(n){return{alias:"devices",renderer:()=>`
				<div class="container">
					<div class="row g-6">
						<div class="col sidebar-container">
							${P("devices")}
						</div>
						<div class="col devices-container"></div>
					</div>
				</div>
			`,initer:e=>{n.devices.list((t,s,i)=>{if(console.log("sh_api.devices.list",t,s,i),i.status_code!=200)return e.error("Server API ERROR","");let a=[];for(let r of s.data.devices){let o=r.connection_state=="active"?'<span class="badge badge-success">Online</span>':'<span class="badge badge-warning">Offline</span>';a.push({deviceName:r.name,alias:r.alias,status:o,ip:`<code class="code">${r.device_ip}</code>`,actions:`
							<button 
								class="btn btn-secondary btn-small details-btn" 
								data-device='${JSON.stringify(r)}'
								type="button"
							>Details</button>

							<button 
								class="btn btn-warning btn-small reboot-btn" 
								data-device-id="${r.id}" 
								data-device-name="${r.name}" 
								data-device-alias="${r.alias}" 
								type="button"
							>Reboot</button>
						`})}e.currentScreen.DOMObject.querySelector(".devices-container").innerHTML=Helper.template.table("Devices list",{deviceName:"Device name",alias:"Device alias",status:"Status",ip:"IP",actions:"Actions"},a,`<span class="table-meta">Total: <span class="total">${s.data.total}</span> devices</span>`),e.currentScreen.DOMObject.querySelectorAll(".reboot-btn").forEach(r=>{r.addEventListener("click",o=>{let c=o.currentTarget.dataset.deviceId,l=o.currentTarget.dataset.deviceName,h=o.currentTarget.dataset.deviceAlias;n.devices.reboot(c,(_,E,d)=>{console.log("Reboot done",Toasts),Toasts.createSuccess("Reboot successful",`Device: ${l}<br>
									Alias: <b>${h}</b>`).show()})})}),e.currentScreen.DOMObject.querySelectorAll(".details-btn").forEach(r=>{r.addEventListener("click",o=>{let c=JSON.parse(o.currentTarget.dataset.device);A(c,n).show()})}),e.ready()})}}}function ie(n){return{alias:"devices-scanning",renderer:()=>`
				<div class="container">
					<div class="row g-6">
						<div class="col sidebar-container">
							${P("scanning")}
						</div>
						<div class="col devices-container"></div>
					</div>
				</div>
			`,initer:e=>{n.devices.scanning_all((t,s,i)=>{if(console.log("sh_api.devices.scanning_all",t,s),i.status_code!=200)return e.error("Server API ERROR","");let a=[];for(let r of s.data.devices)a.push({deviceId:r.device_id,deviceName:r.device_name,deviceType:r.device_type,status:`<span class="badge badge-primary">${r.status}</span>`,ip:`<code class="code">${r.ip_address}</code>`,wifiSignal:r.wifi_signal,actions:`
							<button 
								class="btn btn-secondary btn-small setup-btn" 
								data-device-id="${r.device_id}"
								data-device-ip="${r.ip_address}"
								data-device-name="${r.device_name}"
								type="button"
							>Setup</button>
						`});e.currentScreen.DOMObject.querySelector(".devices-container").innerHTML=Helper.template.table("Found devices",{deviceId:"Device ID",deviceName:"Device name",deviceType:"Type",status:"Status",ip:"IP",wifiSignal:"Signal",actions:"Actions"},a,`<span class="table-meta">Total: <span class="total">${s.data.devices.length}</span> devices</span>`),e.currentScreen.DOMObject.querySelectorAll(".setup-btn").forEach(r=>{r.addEventListener("click",o=>{let c=o.currentTarget.dataset.deviceIp,l=o.currentTarget.dataset.deviceId,h=o.currentTarget.dataset.deviceName})}),e.ready()})}}}var I={list:ne,scanning:ie};function z(n,e){n.add("/",{alias:"home",renderer:()=>'<h2 class="mt-4">Hello world</h2>',initer:t=>{setTimeout(()=>t.ready(),1e3),setTimeout(()=>t.error("Error","Just testing"),2e3)}}),n.add("-",{alias:"not-found-screen",renderer:()=>'<h2 class="mt-4">404 NOT FOUND</h2>',initer:t=>{t.ready()}}),n.add("/devices",I.list(e)),n.add("/devices/scanning",I.scanning(e))}function re(n,e,t){return`
		<div class="modal" aria-hidden="true" id="${n}">
      <div class="modal-backdrop"></div>

      <div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="modal-title-basic">
        <header class="modal-header">
          <h4 class="modal-title" id="modal-title-basic">${e}</h4>
          <button class="btn-icon modal-close" type="button" aria-label="Close">\u2715</button>
        </header>

        <div class="modal-body"></div>
        <footer class="modal-footer">${t}</footer>
      </div>
    </div>
	`}function ae(n){return n.show=function(){document.querySelector("body").append(n),setTimeout(()=>{this.classList.add("a-show")},10)},n.close=function(){this.classList.add("a-hide"),setTimeout(()=>{this.remove()},300)},n.querySelector(".modal-close").addEventListener("click",e=>{n.close()}),n}function oe(n,e){let t=e.title||"",s=e.footer||"",i=document.createElement("div");i.innerHTML=re(n,t,s);let a=i.childNodes[1],r=a.querySelector(".modal-body"),o=a.querySelector(".modal-footer");if(typeof e.actions=="function"){let c=e.actions(a);if(typeof c[0]=="object"){let l=document.createElement("div");l.classList.add("actions");for(let h of c)l.append(h);o.append(l)}}if(typeof e.body=="function"){let c=e.body(a);typeof c=="object"?r.append(c):typeof c=="string"&&(r.innerHTML=c)}return ae(a)}var F={create:oe};function M(n,e,t){Modals.create("confirm-popup",{title:"Requires confirmation",body:s=>`
				<p>${n}</p>
			`,actions:s=>{let i=document.createElement("button");i.classList.add("btn"),i.classList.add("btn-primary"),i.innerHTML="NO",i.addEventListener("click",r=>{s.close(),t()});let a=document.createElement("button");return a.classList.add("btn"),a.classList.add("btn-warning"),a.innerHTML="YES",a.addEventListener("click",r=>{s.close(),e()}),[i,a]}}).show()}document.addEventListener("DOMContentLoaded",n=>{console.log("App init"),window.Toasts=j,window.Helper=q,window.Modals=F,window.confirmPopup=M,N();let e=new T({base_url:"http://shswebclient.local",token:"YOUR_TOKEN",timeout_ms:3e3,on_unauthorized:({error:s})=>console.log("auth problem:",s),proxy_path:"/proxy.php"}),t=new y(".screens",".load-screen",".error-screen");z(t,e),console.log(t.getScreens()),t.onSwitch((s,i)=>{R()}),t.routing()});})();
//# sourceMappingURL=main.js.map