Mikrotik CAPs 2 провайдера Виджет Таблица устройств
Добавлено: Пн июн 15, 2020 12:01 pm
Предлагаю свой вариант работы с сетью микротиков. Всё это очень уникально, так что просто скопировать не получится. Берите куски кода, или разбирайтесь что к чему. Постараюсь снабдить комментариями. Советую блок кода двух провайдеров и настройку микротика оставьте на потом. Этот момент очень сильно зависит от вашей текущей настройки микротика.
Моя сеть
Несколько микротиков в режиме Caps-Man. Все обращения будут к главному.
Если у вас только один микротик и Caps-Man не используется, то в сценарии можно указать другую таблицу с WiFi устройствами роутера, закомментировать блок кода получения CAPs точек, и не добавлять класс WiFiCaps
2 провайдера Ростелеком и Билайн. Оба провайдера активные. Основной BL, если недоступен, то Интернет будет через РТ.
Если у вас только один провайдер, закомментируйте блок кода в сценарии. Связи с классами по этому моменту никакой нет.
Классы
Во вложении классы WiFiDevice и WiFiCaps. Код в общих методах классов только сообщает о приходе и уходе устройств из сети. Конкретные действия прописываются в личном коде устройства. Пример будет ниже.
Сценарий опроса Микротика
getWiFiclients это главный сценарий который периодически опрашивает Микротик, записывает данные в объекты классов WiFiDevice и WiFiCaps, и вызывает их методы. getWiFiclients вызывает сам себя каждые 10 секунд по средствам функции setTimeOut. Но его нужно запустить, и поддерживать в работе. Добавьте в метод onNewMinute класса Timer код
Альтернативный вариант с использованием цикла
Во вложении есть файл cycle_microtik. Распакуйте его и положите в папку scripts где лежат все файлы циклов. При этом уберите все точки запуска сценария getWiFiclients, например в метод onNewMinute класса Timer. Сам сценарий getWiFiclients тоже при этом не нужен. Весь его код находится в файле cycle_microtik. Исправьте id и имена интерфейсов на свои. Запустите цикл cycle_microtik из X-Ray -> Services, или перезапустите все циклы.
В данном случае плюсы цикла перед сценарием:
- логинимся в микротике только один раз, а не каждый запуск сценария;
- код выполняется в отдельном процессе и не мешает системе;
- при неудачном обращении к микротику, следующая попытка идет с задержкой.
- время чтения можно увеличить. Мне хватает каждые 10 секунд, но легко можно и чаще;
- не используем таймеры для частого запуска сценария;
- меньше действий в системе, и меньше кода в разных модулях.
В объекте ThisComputer должны быть свойства Mikrotik_login, Mikrotik_password; и будут свойства isp1, isp2, isp1png, isp2png
в isp-ы будет записан статус провайдеров. 0-не активен 1-активен 2-есть интернет 3-выбран основным
в isp_png - пинги до микротиковского облака. Они выводятся на виджете.
Для получения этих данных, в сценарии нужно указать имена интерфейсов провайдеров и id их маршрутов в таблице routes.
Внимание на имя класса routeros_api. Ранее у меня было routerosAPI. файл скачивал от сюда https://yadi.sk/d/W3PkbOjY3OXzzQ
Положил его в .../Lib/ Чтобы класс роутера был доступен везде, и не делать require перед обращением к классу.
Отдельно стоит упомянуть про свойства объектов класса WiFiDevice:
notify - При каких событиях уведомлять
0-нет, 1-днем, 2-всегда, иначе не писать в чате. 1 цифра пришел, 2 ушел, 3 смена точки.
например 210 будет означать, что говорить всегда при появлении девайса в сети, говорить только днем при уходе, и вообще не говорить при смене точки. Только днем или всегда зависит от приоритетов функции say() вашей системы.
holdCycles - Количество циклов удержания и
online - в сети, где 0 - не в сети, а любая цифра - в сети.
Некоторые устройства периодически выключают свой wifi модуль для экономии энергии. В таких случаях следует установить holdCycles побольше. Каждое выполнение сценария getWiFiclients будет понижать свойство online на единицу, пока оно не станет =0. При появлении устройства online будет установлено = holdCycles. Так что при больших значениях holdCycles устройство ещё "будет в сети" какое то время.
Обнаруженные WiFi устройства будут добавлены в класс WiFiDevice
Точки Caps в класс WiFiCaps
Так же сценарий вызовет соответствующие методы классов при появлении и ухода устройств из сети.
Для работы сценария понадобится дополнительный сценарий переопределения имени точки на дружественное. Исправьте имена точек на свои. Задача CAPsFriendlyName только в назначении дружественного имени точке. Причем в двух падежах. CAPsFriendlyName работает как функция. Мы ей имя точки как в микротике, а она нам массив дружественных имен из двух падежей.
Домашняя страница с таблицей устройств
Нам будет нужно посмотреть, какие устройства сейчас в сети, и какие были совсем недавно. Для этого создадим домашнюю страницу с типом Ссылка, которая будет запускать скрипт для составления таблицы.
В настройках страницы:
Ссылка: http://localhost/objects/?script=wifiTable&print=1
исправьте адрес сервера на свой
Красивый элемент на сценене забудьте исправить element_164 на свой!
Все элементы выполнены на svg, можете раздербанить на запчасти. В коде всё сделано группами
Цвет провайдеров будет меняться в зависимости от значений свойств isp1 и isp2.
На линиях будут крестики при неактивном провайдере и отсутствии Интернета за ним.
К примеру у меня сейчас активны оба провайдера, у обоих есть Интернет, и второй выбран основным.
При нажатии на провайдеров вызывается скрипт runScript("isp1select"); и runScript("isp2select");
Скрипт меняет дистанцию провайдера. Так можно быстро выбрать основным провайдером другого, если на то есть необходимость.
У меня у isp2 дистанция всегда 20. У isp2 cкриптами isp1select меняю на 10, isp2select на 30, тем самым маршрут isp1 встает до или после isp2
Поменяйте *1D на id своего маршрута. Во втором скрипте так же. Дистанцию тоже укажите согласно вашей настройке.
Пример настройки методов объекта телефона
Где ещё можно использовать
marker - свойство класса WiFiDevice. Поставьте в нем флаг "f" для устройств близких друзей.
Заполните свойства
owner - владелец. Например просто Имя или Имя Фамилия
deviceName - имя устройства. Например Телефон брата или Терминал на кухне.
Добавьте в метод activate объекта GuestsMode класс OperationalModes код
И получите уведомление кто к вам пришел при активации режима "У нас гости". По аналогии можно добавить любые свои флаги.
Создайте сценарий friendshere
И запускайте его, например из методов Found и Lost телефонов своих друзей. Вы получите активацию и деактивацию режима "У нас гости". Лично я пока что не знаю как по другому определять этот режим, кроме как по мобильным телефонам.
Моя сеть
Несколько микротиков в режиме Caps-Man. Все обращения будут к главному.
Если у вас только один микротик и Caps-Man не используется, то в сценарии можно указать другую таблицу с WiFi устройствами роутера, закомментировать блок кода получения CAPs точек, и не добавлять класс WiFiCaps
2 провайдера Ростелеком и Билайн. Оба провайдера активные. Основной BL, если недоступен, то Интернет будет через РТ.
Если у вас только один провайдер, закомментируйте блок кода в сценарии. Связи с классами по этому моменту никакой нет.
Классы
Во вложении классы WiFiDevice и WiFiCaps. Код в общих методах классов только сообщает о приходе и уходе устройств из сети. Конкретные действия прописываются в личном коде устройства. Пример будет ниже.
Сценарий опроса Микротика
getWiFiclients это главный сценарий который периодически опрашивает Микротик, записывает данные в объекты классов WiFiDevice и WiFiCaps, и вызывает их методы. getWiFiclients вызывает сам себя каждые 10 секунд по средствам функции setTimeOut. Но его нужно запустить, и поддерживать в работе. Добавьте в метод onNewMinute класса Timer код
Код: Выделить всё
setTimeOut('getWiFiclientsTimer',"runScript('getWiFiclients');",10);сценарий getWiFiclients для опроса микротикаSPOILER_SHOW
Код: Выделить всё
$ip = gg('Mikrotik_IP');
$login = gg('Mikrotik_login');
$password = gg('Mikrotik_password');
// ВНИМАНИЕ на имя класса в вашем файле routeros_api.class.php (в самом начале)
//$API = new routerosAPI();
$API = new routeros_api();
if ($API->connect($ip, $login, $password)) {
/* ******************************************************************** */
// Получить все объекты класса WiFiDevice
$objects = getObjectsByClass('WiFiDevice');
// При работе в режиме CAPsMAN все клиенты находятся в таблице /caps-man/registration-table/print
$ARRAY = $API->comm('/caps-man/registration-table/print');
// перебор объектов класса WiFiDevice
foreach ($objects as $obj) {
$obj=getObject($obj['TITLE']);
$f=0;
// перебор массива роутера
foreach ($ARRAY as $i => $arr) {
if ($obj->getProperty('MAC') == $arr['mac-address']) {
//$obj->setProperty('lastActive' , date("Y-m-d H:i:s",time()));
// Вызов метода CAPchange, если предыдущая точка была другая, не было вовсе,
// и устройство было online.
// Этот метод будет вызван первым. Ранее Found и Located
if ($obj->getProperty('CAPname') != $arr['interface']) {
// ToThink Имя старой точки можно передавать параметром через метод CAPchange.
// Тогда в методе будет возможность увидеть имя старой и новой точки.
// может быть полезным при реализации условия перехода именно с конкретной точки на нужную
$oldCAPName = $obj->getProperty('CAPname');
$obj->setProperty('CAPname', $arr['interface']);
if ($obj->getProperty('online')) {
$obj->callMethod('CAPchange', array('oldCAPName' => $oldCAPName)); //ToTest
}
unset($oldCapName);
}
// Вызов метода Found если устройство не было onLine
// В данный момент точка доступа в объекте уже записана новая
if (!$obj->getProperty('online')) {
$j=$obj->getProperty('holdCycles');
if (!$j) { $j=1; }
$obj->setProperty('online', $j);
unset($j);
$obj->callMethod('Found');
}
// Вызов метода Located каждый раз при опросе и присутствии девайса в сети
// будет использоваться например для продления активности в комнате
// в данный момент в объекте записана новая точка доступа
$obj->callMethod('Located');
$obj->setProperty('lastActive' , date("Y-m-d H:i:s",time()));
// удаляем элемент массива
unset($ARRAY[$i]);
$f=1;
// break;
}
}
// если MAC этого объекта в массиве роутера нет
// Уменьшить свойство online. Когда достигнет нуля, вызвать метод Lost
if (!$f) {
$j=$obj->getProperty('online');
if ($j) {
$j=$j-1;
$obj->setProperty('online', $j);
// Вызов метода Lost
if (!$j) { $obj->callMethod('Lost'); }
}
unset($j);
}
}
// перебор оставшихся MAC адресов массива роутера
foreach ($ARRAY as $arr) {
// Создание нового объекта
say('Зарегистрировано новое ВайФай устройство.');
addClassObject('WiFiDevice', 'wifi_'.$arr['mac-address']);
$obj=getObject('wifi_'.$arr['mac-address']);
$obj->setProperty('MAC', $arr['mac-address']);
$obj->setProperty('CAPname', $arr['interface']);
//$obj->setProperty('name', 'Неизвестный девайс');
$obj->setProperty('lastActive' , date("Y-m-d H:i:s",time()));
$obj->setProperty('notify' , '20');
$obj->setProperty('online', '1');
$obj->setProperty('holdCycles', '2');
$obj->callMethod('Found');
}
/* ******************************************************************** */
// Получить все объекты класса WiFi точек доступа
$objects = getObjectsByClass('WiFiCaps');
// таблица роутера с точками доступа
$ARRAY = $API->comm('/caps-man/interface/print');
// перебор объектов класса WiFiCaps
foreach ($objects as $obj) {
$obj=getObject($obj['TITLE']);
// перебор массива роутера
foreach ($ARRAY as $i => $arr) {
if ($obj->object_title == $arr['name']) {
if ($obj->getProperty('MAC') != $arr['mac-address']) {
$obj->setProperty('MAC', $arr['mac-address']);
}
// Основной показатель работоспособности точки
// свидетельствует о наличии линка у точки с главным
if ($obj->getProperty('bound') != $arr['bound']) {
$obj->setProperty('bound', $arr['bound']);
// Вызов метода
if ($arr['bound']=='true') {
$obj->callMethod('Found');
} else {
$obj->callMethod('Lost');
}
}
if ($obj->getProperty('inactive') != $arr['inactive']) {
$obj->setProperty('inactive', $arr['inactive']);
//$obj->callMethod('');
}
if ($obj->getProperty('running') != $arr['running']) {
$obj->setProperty('running', $arr['running']);
//$obj->callMethod('');
}
if ($obj->getProperty('disabled') != $arr['disabled']) {
$obj->setProperty('disabled', $arr['disabled']);
//$obj->callMethod('');
}
// удаляем элемент массива
unset($ARRAY[$i]);
} // имена равны
} // массив роутера
} // объекты класса
// перебор оставшихся элементов массива роутера
foreach ($ARRAY as $arr) {
// Создание нового объекта
say('Зарегистрирована новая точка доступа '.$arr['name']);
addClassObject('WiFiCaps', $arr['name']);
$obj=getObject($arr['name']);
$obj->setProperty('MAC', $arr['mac-address']);
$obj->setProperty('bound', $arr['bound']);
$obj->setProperty('inactive', $arr['inactive']);
$obj->setProperty('running', $arr['running']);
$obj->setProperty('disabled', $arr['disabled']);
// $obj->callMethod('Found');
}
/* ******************************************************************** */
// Контроль состояния провайдеров
// 0-нет линка 1-линк с провайдером 2-есть Интернет 3-выбран основным
$isp1=0; $isp2=0;
// пинг до микротиковского облака
$isp1png=''; $isp2png='';
/* Узнаем о наличии линка и Интернета за провайдером с помощью Detect Internet
ВНИМАНИЕ замените имена интерфейсов на свои
нужно активировать Detect Internet Для интерфейсов провайдеров.
Я сделал лист wan, и добавил в него провайдеров. Detect Internet смотрит на лист wan
*/
$ARRAY = $API->comm('/interface/detect-internet/state/print');
// перебор массива роутера
foreach ($ARRAY as $i => $arr) {
switch ($arr['state']) {
// Интернет за провайдером
case 'internet':
if ($arr['name']=='pppoe-out1') { $isp1=2; $isp1png=$arr['cloud-rtt']; }
if ($arr['name']=='ether9') { $isp2=2; $isp2png=$arr['cloud-rtt']; }
break;
// провайдер доступен
case 'wan':
case 'lan':
if ($arr['name']=='pppoe-out1') { $isp1=1; }
if ($arr['name']=='ether9') { $isp2=1; }
break;
// нет линка с провайдером
case 'no-link':
default:
if ($arr['name']=='pppoe-out1') { $isp1=0; }
if ($arr['name']=='ether9') { $isp2=0; }
}}
// пинги до м.облока
sg('isp1ping',$isp1png);
sg('isp2ping',$isp2png);
// таблица роутера ip route print detail
$ARRAY = $API->comm('/ip/route/print');
/* перебор массива роутера в поисках основного провайдера
ВНИМАНИЕ замените id *1D и *1E на свои
Независимо от способа переключения провайдеров,
у вас в итоге должны быть два маршрута на разных провайдеров.
Один в данный момент активный, второй нет
при желании id записей можно заменить на уникальные комментарии
*/
foreach ($ARRAY as $i => $arr) {
if ($arr['active']=='true') {
switch ($arr['.id']) {
case '*1D':
$isp1=max($isp1,3);
break;
case '*1E':
$isp2=max($isp2,3);
break;
}}}
$pisp=gg('isp1');
$isp=$isp1;
$t='';
if ($pisp<>$isp) {
$p='Ростелеком';
if ($isp==0) {$t="Потеря соединения c провайдером $p";}
elseif ($isp==1 && $pisp==0) {$t="Установлено соединения c провайдером $p";}
elseif ($isp==1 && $pisp>=2) {$t="Провайдер $p не предоставляет Интернет";}
elseif ($isp==2 && $pisp<=1) {$t="Провайдер $p, Интернет предоставлен";}
elseif ($isp==3) {$t="Провайдер $p выбран основным";}
sg('isp1',$isp1);
if ($t) { say($t); }
}
$pisp=gg('isp2');
$isp=$isp2;
$t='';
if ($pisp<>$isp) {
$p='Бинайн';
if ($isp==0) {$t="Потеря соединения c провайдером $p";}
elseif ($isp==1 && $pisp==0) {$t="Установлено соединения c провайдером $p";}
elseif ($isp==1 && $pisp>=2) {$t="Провайдер $p не предоставляет Интернет";}
elseif ($isp==2 && $pisp<=1) {$t="Провайдер $p, Интернет предоставлен";}
elseif ($isp==3) {$t="Провайдер $p выбран основным";}
sg('isp2',$isp2);
if ($t) { say($t); }
}
/* ******************************************************************** */
$API->disconnect();
echo 'getWiFiclients ok';
setTimeOut('getWiFiclientsTimer',"runScript('getWiFiclients');",10);
} else { echo 'error'; }Альтернативный вариант с использованием цикла
Во вложении есть файл cycle_microtik. Распакуйте его и положите в папку scripts где лежат все файлы циклов. При этом уберите все точки запуска сценария getWiFiclients, например в метод onNewMinute класса Timer. Сам сценарий getWiFiclients тоже при этом не нужен. Весь его код находится в файле cycle_microtik. Исправьте id и имена интерфейсов на свои. Запустите цикл cycle_microtik из X-Ray -> Services, или перезапустите все циклы.
В данном случае плюсы цикла перед сценарием:
- логинимся в микротике только один раз, а не каждый запуск сценария;
- код выполняется в отдельном процессе и не мешает системе;
- при неудачном обращении к микротику, следующая попытка идет с задержкой.
- время чтения можно увеличить. Мне хватает каждые 10 секунд, но легко можно и чаще;
- не используем таймеры для частого запуска сценария;
- меньше действий в системе, и меньше кода в разных модулях.
В объекте ThisComputer должны быть свойства Mikrotik_login, Mikrotik_password; и будут свойства isp1, isp2, isp1png, isp2png
в isp-ы будет записан статус провайдеров. 0-не активен 1-активен 2-есть интернет 3-выбран основным
в isp_png - пинги до микротиковского облака. Они выводятся на виджете.
Для получения этих данных, в сценарии нужно указать имена интерфейсов провайдеров и id их маршрутов в таблице routes.
Внимание на имя класса routeros_api. Ранее у меня было routerosAPI. файл скачивал от сюда https://yadi.sk/d/W3PkbOjY3OXzzQ
Положил его в .../Lib/ Чтобы класс роутера был доступен везде, и не делать require перед обращением к классу.
Отдельно стоит упомянуть про свойства объектов класса WiFiDevice:
notify - При каких событиях уведомлять
0-нет, 1-днем, 2-всегда, иначе не писать в чате. 1 цифра пришел, 2 ушел, 3 смена точки.
например 210 будет означать, что говорить всегда при появлении девайса в сети, говорить только днем при уходе, и вообще не говорить при смене точки. Только днем или всегда зависит от приоритетов функции say() вашей системы.
holdCycles - Количество циклов удержания и
online - в сети, где 0 - не в сети, а любая цифра - в сети.
Некоторые устройства периодически выключают свой wifi модуль для экономии энергии. В таких случаях следует установить holdCycles побольше. Каждое выполнение сценария getWiFiclients будет понижать свойство online на единицу, пока оно не станет =0. При появлении устройства online будет установлено = holdCycles. Так что при больших значениях holdCycles устройство ещё "будет в сети" какое то время.
Обнаруженные WiFi устройства будут добавлены в класс WiFiDevice
Точки Caps в класс WiFiCaps
Так же сценарий вызовет соответствующие методы классов при появлении и ухода устройств из сети.
Для работы сценария понадобится дополнительный сценарий переопределения имени точки на дружественное. Исправьте имена точек на свои. Задача CAPsFriendlyName только в назначении дружественного имени точке. Причем в двух падежах. CAPsFriendlyName работает как функция. Мы ей имя точки как в микротике, а она нам массив дружественных имен из двух падежей.
CAPsFriendlyNameSPOILER_SHOW
Код: Выделить всё
/* Возвращает дружественное имя точек доступа WiFi */
$s=$params['name'];
if (isset($s)) {
switch($s) {
case 'CAP--Bagir-1': $a[0]='Кабинет'; $a[1]='в кабинете'; break;
case 'CAP--BagirS-1': $a[0]='Крыльцо'; $a[1]='на крыльце'; break;
case 'CAP--SXT-1': $a[0]='Двор'; $a[1]='во дворе'; break;
case 'CAP--hAPmini-1': $a[0]='Кухня'; $a[1]='на кухне'; break;
case 'CAP--alikey-1': $a[0]='Громоотвод'; $a[1]='на громоотводе'; break;
default: $a[0]=$s; $a[1]=$s;
}
} else { $a[0]='Сетевое имя точки не передано'; $a[1]=$a[0];}
return $a;Домашняя страница с таблицей устройств
Нам будет нужно посмотреть, какие устройства сейчас в сети, и какие были совсем недавно. Для этого создадим домашнюю страницу с типом Ссылка, которая будет запускать скрипт для составления таблицы.
В настройках страницы:
Ссылка: http://localhost/objects/?script=wifiTable&print=1
исправьте адрес сервера на свой
Сценарий wifiTableSPOILER_SHOW
Код: Выделить всё
$now = new DateTime(); // текущее время на сервере
$date1 = $now->modify('-1 hour'); // старше часа будут серые
$f=0; // флаг наличия первых данных для начала таблицы
// >WiFi устройства onLine
$objects=getObjectsByClass('WiFiDevice');
foreach($objects as $obj) {
$obj=getObject($obj['TITLE']);
$ol = $obj->getProperty('online');
// Дата последней активности
$dateol = DateTime::createFromFormat("Y-m-d H:i:s", $obj->getProperty('lastActive')); // дату в дату
if ($ol || $date1 < $dateol ) {
// При первом найденном открываем раздел и таблицу
if (!$f) {
// Имя таблицы
echo '<br /><details open><summary><big><b style="color:#ff0000">WiFi устройства onLine</b></big></summary> <blockquote>';
echo '<table border="1">';
// Заголовок таблицы
echo '<tr>';
echo '<th>MAC</th>';
echo '<th>Устройство</th>';
echo '<th>Владелец</th>';
echo '<th>CAPs name</th>';
echo '<th>Имя точки</th>';
echo '</tr>';
$f=1;
}
$no=$obj->getProperty('CAPname');
// переопределить имя точки. [0] имя [1] где
$nf=runscript('CAPsFriendlyName',array('name'=>$no))[0];
// Разный цвет текста
if (!$ol) { $cn='<font color="gray">'; $ce='</font>'; } else { $cn=''; $ce=''; }
// Напечатать имена и описание объектов
echo '<tr>';
echo '<td>'.$cn.$obj->getProperty('MAC').$ce.'</td>';
echo '<td>'.$cn.$obj->getProperty('deviceName').'</td>'.$ce;
echo '<td>'.$cn.$obj->getProperty('owner').$ce.'</td>';
echo '<td>'.$cn.$no.$ce.'</td>';
echo '<td>'.$cn.$nf.$ce.'</td>';
echo '</tr>';
}
}
// Закрываем таблицу
if ( $f ) { echo '</table></blockquote></details>'; }
$f=0;
// >WiFi точки доступа
$objects=getObjectsByClass('WiFiCaps');
foreach($objects as $obj) {
$obj=getObject($obj['TITLE']);
// При первом найденном открываем раздел и таблицу
if (!$f) {
// Имя таблицы
echo '<br /><details open><summary><big><b style="color:#ff0000">WiFi точки доступа</b></big></summary> <blockquote>';
echo '<table border="1">';
// Заголовок таблицы
echo '<tr>';
echo '<th>Имя точки</th>';
echo '<th>MAC</th>';
echo '<th><p title="bound">Связь</p></th>';
echo '<th><p title="inactive">Отключена</p></th>';
echo '<th><p title="running">Клиенты</p></th>';
echo '<th><p title="disabled">Запрещена</p></th>';
echo '</tr>';
$f=1;
}
// Напечатать имена и описание объектов
echo '<tr>';
echo '<td>'.$obj->object_title.'</td>';
echo '<td>'.$obj->getProperty('MAC').'</td>';
if ($obj->getProperty('bound') == 'true' ) {$t='•';} else {$t=' ';} echo '<td align="center" style="color:#008000"><big>'.$t.'</big></td>';
if ($obj->getProperty('inactive') == 'true' ) {$t='•';} else {$t=' ';} echo '<td align="center" style="color:#ff0000"><big>'.$t.'</big></td>';
if ($obj->getProperty('running') == 'true' ) {$t='•';} else {$t=' ';} echo '<td align="center" style="color:#000080"><big>'.$t.'</big></td>';
if ($obj->getProperty('disabled') == 'true' ) {$t='•';} else {$t=' ';} echo '<td align="center" style="color:#ff0000"><big>'.$t.'</big></td>';
echo '</tr>';
}
// Закрываем таблицу
if ( $f) { echo '</table></blockquote></details>'; }
$f=0;
Красивый элемент на сцене
Дополнительный код CSSSPOILER_SHOW
Код: Выделить всё
div.element_164 {
border-radius:10px;
padding:5px;
width:120px;
height:120px;
background: linear-gradient(135deg, rgba(100,160,220,1) 1%,rgba(170,230,280,1) 100%);
}html код элементаSPOILER_SHOW
Код: Выделить всё
<svg width="100%" height="100%">
<!-- Облако -->
<g transform="translate(27.5, 0)" >
<g transform="scale(1.2) translate(-3, -3)" >
<path style="fill:linen;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;" d="M47.2,18.6c0-2-1.4-3.7-3.3-4.1c0-0.2,0.1-0.4,0.1-0.6c0-2.3-1.9-4.2-4.2-4.2 c-0.9,0-1.7,0.3-2.4,0.7c0-0.1,0-0.2,0-0.3c0-2.3-1.9-4.2-4.2-4.2c-1.2,0-2.2,0.5-3,1.2c-0.5-1.8-2.1-3.1-4.1-3.1 s-3.6,1.3-4.1,3.1c-0.8-0.8-1.8-1.2-3-1.2c-2.3,0-4.2,1.9-4.2,4.2c0,0.1,0,0.2,0,0.3c-0.7-0.5-1.5-0.7-2.4-0.7 c-2.3,0-4.2,1.9-4.2,4.2c0,0.2,0,0.4,0.1,0.6C6.4,14.9,5,16.6,5,18.6c0,2.1,1.5,3.8,3.6,4.2c-0.2,0.5-0.3,1-0.3,1.5 c0,2.3,1.9,4.2,4.2,4.2c0.9,0,1.7-0.3,2.4-0.7c0,0.1,0,0.2,0,0.3c0,2.3,1.9,4.2,4.2,4.2c1.3,0,2.4-0.6,3.2-1.5 c0.7,1.4,2.1,2.5,3.8,2.5c1.7,0,3.2-1,3.8-2.5c0.8,0.9,1.9,1.5,3.2,1.5c2.3,0,4.2-1.9,4.2-4.2c0-0.1,0-0.2,0-0.3 c0.7,0.5,1.5,0.7,2.4,0.7c2.3,0,4.2-1.9,4.2-4.2c0-0.5-0.1-1-0.3-1.5C45.7,22.5,47.2,20.7,47.2,18.6z"/>
<path fill="#FFD0E0" d="M44.9,17c0.2,0.5,0.3,1,0.3,1.6c0,2.5-2,4.5-4.5,4.5c-0.9,0-1.8-0.3-2.5-0.8 c0,0.1,0,0.2,0,0.3c0,2.5-2,4.5-4.5,4.5c-1.4,0-2.6-0.6-3.4-1.6c-0.7,1.5-2.3,2.6-4.1,2.6S22.7,27,22,25.4c-0.8,1-2,1.6-3.4,1.6 c-2.5,0-4.5-2-4.5-4.5c0-0.1,0-0.2,0-0.3c-0.7,0.5-1.6,0.8-2.5,0.8c-2.5,0-4.5-2-4.5-4.5c0-0.6,0.1-1.1,0.3-1.6 c-0.6-0.1-1.2-0.3-1.7-0.7C5.2,17,5,17.8,5,18.6c0,2.1,1.5,3.8,3.6,4.2c-0.2,0.5-0.3,1-0.3,1.5c0,2.3,1.9,4.2,4.2,4.2 c0.9,0,1.7-0.3,2.4-0.7c0,0.1,0,0.2,0,0.3c0,2.3,1.9,4.2,4.2,4.2c1.3,0,2.4-0.6,3.2-1.5c0.7,1.4,2.1,2.5,3.8,2.5 c1.7,0,3.2-1,3.8-2.5c0.8,0.9,1.9,1.5,3.2,1.5c2.3,0,4.2-1.9,4.2-4.2c0-0.1,0-0.2,0-0.3c0.7,0.5,1.5,0.7,2.4,0.7 c2.3,0,4.2-1.9,4.2-4.2c0-0.5-0.1-1-0.3-1.5c2-0.3,3.6-2.1,3.6-4.2c0-0.8-0.3-1.6-0.7-2.3C46,16.6,45.5,16.9,44.9,17z"/>
</g>
<text x="10" y="22" fill="black" stroke="none" font-size="10px">Internet</text>
</g>
<!-- Провайдер RT -->
<g transform="translate(0, 51.3)" onClick='runScript("isp1select");'>
<g transform="scale(0.6) translate(-291 -732) matrix(.10273 0 0 .10273 282.24 709.45)">
<g stroke="none">
<path fill='%ThisComputer.isp1|"rgb(50%, 50%, 50%);rgb(80%, 80%, 0%);rgb(0%, 50%, 0%);rgb(27%, 31%, 70%)"%' d="m95.37 339.97 241.06 113.64 245.89-115.92-241.06-113.64z"/>
<path fill='%ThisComputer.isp1|"rgb(60%, 60%, 60%);rgb(90%, 90%, 10%);rgb(10%, 60%, 10%);rgb(36%, 40%, 80%)"%' d="m336.43 453.61v141.21l245.89-115.92v-141.21z"/>
<path fill='%ThisComputer.isp1|"rgb(70%, 70%, 70%);rgb(100%, 100%, 20%);rgb(20%, 70%, 20%);rgb(48%, 48%, 90%)"%' d="m95.37 339.97 241.06 113.64v141.21l-241.06-113.65z"/>
</g>
<g fill="#f0f0f0">
<path d="m302.68 264.66 38.514 5.6218 38.514 5.6218-16.367 5.3307 40.055 21.107-26.52 10.44-44.055-21.607-16.367 5.3308-6.8691-15.929z"/>
<path d="m473.91 375.32-6.8691-15.929-6.8691-15.929-16.367 5.3308-46.555-22.107-26.02 9.9405 42.055 22.107-16.367 5.3307 38.514 5.6218z"/>
<path d="m218.1 303.03 38.514 5.6218 38.514 5.6218-16.367 5.3307 41.555 21.107-25.52 10.94-46.555-22.107-16.367 5.3308-6.8691-15.929z"/>
<path d="m391.74 411.85-6.8691-15.929-6.8691-15.929-16.367 5.3308-46.555-22.107-26.52 9.9405 42.555 22.107-16.367 5.3307 38.514 5.6218z"/>
</g>
</g>
<text x="0" y="0" fill="black" stroke="none" font-size="10px">RT</text>
<text x="0" y="-10" fill="black" stroke="none" font-size="10px">%ThisComputer.isp1ping%</text>
</g>
<!-- Провайдер BL -->
<g transform="translate(79, 51.3)" onClick='runScript("isp2select");'>
<g transform="scale(0.6) translate(-291 -732) matrix(.10273 0 0 .10273 282.24 709.45)">
<g stroke="none"> <!-- gray;yellow;green;blue -->
<path fill='%ThisComputer.isp2|"rgb(50%, 50%, 50%);rgb(80%, 80%, 0%);rgb(0%, 50%, 0%);rgb(27%, 31%, 70%)"%' d="m95.37 339.97 241.06 113.64 245.89-115.92-241.06-113.64z"/>
<path fill='%ThisComputer.isp2|"rgb(60%, 60%, 60%);rgb(90%, 90%, 10%);rgb(10%, 60%, 10%);rgb(36%, 40%, 80%)"%' d="m336.43 453.61v141.21l245.89-115.92v-141.21z"/>
<path fill='%ThisComputer.isp2|"rgb(70%, 70%, 70%);rgb(100%, 100%, 20%);rgb(20%, 70%, 20%);rgb(48%, 48%, 90%)"%' d="m95.37 339.97 241.06 113.64v141.21l-241.06-113.65z"/>
</g>
<g fill="#f0f0f0">
<path d="m302.68 264.66 38.514 5.6218 38.514 5.6218-16.367 5.3307 40.055 21.107-26.52 10.44-44.055-21.607-16.367 5.3308-6.8691-15.929z"/>
<path d="m473.91 375.32-6.8691-15.929-6.8691-15.929-16.367 5.3308-46.555-22.107-26.02 9.9405 42.055 22.107-16.367 5.3307 38.514 5.6218z"/>
<path d="m218.1 303.03 38.514 5.6218 38.514 5.6218-16.367 5.3307 41.555 21.107-25.52 10.94-46.555-22.107-16.367 5.3308-6.8691-15.929z"/>
<path d="m391.74 411.85-6.8691-15.929-6.8691-15.929-16.367 5.3308-46.555-22.107-26.52 9.9405 42.555 22.107-16.367 5.3307 38.514 5.6218z"/>
</g>
</g>
<text x="20" y="0" fill="black" stroke="none" font-size="10px">BL</text>
<text x="5" y="-10" fill="black" stroke="none" font-size="10px">%ThisComputer.isp2ping%</text>
</g>
<!-- Роутер -->
<g transform="translate(39.5, 89) scale(0.1)" onClick='runScript("getWiFiclients");'>
<defs>
<linearGradient x1="0.65625" y1="125.12147" x2="299.9375" y2="125.12147" id="linearGradient3833" gradientUnits="userSpaceOnUse" gradientTransform="translate(-0.31342216,851.86901)">
<stop id="stop3780" style="stop-color:#557b8b;stop-opacity:1" offset="0"/>
<stop id="stop3617" style="stop-color:#8aaac0;stop-opacity:1" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(5,-848)">
<path d="m 0.34282784,901.90026 0,100.62504 0.03125,0 c 0.13323192,17.103 26.93452416,33.7389 74.81249916,42.9062 71.558743,13.7016 163.060593,5.5444 204.375003,-18.1875 13.59024,-7.8065 20.05029,-16.3087 20.0625,-24.7187 l 0,-100.37504 c -0.0444,8.38955 -6.50621,16.86923 -20.0625,24.65625 C 238.24717,950.53839 146.74532,958.66433 75.186577,944.96276 27.137451,935.76265 0.31623817,919.06779 0.37407784,901.90026 l -0.03125,0 z" style="fill:url(#linearGradient3833);fill-opacity:1;stroke:none"/>
<path d="m 280.43717,678.68689 a 113.47587,113.47587 0 1 1 -226.951735,0 113.47587,113.47587 0 1 1 226.951735,0 z" transform="matrix(1.141816,0.218627,-0.659227,0.378674,406.7696,608.8589)" style="fill:#557b8b;fill-opacity:1;stroke:none"/>
<path d="m 229.92724,898.48426 -18.78401,10.78993 66.58103,12.7485 -12.52268,7.1933 -66.58103,-12.7485 -18.78403,10.7899 -12.48521,-21.5727 62.57593,-7.20043 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
<path d="m 120.16224,877.46718 -18.78403,10.78992 -66.581031,-12.7485 -12.52268,7.19328 66.581041,12.7485 -18.78402,10.78991 62.57593,-7.20044 -12.48521,-21.57267 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
<path d="m 136.24453,936.84279 -26.02789,-4.9836 38.44057,-22.0811 -17.35193,-3.3224 -38.44058,22.0811 -26.0279,-4.9837 13.03556,19.0916 56.37217,-5.8019 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
<path d="m 233.16085,881.17199 -26.0279,-4.98365 -38.43962,22.08051 -17.35193,-3.32244 38.43961,-22.0805 -26.0279,-4.98366 56.37312,-5.80238 13.03462,19.09212 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
</g>
</g>
<!-- Крестики для линий -->
<defs>
<g id="krest8549">
<line x1="10" y1="5" x2="10" y2="15" stroke="red" />
<line x1="5" y1="10" x2="15" y2="10" stroke="red" />
</g>
</defs>
<!-- От провайдера в Интернет -->
<g transform="translate(20, 33)" >
<line x1="20" y1="0" x2="0" y2="20" stroke="black" />
<use href="#krest8549" style='display:%ThisComputer.isp1|"block;block;none;none"%'/>
</g>
<g transform="translate(70, 33)" >
<line x1="0" y1="0" x2="20" y2="20" stroke="black" />
<use href="#krest8549" style='display:%ThisComputer.isp2|"block;block;none;none"%'/>
</g>
<!-- От роутера к провайдеру -->
<g transform="translate(20, 73)" >
<line x1="0" y1="0" x2="20" y2="20" stroke="black" />
<use href="#krest8549" style='display:%ThisComputer.isp1|"block;none;none;none"%'/>
</g>
<g transform="translate(70, 73)" >
<line x1="20" y1="0" x2="0" y2="20" stroke="black" />
<use href="#krest8549" style='display:%ThisComputer.isp2|"block;none;none;none"%'/>
</g>
</svg>Цвет провайдеров будет меняться в зависимости от значений свойств isp1 и isp2.
На линиях будут крестики при неактивном провайдере и отсутствии Интернета за ним.
К примеру у меня сейчас активны оба провайдера, у обоих есть Интернет, и второй выбран основным.
При нажатии на провайдеров вызывается скрипт runScript("isp1select"); и runScript("isp2select");
Скрипт меняет дистанцию провайдера. Так можно быстро выбрать основным провайдером другого, если на то есть необходимость.
У меня у isp2 дистанция всегда 20. У isp2 cкриптами isp1select меняю на 10, isp2select на 30, тем самым маршрут isp1 встает до или после isp2
скрипт isp1selectSPOILER_SHOW
Код: Выделить всё
$ip = gg('Mikrotik_IP');
$login = gg('Mikrotik_login');
$password = gg('Mikrotik_password');
$API = new routeros_api();
$API->debug = false;
if ($API->connect($ip, $login, $password)) {
$API->write('/ip/route/set',false);
$API->write('=.id=*1D',false);
$API->write('=distance=10');
$READ = $API->read(false);
$API->disconnect();
echo 'ok';
} else { echo 'error'; }Пример настройки методов объекта телефона
МетодыSPOILER_SHOW
Метод CAPchange. Сработает при смене точки. Эта за входной дверью. Когда объект не на охране, Алиса откроет дверь своим.
Метод Found. Подъехал к дому, точка на фасаде засечет первая. В этом примере включаю свет только при методе Found, т.к.
телефон может сесть на это точку когда уже буду дома.
Метод Located. Нахождение устройства на точке. Не во всех объектах класса Room у меня есть детекторы движения. На некоторых определяю присутствие по WiFi устройствам. Вот пример
Метод Lost. Телефон как радио метка для разрешения подъема гаражных ворот
Код: Выделить всё
switch ($this->getProperty('CAPname')) {
case 'CAP--BagirS-1':
say('Открываю Серёже входную дверь');
callMethod(.....................);
break;
}телефон может сесть на это точку когда уже буду дома.
Код: Выделить всё
switch ($this->getProperty('CAPname')) {
case 'CAP--Bagir-1':
// Включить прожекторы у подъезда, если телефон обнаружен на этой точке
cm('DrivewayArea.onActivity');
break;
case 'CAP--BagirS-1':
// Открыть дверь
say('Открываю Серёже входную дверь');
callMethod('..............');
break;
}Код: Выделить всё
switch ($this->getProperty('CAPname')) {
case 'CAP--SXT-1':
cm('Gazebo.onActivity');
break;
case 'CAP--hAPmini-1':
cm('KitchenArea.onActivity');
break;
}Код: Выделить всё
callMethod('bs_garageLabel.statusChanged', array('status'=>0));Где ещё можно использовать
marker - свойство класса WiFiDevice. Поставьте в нем флаг "f" для устройств близких друзей.
Заполните свойства
owner - владелец. Например просто Имя или Имя Фамилия
deviceName - имя устройства. Например Телефон брата или Терминал на кухне.
Добавьте в метод activate объекта GuestsMode класс OperationalModes код
Код: Выделить всё
// перебор объектов класса
$objects=getObjectsByClass("WiFiDevice");
//перебрать объекты класса и собрать массив с именами друзей
foreach($objects as $obj) {
$obj=getObject($obj['TITLE']);
// Если online и друг
if ($obj->getProperty('online')) {
if (stripos($obj->getProperty('marker'), 'f')!==false) {
$s=$obj->getProperty('owner');
if ($s) {
// взять до пробела (отбросить фамилию)
$arr[]=explode(' ', $s)[0];
} else {
$arr[]='Неизвестный';
}
}}
}
$s='У нас гости.';
if (isset($arr)) {
//Первый элемент массива
$s.=' '.$arr[0];
//со второго по предпоследний через запятую
for($i = 1, $size = count($arr)-1; $i < $size; ++$i) {
$s.=', '.$arr[$i];
}
// последний через "и"
if (count($arr) > 1) {
$s.=' и '.end($arr);
}
// вывод
//echo $s;
say($s, (gg('GuardMode.status')>1)?3:2);
}Создайте сценарий friendshere
Код: Выделить всё
// Получить все объекты класса
$objects = getObjectsByClass('WiFiDevice');
// перебор объектов класса WiFiDevice
// Поиск первого совпадения. Устройство друга в сети
foreach ($objects as $obj) {
$obj=getObject($obj['TITLE']);
if ($obj->getProperty('online')) {
if (stripos($obj->getProperty('marker'), 'f') !== false) {
$f = true;
break;
}
}
}
// Решение о изменении состояния режима
if (gg('GuestsMode.status')) {
if (!$f) { cm('GuestsMode.deactivate'); }
} else {
if ($f) { cm('GuestsMode.activate'); }
}