Компьютер давно многим заменил телевизор, а что не хватает компьютеру для комфортного просмотра кино, сериалов и прочего? Мне лично не хватало пульта дистанционного управления.
Я не много увлекаюсь веб-программированием в свободное время. Было решено написать свой велосипед и заодно совместить приятное с полезным. Была изучена возможность с помощью консоли управлять компьютером, в первую очередь меня интересовал следующий минимальный объем задач (который немного увеличился в последствии):
- Возможность отправить ПК в режим сна
- Возможность выключить громкость
С помощью известного поисковика нашел консольную программу, NirCmd, программа позволяет осуществлять достаточно много действий кроме указанных выше. С основной частью мы определились. Как я говорил, не много изучаю php, веб-сервер у меня как правило запущен постоянно, поэтому ничего специфичного в плане сервера я придумывать не стал. Сервер написан на php и состоит из двух классов: Control, который представляет собой методы которые исполняют консольные команды и Route, который делает валидацию приходящих запросов и исполняет методы класса Control.
Control выгладит вот так (код однотипный, поэтому я сократил):
class Control implements ActionControl {
protected $Path; //путь до программы nirCmd;
function __construct($p = 'C:/nircmd/nircmd.exe') {
$this->Path = $p;
}
function standby() {
`{$this->Path} standby`;
}
function hibernate() {
`rundll32 powrprof.dll,SetSuspendState 0,1,0`;
}
function reboot() {
`{$this->Path} exitwin reboot`;
}
function turnOff() {
`{$this->Path} exitwin poweroff`;
}
function logout() {
`{$this->Path} exitwin logoff`;
}
}
Второй класс, — Route. Собственно к нему обращается клиент. Класс, в первую очередь осуществляет валидацию запроса, и если запрос правильный вызывает методы класса Control.
Выглядит это так:
class Route implements ActionRoute {
protected $possible = [];
protected $ControlObj;
function __construct($obj) {
$this->possible = get_class_methods($obj);
$this->ControlObj = $obj;
}
function route($arr) {
forEach($arr as $key => $value) {
if (in_array($key, $this->possible) && $value == 'true') {
$this->execute($key);
} else
{
Message::sent('wrong method');
}
}
}
function execute($c) {
$this->ControlObj->$c();
Message::sent('executed');
}
}
Чтобы получилось в случае чего легко расширить количество методов в классе Control, валидация в классе Route жестко не привязана к определенному списку методов класса Control, точнее привязана, но все возможные методы извлекаются из самого объекта, с ними и идет сравнение пришедших данных.
Сам файл к которому обращается клиент выглядит вот так:
<?
define("PATH", "C:/nircmd/nircmd.exe");
function __autoload($name) {
require "class/$name.class.php";
}
$obj = new Control(PATH);
$route = new Route($obj);
if ($_GET) {
$route->route($_GET);
}
?>
Если приходит GET запрос, то весь массив отдаем методу route.
Клиентская часть представляет собой кнопки управления и один обработчик, по которому мы шлем данные при помощи ajax на сервер.
var wrapper = document.querySelector(".wrapper");
function getXmlHttpRequest(){
if (window.XMLHttpRequest) {
try {
return new XMLHttpRequest();
} catch (e){ }
} else if (window.ActiveXObject) {
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e){}
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch (e){}
}
return null;
}
wrapper.addEventListener('click', function(e){
var target = e.target;
if(target.tagName!= 'BUTTON') return;
if (target.getAttribute('data')=='qestion') {
var yn = confirm("Уверен?");
if (!yn) return
ajaxf(target.id);
}
ajaxf(target.id);
})
function ajaxf($com) {
var command = $com+'=true';
var xhr = new getXmlHttpRequest();
xhr.open('GET', 'remote.php?'+command, true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
console.log(xhr.status + ': ' + xhr.statusText);
} else {
console.log(xhr.responseText);
}
}
}
На сегодня реализован следующий функционал управления:
- спящий режим
- гибернация
- перезагрузится
- выключить
- выйти
- выключить монитор
- включить/отключить звук по триггеру
- громкость больше
- громкость меньше
- Медиа кнопки, stop/play next/prev
Все, что я хотел получить, получил, но, как говорится, аппетит приходит во время еды. Хотелось бы иметь обратную связь от сервера, т.е. при первом заходе на страницу делать запрос текущего уровня громкости например. К сожалению, nirCmd не возвращает значения при выполнении, поэтому с тем, что имеем сейчас, я не знаю, как это осуществить.
Вопрос безопасности. Хотя это все и крутится на локальной машине, будет очень печально, если доступ к исполнению команд nirCmd будет у злоумышленника, т.к. можно сотворить много деструктивных действий.
Готовый проект на GIthab.
Автор: Sad_Bro