До этого больше года имел стабильно работающую систему под Ubuntu, реализованную на 960 Grid System и давно хотелось оптимизировать интерфейс для просмотра на мобильных устройствах.
А тут еще помимо мобильного интерфейса заполучил и бесплатную настроенную CMS, которую Сергей изначально разрабатывал в коммерческих целях судя по всему. Так что ему и всем разработчикам большой респект за проделанную и проделываемую работу.
Итак, для начала, что получилось:


"Пульт управления" - дешевый планшет типа ePad 7-дюймов с Android 2.2. Заказывал за ~50 фунтов на amazon.co.uk по "подарочным" купонам.
Теперь распишу, что устанавливал, что дорабатывал (может встречаться "быдло-код"

"Сервер": Intel D945GCZ, Celeron 2.8Ghz, 1Gb RAM, 80Gb HDD, сеть/аудио/видео интегрированное, мастер шины 1-wire DS9097 (на COM-порт), плата видеозахвата AverMedia EZCapture (низкопрофильная, чтобы влезала в Slim Desktop корпус).
Ubuntu 12.04.1 Server. При инсталляции из дополнительных пакетов выбрал LAMP (Apache, MySQL, PHP).
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install mc (Midnight Commander)
sudo apt-get install php5-curl (необходимый модуль для системы)
sudo apt-get install phpmyadmin (админка для mysql)
sudo apt-get install owfs (пакет для работы с 1-wire)
sudo apt-get install motion (пакет для видеонаблюдения)
sudo apt-get install lm-sensors (пакет для мониторинга температуры системы, частоты вращения кулеров ...)
sudo apt-get install hddtemp (пакет для мониторинга температуры жесткого диска)
для нормальной работы в дальнейшем необходимо дать команду sudo chmod u+s /usr/sbin/hddtemp
установил статический адрес в /etc/network/interfaces:
Код: Выделить всё
auto eth0
iface eth0 inet static
address 192.168.1.222
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
dns-nameservers 192.168.1.1
#iface eth0 inet dhcp
Код: Выделить всё
nameserver 192.168.1.1
раскомментарить server: device = /dev/ttyS0 (для COM1, для USB будет немного по-другому)
закомментарить server: FAKE = DS18B20, DS2405
по желанию можно убрать из автозапуска owftpd и owhttpd, а оставить только owserver
в /etc/php5/apache2/php.ini и /etc/php5/cli/php.ini установить
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED
чтобы не заваливало ненужными сообщениями в логах
в /etc/apache2/sites-enabled/@000-default изменить
AllowOverride all для /var/www
/etc/apache2/mods-available/rewrite.load перенести в /etc/apache2/mods-enabled
Далее по очереди по пунктам:
1. Заголовок заглавной страницы menu.html
Вместо фразы MajorDoMo по совету супруги добавил туда часы, потому что на "пульте управления" я использую браузер Dolphin Mini в полноэкранном режиме и часов в трее не видно, а часы у входной двери перед глазами не помешают.
В каталоге /js разместил файл date.js, который можно найти в аттаче:
В файле /templates/menu.html добавил на него ссылку в самом начале:
<script type="text/javascript" src="/js/date.js"></script>
В файле /templates/commands/commands_search_pda.html заменил сам заголовок
<h1 id="label_[#PARENT_ID#]">[#if PARENT_TITLE!=""#][#PARENT_TITLE#][#else#]MajorDoMo[#endif#]</h1>
на
<h1 id="label_[#PARENT_ID#]">[#if PARENT_TITLE!=""#][#PARENT_TITLE#][#else#]<span id="clock">MajorDoMo</span>[#endif#]</h1>
Кстати, на втором скрине ошибка в месяце. В js-скрипте месяцы считались от 0 до 11, а не от 1 до 12 по умолчанию. Поправил.
2. Климат

У меня 4 1-wire термометра ds18b20. Для них в CMS создан класс tempSensors со свойством temp и методом tempChanged, созданы 4 объекта Kitchen, Room, Outside, Balcony, в разделе 1-Wire в CMS для каждого термометра DS18B20 в соответствующем сценарии прописал callMethod("Balcony.tempChanged",array('t'=>$params['temperature12']['VALUE']));
Balcony - это имя объекта соответственно. temperature12 - это единственное правильное значение для моих термометров.
Метод tempChanged выглядит так:
Код: Выделить всё
if (is_numeric($params['t']) and ($params['t'] <> 85)) {
$t=round($params['t']*2)/2;
$this->setProperty('temp',$t);
$this->setProperty('updated',time());
$this->setProperty('updatedTime',date("H:i d-m-Y",time()));
};
Прогноз погоды пока оставил по умолчанию, какой был в дистрибутиве. Только изменил город на Москву.
Коды для графиков вида: <img src="/pChart/?p=Outside.temp&title=Улица (%Outside.temp%°C)&type=24h&width=200&height=160>ype=curve&px=15&%rand%">
Заголовок меню управления выглядит так:
Климат ( %Outside.temp%°C / %Room.temp%°C )
Графики пока работают ужасно: тормозят, не отображаются, срезаются ... но ребята активно борются с этой проблемой - ждем результатов.
3. Пробки

В объекте ThisComputer создал два свойства: ProbkiText и Probki, cделал две веб-переменные: YandexProbkiText и YandexProbki
Ссылка для Москвы: http://export.yandex.ru/bar/reginfo.xml?ncrnd=2789
Кодировка: utf-8
Шаблоны поиска: hint lang="ru"\>(.+?)\<\/hint для ProbkiText и level\>(.+?)\<\/level\> для Probki
Заголовок меню управления выглядит так:
%ThisComputer.ProbkiText% (%ThisComputer.Probki%)
При входе в данный пункт меню попадаем в HTML-блок с кодом:
<iframe src="/ya.php" border=0 frameborder=0 width="260"/>
Код страницы ya.php положил в аттач. Код написал на основе какого-то примера и все заработало без всяких API-ключей и вроде во всех браузерах. Чисто из php без участия браузера Яндекс не даёт получать маршруты, поэтому пришлось использовать javascript. В результате времена маршрутов отображаются не моментально. Они просчитываются каждый раз при открытии страницы.
Наверное, круто было бы парсить сразу несколько веб-переменных за одно скачивание страницы и раскладывать в разные свойства.
4. Видеонаблюдение

Пока опишу алгоритм работы данного модуля, потому что описание настроек пакета motion довольно комплексное, надо этому посвятить отдельную тему. Если будут заинтересованные, то я сделаю.
Итак: после обнаружения движения в камере пакет motion, во-первых, создает две записи (для фотки и для видео) в специальной таблице mySQL, которую я добавил в базу db_terminal, а, во-вторых, запускает сценарий MajorDoMo, который берет последние 10 событий из вышеуказанной таблицы, создает картинки-превьюшки для новых событий в отдельной папке и потом формирует кусок html-кода, который записывается в глобальную переменную в объекте ThisComputer. Содержимое этой переменной и отображается при входе в раздел Видеонаблюдение.
Для просмотра увеличенных фотографий используется шаблонный файл showimage.php, которому передается имя фотки. Синий кружочек с белой стрелочкой в правом столбце это ссылка на видеофрагмент.
Для примера приведу сценарий, который отрабатывает при обнаружении движения:
Код: Выделить всё
include("./simpleimage.php");
//say("Обнаружено движение в камере");
$sql=SQLSelect("SELECT * FROM security where file_type=1 order by time_stamp DESC limit 10");
$result='<ul data-role="listview" class="ui-listview">';
foreach($sql as $row) {
$shortname = substr($row[filename],strripos($row[filename],'/'));
$webname = substr($row[filename],8);
$resultname = '/var/www/camsjpg/cam1/thumbs/'.$shortname;
$sql1=SQLSelect("SELECT * FROM security where file_type=8 and text_event=\"".$row['text_event']."\"");
$videoname = substr($sql1[0]['filename'],8);
if (!file_exists($resultname)) {
$image = new SimpleImage();
$image->load($row[filename]);
$image->resizeToHeight(70);
$image->save($resultname);
};
$result = $result.'<li><a href="/showimage.php?img='.$webname.'&date='.$row[time_stamp].'" rel="external" style="padding-right:0px;"><img src="/camsjpg/cam1/thumbs/'.$shortname.'" style="position:absolute;top:0;bottom:0;margin:auto;" class="image">'.substr($row[time_stamp],11,8).'<br>'.substr($row[time_stamp],0,10).'</a><a href="'.$videoname.'" data-rel="dialog" data-transition="slideup" rel="external">Видео</a></li>';
};
$result = $result.'</ul>';
sg('ThisComputer.security',$result);
5. История событий
Практически всё штатно. Я только поправил /modules/shoutbox/shouts_search.inc.php строку 129, где выводилось имя пользователя, который что-либо сообщает в чате. Мне это не нужно.
6. Архив событий

С помощью Сергея пришёл к такому порядку действий:
Сначала создаём домашнюю страницу с кодом
Код: Выделить всё
<div style="text-shadow:none;font-weight:normal;">[#module name="shoutbox" limit="100" reverse="1" mobile="1"#]</div>
Код: Выделить всё
<iframe src="/page/14.html" border=0 frameborder=0 width="260" height="800"/>
7. Балансы мобильных телефонов

Раз в полчаса по крону запускается php-скрипт, получающий балансы мобильников из личных кабинетов на сайтах операторов.
Код: Выделить всё
0-59/30 * * * * php /var/www/scripts/balance.php 2>/dev/null
Итак, создаем класс объектов MobilePhone со свойством balance и методом balanceChanged:
Код: Выделить всё
$this->setProperty('balance',$params['balance']);
Код: Выделить всё
wget http://127.0.0.1/objects/?object=andy\&op=m\&m=balanceChanged\&balance=".$balance." -O \/dev\/null"
Код: Выделить всё
<table data-role="table" id="my-table" data-mode="reflow">
<tbody>
<tr>
<th style="width:160px;">Андрей</th>
<td>%andy.balance%</td>
</tr>
<tr>
<th>Ольга</th>
<td>%olga.balance%</td>
</tr>
</tbody>
</table>
8. Доступность устройств

Тут опять же всё довольно штатно. Добавляем наши объекты в разделе CMS "Устройства Online", прописываем для них действия при переходе в онлайн/оффлайн по необходимости типа
Код: Выделить всё
say("Интернет не доступен");
Я для себя немного поправил шаблон для отображения состояний. Он находится в /templates/pinghosts/pinghosts_search_site_mobile.html:
Код: Выделить всё
<table>
[#begin RESULT#]
<tr>
<td>
<a href="#" data-role="button" data-icon="info" style="background:
[#if STATUS="0"#]unknown[#endif#]
[#if STATUS="1"#]green[#endif#]
[#if STATUS="2"#]red[#endif#]
; color: white;">[#if TITLE!=""#][#TITLE#][#endif#]</a></td>
<td>[#CHECK_LATEST#]</td>
</tr>
[#end RESULT#]
</table>

Данная информация собирается при помощи линуксовых программ sensors, hddtemp, uptime ... Предварительно я создал несколько свойств у объекта ThisComputer: system_temp1, system_temp3, system_mbtemp, system_fan1, system_hdd1temp, system_uptime, system_freespace. В зависимости от материнской платы и её средств мониторинга полезных датчиков может быть другое количество или немного могут меняться их названия. Раз в минуту в методе OnNewMinute вызывается сценарий sensors: RunScript('sensors');. Он выглядит следующим образом:
Код: Выделить всё
exec("sensors",$asensors);
for($i=0;$i<count($asensors);$i++) {
ereg("[^:]*\:[ ]*([0-9+-.]*)",$asensors[$i],$arr);
$temp = trim($arr[1]);
$temp = str_replace('+','',$temp);
switch (substr($asensors[$i],0,5)) {
case 'fan1:':
sg('system_fan1',$temp);
break;
case 'temp1':
sg('system_temp1',$temp);
break;
case 'temp3':
sg('system_temp3',$temp);
break;
case 'M/B T':
sg('system_mbtemp',$temp);
break;
};
};
$temp=exec("\/usr\/sbin\/hddtemp \/dev\/sda1");
ereg("[^:]*\:[ ]*[^:]*\:[ ]*([0-9+-.]*)",$temp,$arr);
$temp = trim($arr[1]);
$temp = str_replace('+','',$temp);
sg('system_hdd1temp',$temp);
$data = shell_exec('uptime');
$uptime = explode(' up ', $data);
$uptime = explode(',', $uptime[1]);
sg('system_uptime',trim($uptime[0]));
$bytes = disk_free_space(".");
$si_prefix = array( 'B', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
$base = 1024;
$class = min((int)log($bytes , $base) , count($si_prefix) - 1);
$temp = round($bytes / pow($base,$class)) . ' ' . $si_prefix[$class];
sg('system_freespace',$temp);
Соответственно эти данные добавляем в таблицу в HTML-блок в очередном разделе в меню управления:
Код: Выделить всё
<table>
<tr>
<td>
Свободное место на диске: </td><td>%ThisComputer.system_freespace%</td></tr>
<tr><td>
Время непрерывной работы: </td><td>%ThisComputer.system_uptime%</td></tr>
<tr><td>
Температура системы 1: </td><td>%ThisComputer.system_temp1%</td></tr>
<tr><td>
Температура системы 3: </td><td>%ThisComputer.system_temp3%</td></tr>
<tr><td>
Температура мат. платы: </td><td>%ThisComputer.system_mbtemp%</td></tr>
<tr><td>
Температура жесткого диска: </td><td>%ThisComputer.system_hdd1temp%</td></tr>
<tr><td>
Скорость вращения кулера: </td><td>%ThisComputer.system_fan1%</td></tr>
</table>

В этом разделе остались штатные пункты меню типа Календарь, GPS ..., а также я добавил несколько кнопок для управления внешними устройствами. В данный момент активны две первые кнопки, включающие и выключающие большой домашний компьютер. Команды от кнопок посылаются на Arduino-контроллер с Ethernet-шилдом.
Код для кнопки "Выключить компьютер":
Код: Выделить всё
$url='http://192.168.1.223/command?3=LCLICK';
getURL($url,0);
Код: Выделить всё
$url='http://192.168.1.223/command?3=CLICK';
getURL($url,0);
Вот список того, что используется для компиляции "серверной" прошивки Arduino.
DallasTemperature library: https://github.com/milesburton/Arduino- ... ol-Library
OneWire library: http://www.pjrc.com/teensy/td_libs_OneWire.html
WebServer library: https://github.com/sirleech/Webduino
MajorDomo Control Server: https://github.com/sochkasov/majordomo-arduino
В Control Server я правда для себя закомментарил всё, относящееся к протоколу 1-wire, потому что этим у меня занимается Linux.
11. GPS
Использую всё, как "в букваре": Big Brother GPS под Android. В целом нормально, но если долго программа висит в фоне (когда нахожусь в здании), то после выхода на улицу пока её не активируешь, она ничего посылать не собирается. Continuos Operation режим не использую, потому что батарейка и так хреново держит.
Привязки к координатам пока не придумал, как использовать, но домашним нравится "за мной наблюдать" по истории событий

Что еще из косметических изменений:
Бэджи (цифры, обозначающие количество подпунктов) из меню убираются в /templates/commands/commands_search_pda.html: <span class="ui-li-count">[#RESULT#]
Чтобы не урезались заголовки страниц я поправил /jquerymobile/jquery.mobile-1.1.0.css и установил margin: .6em 0 .8em 5px; для ui-header.
P.S. Сейчас делаю удаленные датчики и relay-модули на attiny85+nrf24l01+dht11/ds18b20. Для чего купил соответствующей комплектухи на ebay.