From 60d39c6fbf78cbbab191b6f6963a7415983fdddb Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Sat, 2 Dec 2023 12:42:53 +0100 Subject: [PATCH] implement enhanced app for shelly plugs (#661) --- ShellyPlug/ShellyPlug.php | 175 +++++++++++++++++++++++++++++++++ ShellyPlug/app.json | 10 ++ ShellyPlug/config.blade.php | 15 +++ ShellyPlug/livestats.blade.php | 10 ++ ShellyPlug/shellyplug.png | Bin 0 -> 3901 bytes 5 files changed, 210 insertions(+) create mode 100644 ShellyPlug/ShellyPlug.php create mode 100644 ShellyPlug/app.json create mode 100644 ShellyPlug/config.blade.php create mode 100644 ShellyPlug/livestats.blade.php create mode 100644 ShellyPlug/shellyplug.png diff --git a/ShellyPlug/ShellyPlug.php b/ShellyPlug/ShellyPlug.php new file mode 100644 index 0000000000..ad2f0fe33b --- /dev/null +++ b/ShellyPlug/ShellyPlug.php @@ -0,0 +1,175 @@ + "Cloud", + "mqtt" => "MQTT", + "ws" => "Websocket", + "sysUptime" => "Uptime", + "state" => "State", + "power" => "Watts", + "tempC" => "Temperature in °C", + "tempF" => "Temperature in °F", + ]; + } + + private static function toTime($timestamp) + { + $hours = floor($timestamp / 3600); + $minutes = floor($timestamp % 3600 / 60); + $seconds = $timestamp % 60; + + $hourDuration = sprintf('%02dh', $hours); + $minDuration = sprintf('%02dm', $minutes); + $secDuration = sprintf('%02ds', $seconds); + $HourMinSec = $hourDuration.$minDuration.$secDuration; + + if($hourDuration > 0){ + $hourDuration = $hourDuration; + } else { + $hourDuration = '00h'; + } + + if($minDuration > 0){ + $minDuration = $minDuration; + } else { + $minDuration = '00m'; + } + + if($secDuration > 0){ + $secDuration = $secDuration; + } else { + $secDuration = '00s'; + } + + $HourMinSec = $hourDuration.$minDuration.$secDuration; + + return $HourMinSec; + } + + private static function formatValueUsingStat($stat, $value) + { + if (!isset($value)) { + return "N/A"; + } + + switch ($stat) { + case "cloud": + case "mqtt": + case "ws": + return (bool)$value ? "Connected" : "Disconnected"; + case "sysUptime": + return self::toTime((int)$value); + case "state": + return (bool)$value ? "On" : "Off"; + case "power": + return "{$value} W"; + case "tempC": + return "{$value} °C"; + case "tempF": + return "{$value} °F"; + default: + return "{$value}"; + } + } + + public function test() + { + $test = parent::appTest( + $this->url("Shelly.GetStatus") + ); + echo $test->status; + } + + public function livestats() + { + $status = 'inactive'; + $res = parent::execute($this->url('Shelly.GetStatus')); + $details = json_decode($res->getBody()); + $res = parent::execute($this->url('Switch.GetStatus?id=0')); + $switch = json_decode($res->getBody()); + + $data = ["visiblestats" => []]; + if ($details) { + foreach ($this->config->availablestats as $stat) { + if (!isset(self::getAvailableStats()[$stat])) { + continue; + } + + $newstat = new \stdClass(); + + switch ($stat) { + case "cloud": + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = self::formatValueUsingStat( + $stat, + $details->cloud->connected + ); + break; + case "mqtt": + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = self::formatValueUsingStat( + $stat, + $details->mqtt->connected + ); + break; + case "ws": + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = self::formatValueUsingStat( + $stat, + $details->ws->connected + ); + break; + case "sysUptime": + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = self::formatValueUsingStat( + $stat, + $details->sys->uptime + ); + break; + case "state": + $newstat->value = self::formatValueUsingStat( + $stat, + $switch->output + ); + break; + case "power": + $newstat->value = self::formatValueUsingStat( + $stat, + $switch->apower + ); + break; + case "tempC": + $newstat->value = self::formatValueUsingStat( + $stat, + $switch->temperature->tC + ); + break; + case "tempF": + $newstat->value = self::formatValueUsingStat( + $stat, + $switch->temperature->tF + ); + break; + } + $data["visiblestats"][] = $newstat; + } + } + + return parent::getLiveStats($status, $data); + } + + public function url($endpoint) + { + $api_url = parent::normaliseurl($this->config->url)."rpc/".$endpoint; + return $api_url; + } +} diff --git a/ShellyPlug/app.json b/ShellyPlug/app.json new file mode 100644 index 0000000000..80ed9017a4 --- /dev/null +++ b/ShellyPlug/app.json @@ -0,0 +1,10 @@ +{ + "appid": "304c38e1914566a45eb705ba7315a638d25b44ce", + "name": "Shelly Plug", + "website": "https://www.shelly.com/", + "license": "MIT License", + "description": "Shelly offers easy-to-use products for smart home enthusiasts and professionals. The wide product range delivers complete home automation experience and innovative technology at reasonable prices and with attention to the smallest details.\r\n\r\nThis application supports Shelly Plugs.", + "enhanced": true, + "tile_background": "dark", + "icon": "shellyplug.png" +} \ No newline at end of file diff --git a/ShellyPlug/config.blade.php b/ShellyPlug/config.blade.php new file mode 100644 index 0000000000..c058640fa0 --- /dev/null +++ b/ShellyPlug/config.blade.php @@ -0,0 +1,15 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+
+ + {!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!} +
+
+ + {!! Form::select('config[availablestats][]', App\SupportedApps\ShellyPlug\ShellyPlug::getAvailableStats(), isset($item) ? $item->getConfig()->availablestats ?? null : null, ['multiple' => 'multiple']) !!} +
+
+ +
+
+ diff --git a/ShellyPlug/livestats.blade.php b/ShellyPlug/livestats.blade.php new file mode 100644 index 0000000000..795b9403ee --- /dev/null +++ b/ShellyPlug/livestats.blade.php @@ -0,0 +1,10 @@ + diff --git a/ShellyPlug/shellyplug.png b/ShellyPlug/shellyplug.png new file mode 100644 index 0000000000000000000000000000000000000000..41e44cbbfe17ce79b322e2cf8d676cf5dd32ac10 GIT binary patch literal 3901 zcmV-D55n+?P)E#1-KILZ8 zMU0uX5@%?iz#;*R7ea$XMV-*ZT;}pYc>=-{2w##hirpMOTytg3U}Yc<_8O;^=9zxwZeZu?N8sP#@iFeo7gyi5~HlJMM~ zAn;5nzD`s85UqZm*8fs{qTbIE{B(%_SjJ~cX$hX~+2d`tisegHxX7NV`v_4Q7)Zmf z2RxVvP11%^NK`=~ErPYKAStc9~e*cvPW?da8es5&Nq(hC)7!fOPJ zP+EWKF@Z$@EJB0#T>mW_j%C5?1$b@U7%&_S7N>T&a4(slB!gZK9+@&IZcW8=1eSkp z)sGk9ZwOg&=2*(nU~yna3Ts&(CYqrQS0fBqwC?+h@N!|MKb6;D#cYT2YZ!E&1BF7U zl9j5L0j%ni13wV1iR*nSTZ0v)yUVYcP#L2bKY5kY2#Q=_)qbf3|2uXq^Sq_O3ejHA z-9?Z_6Mi6GtsHg0Dt{k>B?Mqp1fZ%4<)5y?U{w_i0Gs9*FfPG5c;znkH(h|TH0wao zU^Sisf;9qwl{8pe1CO5Hof(C1&iVLo9+*7dL=eX&rNbqtZ-II5)@9AL~Ds3$p zmf6EIfB$zzD^?XKRH+<1GVAV2tys+}R_*7D@ED$cgROcoRjh+mmC9(*3C+AR{o`jp zXOvT{E?=caX@lF-iWQ1t)t=Wlc27+8>~^FFtNZe*0fljkl&7X>u%ZMD4O@(T0GBPT zD+H*p``}yROoLe;MBo;)WLiGxs^Xp zy`a9MaO7D>7{x-Uh#ybsAf4}^esSm@za)iFtXimQ?{ckJO)HkwaC}sj|AnAfDT1q# z{PzubGHSXbLRYR`5x}aIYBO_P8!FZ=!$SPo2}oFJW+48#?x0rb0Je1HTzFKh&W~4e za3^Hl4eoN)6bs&Ws9#yjuCGF+Pu^^ERJn&jQ>@njD&6|?l*n6c^U3kHlF9|KDfh9Gff!S2~O`8ELPW9|AaS2w?3Y6UijssM&2kIy1ZG#dz z8hC@S%NP}SoJ++5wMr|{3d9N+iyf*t`-9-7jmjoiEdOd&u~e9^K5_H(UMFU#q8_+s zqE<}>@1RyDA@x~bBvq^Nn4!v&=m+kZs8v&7QP&+M@>{+bvsy*${+O!OwqSL9^qmT7 zg}tGLI1hJX&dvmz7f7jA%_x=%YGoJC?2Ws7Asy{~!d9)e1WOsU3VE(~lh!V?T5YFT zDyS9OGJ|7A*&oPUtu_S<)XGpqt_3B68Wr6G!+eZ zSgwtfZCRPQF2wpHUHqN^C zfMtxCmrk*Qz+KN~V5nL*jv0?l3$LkkD?hD&hUt*mXP197~~M z$&v>?ozQNu&a#Y;FGZ|HDM=z&?V2ZU+~I^I<=CzjtI-`^|DIXQ3GUD)Y^Lyp9>8m` z_;ZH^DZrvSc<0R>$`Hh-6Ply-O=WP0CNp=~pOT_aOM}IyY85nClEzt}TI~9?`coLd z+Zrq##77ltX|NybkQ9380z%L)QU>zhCS!6p)0@AQ4Mg;rnP z(s2f65dm{*?1Ec&1R~Yq;$PaOhN;0?*=ZvKs+9!QVx`&(LW1?pU)#qO0t(d%w;5@5 zd^E#QwM2|%O9=&`%I@S}nRPq#c~fab^Cr$N6@coQx}T(*r{8JDOx6lO#9Eu+Bou%O zp;#o3H$(%i!ylN%Ih4>P!HRuZTE#kg^&YMi8Fj-O^TUOhtj6zzS}EEchnk(!QDPBZ$IU$ik(tggvBxS=+JkP4drNjTv&+5CFfrHW)=tn!tXRm&9Mo_8j@ zpR{8GtMl?Iw#;y!zFxS?Od{qY4wE5Rq718fZaZ?Yx-P9^GZ};E3=w}t5X&`~%S4PG z_@zj}>b$gKKwL6P%Y?X#hXkfke@ydA>lxkq+2kj0ww}nu_0;n*SQe_WmR=waNBp?o5u|`#CF|q1- z7odEL<$ZL*1WWI67`^eHyrpiNyvGKs^Wq8)%^2DZMrWsQ{Ukz0pkkd(Hf`vv2>2M{13$A0B zMP4j`DlKws6$w~hzPAruM~C3b<+;C`rx-vr?{$VQa$Y{$s#WMhKl!D4CA@ipKhvrVp(F} zb!j#0QZkCuYw&<|%ff3Y@QEyl%`hZ}^EdxCX13JM?s4 zi#-Hvt$XrLd0NN;>gRMYLI$gnTKuy=zQo1Q(}_7dOKTVO@(q^f0`rHSc7y_}(GFg@ z8>A%y_u!`~Do=7tCW34<8-NAyP$wv#rJyiMpTG4xt{M&}eAkBydNYZd>);%$K7gv_ z8&cT(Sfbg9PakHXqADsS2w39))yPh+cbBg6$bm9lzbD* z`s5+TilabMRzSbtcrNhpGmZv}6FWZpBgTQa*I`!FYXDX4D;$2tew^>nV1)-%CIHG5 z{8EMis>g2hXs{l`-kkl>2$X4v$gyZQ4^ZX9_#zEfi#9Ox0}jFX>g2lVb)9EoLlxSj zM%~@egdLmt9*0~^QXVPTpjp4w@RUo-J^!@Y2gg@;LkXbTA7+_p#oEC7uYYfNslut( zIcpNRb5mfqLPywN8f9`@6YfIn479&|_Z4m@{$-{g;8>m4)1Sbs$l|67-x*YdbQF#} z>j(o@aMIo!qpnM<=nilj3$zkaX=LtSZH6p z^GcrjD|j4Tzwug-TchJ@&+dF&$BZ>U*6nv*v53FGqNJ-k)*yX9KxM^wP-Y#ZU6Xe% zz`@V!7%S2k3s)YEP4(=K-Agd-c|SOLC*%4vDw7(l@W8@w13o_tKrx$&%`{cy7iV9_ z3vVOxr$LI-yfS486?4U^wQf)^xXUW)r4_4X#d6@s@bnwcadDG!lic=pRlUZw=(KED zW^KGc#g$^!#a>tL8t{}`w|Tf$tj1Homfpy8=6UW~*<4SXc|CU*!SWU&Xt3&zfa9!V z0F=dR-3c)ts|;AepLw7%SP0VCHI9K23aqjn&aYLLQYi+O1^+7qP(p%LwnOy+||X{cz!4 zGT|?B0Lc7pLWBVeV1mElMn}b&V<{;H>7`suarEjvbdM%fUauQuu;C0W8;)fKyJlH8 z1`O$Dd?>L>r)TOuf`!>@aBe#;kVpqplj8u(38rN)!J~v}m1YMFTlO+ncKKKD^aCT< z85Wmwp~AH%upvOb1t94b!Sd$w6aRrzshwb&wLN>h%~r8||1ZD*Exea+K7EO700000 LNkvXXu0mjf!0cNN literal 0 HcmV?d00001