diff --git a/server/SHServ/Controllers/DevicesRESTAPIController.php b/server/SHServ/Controllers/DevicesRESTAPIController.php index d79aef1..ae366b0 100644 --- a/server/SHServ/Controllers/DevicesRESTAPIController.php +++ b/server/SHServ/Controllers/DevicesRESTAPIController.php @@ -174,8 +174,22 @@ $device_status = $device -> device_api() -> get_status(); - if(!$device_status) { - return $this -> utils() -> response_error("device_request_fail"); + if(!$device_status || ($device_status["status"] ?? null) !== "ok") { + if($device -> connection_status != "lost") { + $device -> connection_status = "lost"; + $device -> update(); + } + + return $this -> utils() -> response_error("device_request_fail", [], [ + "device_id" => $device -> id(), + "connection_status" => "lost" + ]); + } + + if($device -> connection_status != "active") { + $device -> connection_status = "active"; + $device -> last_contact = date("Y-m-d H:i:s"); + $device -> update(); } $response_data = [ @@ -367,4 +381,4 @@ return $this -> utils() -> response_success(); } -} \ No newline at end of file +} diff --git a/server/SHServ/Helpers/DeviceScriptsHelper.php b/server/SHServ/Helpers/DeviceScriptsHelper.php index 34eb54f..d3ae413 100644 --- a/server/SHServ/Helpers/DeviceScriptsHelper.php +++ b/server/SHServ/Helpers/DeviceScriptsHelper.php @@ -26,7 +26,11 @@ * @return void */ public function sync_relay_to_btn_channel(\SHServ\Tools\DeviceAPI\Relay $relay_api, \SHServ\Tools\DeviceAPI\Button $btn_block_api, int $relay_channel = 0, int $btn_channel = 0): void { - $relay_channels = ($relay_api -> get_status())["channels"]; + $relay_channels = ($relay_api -> get_status())["channels"] ?? []; + + if(!isset($relay_channels[$relay_channel]["state"])) { + return; + } $btn_block_api -> set_channel_state( $relay_channels[$relay_channel]["state"] == "on" ? "enabled" : "disabled", diff --git a/server/SHServ/Tools/DeviceAPI/Base.php b/server/SHServ/Tools/DeviceAPI/Base.php index cb72d41..2c07531 100644 --- a/server/SHServ/Tools/DeviceAPI/Base.php +++ b/server/SHServ/Tools/DeviceAPI/Base.php @@ -46,7 +46,12 @@ */ public function get_status(): array { $resp = $this->request('GET', '/status'); - return $resp["data"] ?? $resp; + + if (($resp["http_code"] ?? 0) !== 200 || !is_array($resp["data"] ?? null)) { + return []; + } + + return $resp["data"]; } /** @@ -170,6 +175,7 @@ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, + CURLOPT_CONNECTTIMEOUT => 1, CURLOPT_TIMEOUT => 5, CURLOPT_HTTPHEADER => $headers, ]); diff --git a/server/SHServ/Tools/DeviceAPI/Button.php b/server/SHServ/Tools/DeviceAPI/Button.php index 6e20151..6cb8ef4 100644 --- a/server/SHServ/Tools/DeviceAPI/Button.php +++ b/server/SHServ/Tools/DeviceAPI/Button.php @@ -6,7 +6,7 @@ public function get_indicators(): Array { $status_response = $this -> get_status(); - if($status_response["status"] != "ok") { + if(($status_response["status"] ?? null) != "ok") { return []; } @@ -47,4 +47,4 @@ "channel" => $channel_id ]); } -} \ No newline at end of file +} diff --git a/server/SHServ/Tools/DeviceAPI/Hatch.php b/server/SHServ/Tools/DeviceAPI/Hatch.php index ac199ff..af5ca05 100644 --- a/server/SHServ/Tools/DeviceAPI/Hatch.php +++ b/server/SHServ/Tools/DeviceAPI/Hatch.php @@ -6,7 +6,7 @@ public function get_state(): String | null { $status_response = $this -> get_status(); - if($status_response["status"] != "ok") { + if(($status_response["status"] ?? null) != "ok") { return null; } @@ -56,4 +56,4 @@ "percent" => max(0, $percent) ]); } -} \ No newline at end of file +} diff --git a/server/SHServ/text-msgs.php b/server/SHServ/text-msgs.php index 463a00f..d56b4d1 100644 --- a/server/SHServ/text-msgs.php +++ b/server/SHServ/text-msgs.php @@ -30,7 +30,7 @@ "device_not_found" => "", "unknown_device" => "", "error_of_device_auth" => "", - "device_request_fail" => "", + "device_request_fail" => "Устройство не отвечает", "device_mode_error" => "", "db_error" => "", "device_error_of_auth" => "", @@ -55,4 +55,4 @@ "success_and_redirecting" => "Успешно! Перенаправление...", "accept" => "Принять", "confirm" => "Подтвердить", -]; \ No newline at end of file +]; diff --git a/webclient/dist/js/main.js b/webclient/dist/js/main.js index 247d97a..51cad36 100644 --- a/webclient/dist/js/main.js +++ b/webclient/dist/js/main.js @@ -1,4 +1,4 @@ -(()=>{var xe=Object.defineProperty,Fe=Object.defineProperties;var Pe=Object.getOwnPropertyDescriptors;var oe=Object.getOwnPropertySymbols;var je=Object.prototype.hasOwnProperty,Ue=Object.prototype.propertyIsEnumerable;var ce=(n,e,t)=>e in n?xe(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,h=(n,e)=>{for(var t in e||(e={}))je.call(e,t)&&ce(n,t,e[t]);if(oe)for(var t of oe(e))Ue.call(e,t)&&ce(n,t,e[t]);return n},le=(n,e)=>Fe(n,Pe(e));var de=(n,e,t)=>new Promise((a,s)=>{var r=c=>{try{o(t.next(c))}catch(l){s(l)}},i=c=>{try{o(t.throw(c))}catch(l){s(l)}},o=c=>c.done?a(c.value):Promise.resolve(c.value).then(r,i);o((t=t.apply(n,e)).next())});var b,T,D;function Be(){b.dataset.navState="displayed",b.classList.remove("state-off"),b.classList.add("state-on"),T.classList.add("a-show")}function z(){b.dataset.navState="hidden",b.classList.remove("state-on"),b.classList.add("state-off"),T.classList.add("a-hide"),T.classList.remove("a-show"),setTimeout(()=>{T.classList.remove("a-hide")},300)}function ue(){console.log("HUD init"),b=document.querySelector(".hud .nav-toggle"),T=document.querySelector(".hud .navigation"),D=document.querySelector(".hud .reload-screen"),b==null||b.addEventListener("click",n=>{n.currentTarget.dataset.navState!="displayed"?Be():z()}),D==null||D.addEventListener("click",n=>{Screens.reload()})}var M=class{constructor(e,t,a){this.screens={},this.routesMap={},this.currentScreen=null,this.eventsHandlers={switch:[],reload:[],reinit:[],errorScreen:[]},this.screensContainer=document.querySelector(e),this.loader=document.querySelector(t),this.errorScreen=document.querySelector(a),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]=h({route:e},t),this.routesMap[e]=t.alias}switch(e){var a;if(this.runSwitchHandlers(e),this.hideErrorScreen(),this.showLoader(),(a=this.currentScreen)==null||a.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)}reload(){this.currentScreen&&(this.runReloadHandlers(this.currentScreen.alias),this.switch(this.currentScreen.alias))}reinit(){this.currentScreen&&(this.currentScreen.initer(this),this.runReinitHandlers())}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 a;(a=this.currentScreen)==null||a.DOMObject.remove(),this.errorScreen.querySelector(".error-title").innerHTML=e,this.errorScreen.querySelector(".error-text").innerHTML=t,this.showErrorScreen(),this.runErrorScreenHandlers()}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)}onReaload(e){this.eventsHandlers.reload.push(e)}onReinit(e){this.eventsHandlers.reinit.push(e)}onErrorScreen(e){this.eventsHandlers.errorScreen.push(e)}runSwitchHandlers(e){let t=Object.keys(this.routesMap).filter(a=>this.routesMap[a]==e);for(let a of this.eventsHandlers.switch)a(this,e,t.length?t[0]:void 0)}runReloadHandlers(e){for(let t of this.eventsHandlers.reload)t(this,e)}runReinitHandlers(){for(let e of this.eventsHandlers.reinit)e(this)}runErrorScreenHandlers(){for(let e of this.eventsHandlers.errorScreen)e(this)}};var O=class{constructor(){this.data={}}set(e,t){this.data[e]=t}get(e){return this.data[e]}setRaw(e,t){this.set("raw."+e,t)}getRaw(e){return this.get("raw."+e)}invalidate(e){delete this.data[e]}invalidatePrefix(e){for(let t of Object.keys(this.data))t.startsWith(e)&&delete this.data[t]}getOrFetch(e,t,a){let s=this.data[e];if(typeof s!="undefined")return a(null,s);t((r,i)=>{!r&&typeof i!="undefined"&&(this.data[e]=i),a(r,i)})}getCollection(e){let t=[];for(let[a,s]of Object.entries(this.data))a.startsWith(e)&&t.push(s);return t}};var V="sh_fav_areas",pe={getAll(){try{return JSON.parse(localStorage.getItem(V)||"[]")}catch(n){return[]}},has(n){return this.getAll().includes(String(n))},add(n){let e=this.getAll(),t=String(n);e.includes(t)||(e.push(t),localStorage.setItem(V,JSON.stringify(e)))},remove(n){let e=this.getAll().filter(t=>t!==String(n));localStorage.setItem(V,JSON.stringify(e))},toggle(n){return this.has(n)?this.remove(n):this.add(n),this.has(n)}};function ze(n,e,t,a){return` +(()=>{var Pe=Object.defineProperty,Fe=Object.defineProperties;var je=Object.getOwnPropertyDescriptors;var ce=Object.getOwnPropertySymbols;var Ue=Object.prototype.hasOwnProperty,Be=Object.prototype.propertyIsEnumerable;var le=(n,e,t)=>e in n?Pe(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,b=(n,e)=>{for(var t in e||(e={}))Ue.call(e,t)&&le(n,t,e[t]);if(ce)for(var t of ce(e))Be.call(e,t)&&le(n,t,e[t]);return n},de=(n,e)=>Fe(n,je(e));var ue=(n,e,t)=>new Promise((a,s)=>{var r=c=>{try{o(t.next(c))}catch(l){s(l)}},i=c=>{try{o(t.throw(c))}catch(l){s(l)}},o=c=>c.done?a(c.value):Promise.resolve(c.value).then(r,i);o((t=t.apply(n,e)).next())});var S,T,M;function ze(){S.dataset.navState="displayed",S.classList.remove("state-off"),S.classList.add("state-on"),T.classList.add("a-show")}function V(){S.dataset.navState="hidden",S.classList.remove("state-on"),S.classList.add("state-off"),T.classList.add("a-hide"),T.classList.remove("a-show"),setTimeout(()=>{T.classList.remove("a-hide")},300)}function pe(){console.log("HUD init"),S=document.querySelector(".hud .nav-toggle"),T=document.querySelector(".hud .navigation"),M=document.querySelector(".hud .reload-screen"),S==null||S.addEventListener("click",n=>{n.currentTarget.dataset.navState!="displayed"?ze():V()}),M==null||M.addEventListener("click",n=>{Screens.reload()})}var O=class{constructor(e,t,a){this.screens={},this.routesMap={},this.currentScreen=null,this.eventsHandlers={switch:[],reload:[],reinit:[],errorScreen:[]},this.screensContainer=document.querySelector(e),this.loader=document.querySelector(t),this.errorScreen=document.querySelector(a),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]=b({route:e},t),this.routesMap[e]=t.alias}switch(e){var a;if(this.runSwitchHandlers(e),this.hideErrorScreen(),this.showLoader(),(a=this.currentScreen)==null||a.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)}reload(){this.currentScreen&&(this.runReloadHandlers(this.currentScreen.alias),this.switch(this.currentScreen.alias))}reinit(){this.currentScreen&&(this.currentScreen.initer(this),this.runReinitHandlers())}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 a;(a=this.currentScreen)==null||a.DOMObject.remove(),this.errorScreen.querySelector(".error-title").innerHTML=e,this.errorScreen.querySelector(".error-text").innerHTML=t,this.showErrorScreen(),this.runErrorScreenHandlers()}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)}onReaload(e){this.eventsHandlers.reload.push(e)}onReinit(e){this.eventsHandlers.reinit.push(e)}onErrorScreen(e){this.eventsHandlers.errorScreen.push(e)}runSwitchHandlers(e){let t=Object.keys(this.routesMap).filter(a=>this.routesMap[a]==e);for(let a of this.eventsHandlers.switch)a(this,e,t.length?t[0]:void 0)}runReloadHandlers(e){for(let t of this.eventsHandlers.reload)t(this,e)}runReinitHandlers(){for(let e of this.eventsHandlers.reinit)e(this)}runErrorScreenHandlers(){for(let e of this.eventsHandlers.errorScreen)e(this)}};var q=class{constructor(){this.data={}}set(e,t){this.data[e]=t}get(e){return this.data[e]}setRaw(e,t){this.set("raw."+e,t)}getRaw(e){return this.get("raw."+e)}invalidate(e){delete this.data[e]}invalidatePrefix(e){for(let t of Object.keys(this.data))t.startsWith(e)&&delete this.data[t]}getOrFetch(e,t,a){let s=this.data[e];if(typeof s!="undefined")return a(null,s);t((r,i)=>{!r&&typeof i!="undefined"&&(this.data[e]=i),a(r,i)})}getCollection(e){let t=[];for(let[a,s]of Object.entries(this.data))a.startsWith(e)&&t.push(s);return t}};var J="sh_fav_areas",me={getAll(){try{return JSON.parse(localStorage.getItem(J)||"[]")}catch(n){return[]}},has(n){return this.getAll().includes(String(n))},add(n){let e=this.getAll(),t=String(n);e.includes(t)||(e.push(t),localStorage.setItem(J,JSON.stringify(e)))},remove(n){let e=this.getAll().filter(t=>t!==String(n));localStorage.setItem(J,JSON.stringify(e))},toggle(n){return this.has(n)?this.remove(n):this.add(n),this.has(n)}};function Ve(n,e,t,a){return`
It's empty here yet