Modules/devices/devices.class.php
Материал из MajorDoMo инфо
Возможно где-то ошибки с закрывающими или открывающими {} в связи с добавлением коментов, мог гдето херакнуть, прошу заранее прощения, сам ищу косяки и поправлю если найду. Но вроде бы не должно быть.
<?php /** * Общий коммент к пониманию стандарта PHPDoc: * Аннотация @method для указания на то, что это комментарий к методу. * Аннотация @param для описания параметров метода. * Аннотация @return для описания возвращаемого значения метода. * Аннотация @see для ссылок на другие классы или методы. * Аннотация @since для указания версии, с которой был добавлен метод. * * Типы параметров и возвращаемых значений: * - string: строковое значение. * - int: целочисленное значение. * - float: число с плавающей точкой. * - bool: булево значение (true или false). * - array: массив. * - object: объект. * - mixed: любой тип данных. * - callable: функция или метод. * - resource: ресурс (например, файл). * - null: специальное значение, обозначающее отсутствие значения. * * Пример использования: * @method void myVoidMethod() Описание метода, который не возвращает значение. * @param string $param1 Параметр типа string. * @param int $param2 Параметр типа int. * @param float $param3 Параметр типа float. * @param bool $param4 Параметр типа bool. * @param array $param5 Параметр типа array. * @param object $param6 Параметр типа object. * @param mixed $param7 Параметр типа mixed (любой тип). * @param callable $param8 Параметр типа callable (функция или метод). * @param resource $param9 Параметр типа resource (ресурс, например, файл). * @param null $param10 Параметр типа null. * @return string Возвращает значение типа string. * @return int Возвращает значение типа int. * @return float Возвращает значение типа float. * @return bool Возвращает значение типа bool. * @return array Возвращает значение типа array. * @return object Возвращает значение типа object. * @return mixed Возвращает значение типа mixed (любой тип). * @return callable Возвращает значение типа callable (функция или метод). * @return resource Возвращает значение типа resource (ресурс, например, файл). * @return null Возвращает значение типа null. */ /** * Класс Devices * @package project * @author Wizard <sergejey@gmail.com> * @copyright http://majordomo.smartliving.ru/ (c) * @version 0.1 (wizard, 13:07:05 [Jul 19, 2016]) * @class Devices * @property string $name Имя модуля * @property string $title Заголовок модуля * @property string $module_category Категория модуля * @method void __construct() Конструктор класса * @method void saveParams($data = 0) Сохранение параметров модуля * @method void getParams() Получение параметров модуля из строки запроса * @method void setDictionary() Установка словаря для модуля * @method void run() Основная логика модуля * @see module * @since 0.1 */ // Класс devices расширяет модуль, что позволяет использовать функциональность модуля в классе devices. class devices extends module { var $view; var $id; /** * Конструктор класса модуля * @method void __construct() * @see module * @since 0.1 */ function __construct() { // Инициализация имени модуля. $this->name = "devices"; // Установка заголовка модуля, который будет отображаться в интерфейсе. $this->title = "<#LANG_SECTION_DEVICES#>"; // Установка категории модуля, которая также будет отображаться в интерфейсе. $this->module_category = "<#LANG_SECTION_DEVICES#>"; // Проверка, установлен ли модуль. $this->checkInstalled(); // Установка словаря для модуля. $this->setDictionary(); } /** * saveParams * * Сохранение параметров модуля * * @method void saveParams($data = 0) * @param mixed $data Данные для сохранения * @return void * @see module * @since 0.1 */ function saveParams($data = 0) { $p = array(); // Проверка наличия идентификатора модуля и его сохранение в массив параметров. if (isset($this->id)) { $p["id"] = $this->id; } // Проверка наличия режима просмотра и его сохранение в массив параметров. if (isset($this->view_mode)) { $p["view_mode"] = $this->view_mode; } // Проверка наличия режима редактирования и его сохранение в массив параметров. if (isset($this->edit_mode)) { $p["edit_mode"] = $this->edit_mode; } // Проверка наличия вкладки и ее сохранение в массив параметров. if (isset($this->tab)) { $p["tab"] = $this->tab; } // Вызов родительского метода saveParams с передачей массива параметров. return parent::saveParams($p); } /** * getParams * * Получение параметров модуля из строки запроса * Этот фрагмент кода содержит два метода: * saveParams и getParams. * Метод saveParams сохраняет параметры модуля, * а метод getParams получает их из строки запроса. * Оба методы используют глобальные переменные для работы с параметрами * * @method void getParams() * @return void * @see module * @since 0.1 */ function getParams() { global $id; global $mode; global $view_mode; global $edit_mode; global $tab; // Проверка наличия идентификатора и его присвоение свойству класса. if (isset($id)) { $this->id = $id; } // Проверка наличия режима и его присвоение свойству класса. if (isset($mode)) { $this->mode = $mode; } // Проверка наличия режима просмотра и его присвоение свойству класса. if (isset($view_mode)) { $this->view_mode = $view_mode; } // Проверка наличия режима редактирования и его присвоение свойству класса. if (isset($edit_mode)) { $this->edit_mode = $edit_mode; } // Проверка наличия вкладки и ее присвоение свойству класса. if (isset($tab)) { $this->tab = $tab; } } /** * setDictionary * Метод setDictionary включает два файла, * которые содержат структуру устройств и связи между ними. * Это необходимо для корректной работы модуля устройств. * * @method void setDictionary() * @return void * @see module * @since 0.1 */ function setDictionary() { // Включение файлов, содержащих структуру устройств и связи между ними. include(dirname(__FILE__) . '/devices_structure.inc.php'); include(dirname(__FILE__) . '/devices_structure_links.inc.php'); } /** * Run * * Метод run выполняет основную логику модуля. * Он проверяет действие, которое необходимо выполнить, и вызывает соответствующий метод. * Затем он устанавливает различные параметры, * такие как режим просмотра, режим редактирования, режим, действие и вкладку. * После этого он сохраняет данные в свойстве класса и создает новый объект парсера, * используя указанный шаблон и данные. * Результат парсинга сохраняется в свойстве класса. * * @method void run() * @return void * @see module * @since 0.1 */ function run() { // Инициализация массива для хранения данных. $out = array(); // Проверка действия и вызов соответствующего метода. if ($this->action == 'admin') { $this->admin($out); } elseif ($this->action == 'link') { $this->link($out); } else { $this->usual($out); } // Проверка и установка действия родительского объекта. if (isset($this->owner->action)) { $out['PARENT_ACTION'] = $this->owner->action; } // Проверка и установка имени родительского объекта. if (isset($this->owner->name)) { $out['PARENT_NAME'] = $this->owner->name; } // Установка режима просмотра. $out['VIEW_MODE'] = $this->view_mode; // Установка режима редактирования. $out['EDIT_MODE'] = $this->edit_mode; // Установка режима. $out['MODE'] = $this->mode; // Установка действия. $out['ACTION'] = $this->action; // Установка вкладки. $out['TAB'] = $this->tab; // Сохранение данных в свойстве класса. $this->data = $out; // Создание нового объекта парсера с указанным шаблоном и данными. $p = new parser(DIR_TEMPLATES . $this->name . "/" . $this->name . ".html", $this->data, $this); // Сохранение результата парсинга в свойстве класса. $this->result = $p->result; } /** * link * * Метод link обрабатывает связь устройства. * Он проверяет наличие различных параметров, * таких как тип устройства, исходная таблица, идентификатор исходной таблицы, префикс, * дополнительный заголовок и связанный объект. * Если все параметры корректны, он генерирует уникальный идентификатор для устройства * и устанавливает флаг успешного выполнения. * * @method void link() * @return void * @see module * @since 0.1 */ function link(&$out) { $ok = 1; // Проверка наличия типа устройства и его сохранение в массив вывода. if ($this->type) { $out['TYPE'] = $this->type; } else { // Если тип устройства не определен, устанавливаем флаг ошибки. $ok = 0; } // Проверка наличия исходной таблицы и ее сохранение в массив вывода. if ($this->source_table) { $out['SOURCE_TABLE'] = $this->source_table; } else { // Если исходная таблица не определена, устанавливаем флаг ошибки. $ok = 0; } // Проверка наличия идентификатора исходной таблицы и его сохранение в массив вывода. if ($this->source_table_id) { $out['SOURCE_TABLE_ID'] = $this->source_table_id; } else { // Если идентификатор исходной таблицы не определен, устанавливаем флаг ошибки. $ok = 0; } // Проверка наличия префикса и его сохранение в массив вывода. if ($this->prefix) { $out['PREFIX'] = $this->prefix; } // Проверка наличия дополнительного заголовка и его сохранение в массив вывода. if (isset($this->add_title)) { $out['ADD_TITLE'] = urlencode($this->add_title); } // Проверка наличия связанного объекта и его сохранение в массив вывода. if ($this->linked_object) { $device_rec = SQLSelectOne("SELECT ID,TITLE FROM devices WHERE LINKED_OBJECT LIKE '" . DBSafe($this->linked_object) . "'"); if (isset($device_rec['TITLE'])) { $out['TITLE'] = $device_rec['TITLE']; // Если установлен флаг предварительного просмотра, обрабатываем устройство и сохраняем результат в массив вывода. if ($this->preview) { $data = $this->processDevice($device_rec['ID']); $out['HTML'] = $data['HTML']; } $out['ID'] = $device_rec['ID']; } $out['LINKED_OBJECT'] = $this->linked_object; } // Генерация уникального идентификатора для устройства. $out['UNIQ'] = uniqid('dev'); // Если все параметры корректны, устанавливаем флаг успешного выполнения. if ($ok) { $out['OK'] = 1; } } /** * getTypeDetails * * Метод getTypeDetails возвращает детали типа устройства из словаря типов устройств. * * @method array getTypeDetails($type) * @param string $type Тип устройства * @return array Детали типа устройства * @see module * @since 0.1 */ function getTypeDetails($type) { // Возвращает детали типа устройства из словаря типов устройств. return $this->device_types[$type]; } /** * getTypeLinks * * Метод getTypeLinks возвращает все связи, которые связаны с указанным типом устройства. * Он сначала получает детали типа устройства, * а затем проходит по всем связям устройств, проверяя, * присутствует ли класс или родительский класс типа устройства в типах связей. * Если да, то все связи этого типа добавляются в результирующий массив. * * @method array getTypeLinks($type) * @param string $type Тип устройства * @return array Все связи, связанные с указанным типом устройства * @see module * @since 0.1 */ function getTypeLinks($type) { // Получаем детали типа устройства. $type_details = $this->getTypeDetails($type); $res_links = array(); // Проходимся по всем связям устройств. foreach ($this->device_links as $k => $v) { // Разделяем типы связей на отдельные элементы. $link_types = explode(',', $k); $link_types = array_map('trim', $link_types); // Если класс или родительский класс типа устройства присутствует в типах связей, if (in_array($type_details['CLASS'], $link_types) || in_array($type_details['PARENT_CLASS'], $link_types)) { // добавляем все связи этого типа в результирующий массив. foreach ($v as $link) { $res_links[] = $link; } } } // Возвращаем результирующий массив связей. return $res_links; } /** * getLinkDetails * * Метод getLinkDetails возвращает детали связи устройства по указанному имени связи. * Он проходит по всем связям устройств и возвращает детали той связи, * имя которой совпадает с указанным именем связи. * * @method array getLinkDetails($link_name) * @param string $link_name Имя связи устройства * @return array Детали связи устройства * @see module * @since 0.1 */ function getLinkDetails($link_name) { // Проходимся по всем связям устройств. foreach ($this->device_links as $k => $v) { foreach ($v as $link) { // Если имя связи совпадает с указанным именем связи, возвращаем детали этой связи. if ($link['LINK_NAME'] == $link_name) { return $link; } } } } /** * getAllGroups * * Метод getAllGroups возвращает все группы устройств, которые применимы к указанному типу устройства. * Он выбирает все группы устройств из базы данных, * проходит по ним и добавляет в результирующий массив те группы, * типы которых присутствуют в типах, применимых к группе. * * @method array getAllGroups($type) * @param string $type Тип устройства * @return array Все группы устройств, применимые к указанному типу устройства * @see module * @since 0.1 */ function getAllGroups($type) { // Выбираем все группы устройств из базы данных. $groups = SQLSelect("SELECT * FROM devices_groups"); $res = array(); $total = count($groups); // Проходимся по всем группам. for ($i = 0; $i < $total; $i++) { $tmp = explode(',', $groups[$i]['APPLY_TYPES']); // Если тип устройства присутствует в типах, применимых к группе, добавляем группу в результирующий массив. if (in_array($type, $tmp)) { $res[] = $groups[$i]; } } // Возвращаем результирующий массив групп. return $res; } /** * getAllProperties * * Метод getAllProperties возвращает все свойства указанного типа устройства, * включая свойства его родительского класса. * Он сначала получает свойства текущего типа устройства, * а затем, если у типа устройства есть родительский класс, * проходит по всем типам устройств, ищет свойства родительского класса и добавляет их в свойства текущего типа, * если они еще не присутствуют. * * @method array getAllProperties($type) * @param string $type Тип устройства * @return array Все свойства указанного типа устройства, включая свойства его родительского класса * @see module * @since 0.1 */ function getAllProperties($type) { // Получаем свойства текущего типа устройства. $properties = $this->device_types[$type]['PROPERTIES']; $parent_class = isset($this->device_types[$type]['PARENT_CLASS']) ? $this->device_types[$type]['PARENT_CLASS'] : ''; // Если у типа устройства есть родительский класс, if ($parent_class != '') { // проходимся по всем типам устройств. foreach ($this->device_types as $k => $v) { // Если класс текущего типа совпадает с родительским классом, if ($v['CLASS'] == $parent_class) { // получаем свойства родительского класса. $parent_properties = $this->getAllProperties($k); // Проходимся по всем свойствам родительского класса. foreach ($parent_properties as $pk => $pv) { // Если свойство родительского класса еще не присутствует в свойствах текущего типа, добавляем его. if (!isset($properties[$pk])) { $properties[$pk] = $pv; } } } } } // Возвращаем все свойства текущего типа устройства, включая свойства родительского класса. return $properties; } /** * getAllMethods * * Метод getAllMethods возвращает все методы указанного типа устройства, * включая методы его родительского класса. * Он сначала получает методы текущего типа устройства, * а затем, если у типа устройства есть родительский класс, * проходит по всем типам устройств, * ищет методы родительского класса и добавляет их в методы текущего типа, * если они еще не присутствуют. * * @method array getAllMethods($type) * @param string $type Тип устройства * @return array Все методы указанного типа устройства, включая методы его родительского класса * @see module * @since 0.1 */ function getAllMethods($type) { // Получаем методы текущего типа устройства. $methods = $this->device_types[$type]['METHODS']; $parent_class = isset($this->device_types[$type]['PARENT_CLASS']) ? $this->device_types[$type]['PARENT_CLASS'] : ''; // Если у типа устройства есть родительский класс, if ($parent_class != '') { // проходимся по всем типам устройств. foreach ($this->device_types as $k => $v) { // Если класс текущего типа совпадает с родительским классом, if ($v['CLASS'] == $parent_class) { // получаем методы родительского класса. $parent_methods = $this->getAllMethods($k); // Проходимся по всем методам родительского класса. foreach ($parent_methods as $pk => $pv) { // Если метод родительского класса еще не присутствует в методах текущего типа, добавляем его. if (!isset($methods[$pk])) { $methods[$pk] = $pv; } } } } } // Возвращаем все методы текущего типа устройства, включая методы родительского класса. return $methods; } /** * getNewObjectIndex * * Метод getNewObjectIndex возвращает новый индекс для объекта указанного класса. * Он сначала получает объекты указанного класса, а затем, если префикс не пустой, * выбирает дополнительные объекты, которые начинаются с этого префикса. * Затем он проходит по всем объектам, ищет числа в их названиях и обновляет индекс, * если найденное число больше текущего индекса. * В конце метод возвращает индекс, увеличенный на 1, и добавляет в начало ноль, если индекс меньше 10. * * @method string getNewObjectIndex($class, $prefix = '') * @param string $class Имя класса * @param string $prefix Префикс для поиска объектов * @return string Новый индекс для объекта указанного класса * @see module * @since 0.1 */ function getNewObjectIndex($class, $prefix = '') { // Получаем объекты указанного класса. $objects = getObjectsByClass($class); if ($prefix != '') { // Если префикс не пустой, выбираем дополнительные объекты, которые начинаются с этого префикса. $other_objects = SQLSelect("SELECT TITLE FROM objects WHERE TITLE LIKE '" . $prefix . "%'"); foreach ($other_objects as $obj) { $objects[] = $obj; } } $index = 0; $total = count($objects); // Проходимся по всем объектам. for ($i = 0; $i < $total; $i++) { // Если в названии объекта присутствует число, if (preg_match('/(\d+)/', $objects[$i]['TITLE'], $m)) { $current_index = (int)$m[1]; // и это число больше текущего индекса, обновляем индекс. if ($current_index > $index) { $index = $current_index; } } } $index++; // Если индекс меньше 10, добавляем в начало ноль. if ($index < 10) { $index = '0' . $index; } // Возвращаем индекс. return $index; } /** * processDevice * * Метод processDevice обрабатывает устройство с указанным идентификатором. * Он выбирает запись устройства из базы данных, получает шаблон для объекта класса устройства, * обрабатывает шаблон и сохраняет результат в массив результатов. * Если тип устройства - 'camera', он устанавливает высоту результата. * В конце метод возвращает результат. * * @method array processDevice($device_id, $view = '') * @param int $device_id Идентификатор устройства * @param string $view Название представления * @return array Результат обработки устройства * @see module * @since 0.1 */ function processDevice($device_id, $view = '') { // Начинаем измерение времени выполнения метода. startMeasure('processDevice'); // Выбираем запись устройства из базы данных по его идентификатору. $device_rec = SQLSelectOne("SELECT * FROM devices WHERE ID=" . (int)$device_id); $result = array('HTML' => '', 'DEVICE_ID' => $device_rec['ID']); // Получаем шаблон для объекта класса устройства. $template = getObjectClassTemplate($device_rec['LINKED_OBJECT'], $view); // Обрабатываем шаблон и сохраняем результат в массив результатов. $result['HTML'] = processTitle($template, $this); // Если тип устройства - 'camera', устанавливаем высоту результата. if ($device_rec['TYPE'] == 'camera') { $result['HEIGHT'] = 5; } // Завершаем измерение времени выполнения метода. endMeasure('processDevice'); // Возвращаем результат. return $result; } /** * getWatchedProperties * * Метод getWatchedProperties возвращает все свойства устройств, которые нужно отслеживать. * Он сначала устанавливает словарь для модуля, * затем выбирает устройства из базы данных и проходит по ним. * Для каждого устройства он получает все свойства текущего типа устройства и добавляет их в результирующий массив. * Если тип устройства - 'html', он обрабатывает содержимое устройства, * удаляет из него все символы, которые не являются частью имени свойства, * ищет все имена свойств в содержимом устройства * и добавляет каждое найденное свойство в результирующий массив. * В конце метод возвращает результирующий массив свойств. * * @method array getWatchedProperties($device_id = 0) * @param int $device_id Идентификатор устройства * @return array Все свойства устройств, которые нужно отслеживать * @see module * @since 0.1 */ function getWatchedProperties($device_id = 0) { // Устанавливаем словарь для модуля. $this->setDictionary(); $properties = array(); $qry = 1; if ($device_id) { // Если указан идентификатор устройства, добавляем его в запрос. $qry .= " AND devices.ID IN (" . $device_id . ")"; } // Выбираем устройства из базы данных. $devices = SQLSelect("SELECT * FROM devices WHERE $qry"); $total = count($devices); // Проходимся по всем устройствам. for ($i = 0; $i < $total; $i++) { if (!$devices[$i]['LINKED_OBJECT']) { continue; } // Получаем все свойства текущего типа устройства. $props = $this->getAllProperties($devices[$i]['TYPE']); if (is_array($props)) { foreach ($props as $k => $v) { // Если имя свойства начинается с символа подчеркивания, пропускаем его. if (substr($k, 0, 1) == '_') continue; // Добавляем свойство в результирующий массив. $properties[] = array('PROPERTY' => mb_strtolower($devices[$i]['LINKED_OBJECT'] . '.' . $k, 'UTF-8'), 'DEVICE_ID' => $devices[$i]['ID']); } } // Если тип устройства - 'html', обрабатываем содержимое устройства. if ($devices[$i]['TYPE'] == 'html') { $content = getGlobal($devices[$i]['LINKED_OBJECT'] . '.data'); // Удаляем из содержимого устройства все символы, которые не являются частью имени свойства. $content = preg_replace('/%([\w\d\.]+?)\.([\w\d\.]+?)\|(\d+)%/uis', '%\1.\2%', $content); $content = preg_replace('/%([\w\d\.]+?)\.([\w\d\.]+?)\|(\d+)%/uis', '%\1.\2%', $content); $content = preg_replace('/%([\w\d\.]+?)\.([\w\d\.]+?)\|".+?"%/uis', '%\1.\2%', $content); // Ищем все имена свойств в содержимом устройства. if (preg_match_all('/%([\w\d\.]+?)%/is', $content, $m)) { $totalm = count($m[1]); for ($im = 0; $im < $totalm; $im++) { // Добавляем каждое найденное свойство в результирующий массив. $properties[] = array('PROPERTY' => mb_strtolower($m[1][$im], 'UTF-8'), 'DEVICE_ID' => $devices[$i]['ID']); } } } } // Возвращаем результирующий массив свойств. return $properties; } /** * updateGroupObjects * * Метод updateGroupObjects обновляет объекты групп устройств. * Он сначала выбирает все группы устройств из базы данных, * затем проходит по ним и добавляет объект класса 'SGroups' * с указанным именем и описанием для каждой группы. * Затем он устанавливает свойство 'groupName' для каждого объекта * и сохраняет его в массив добавленных объектов. * После этого он получает все объекты класса 'SGroups' * и удаляет те из них, которые не присутствуют в массиве добавленных объектов. * * @method void updateGroupObjects() * @return void * @see module * @since 0.1 */ function updateGroupObjects() { // Выбираем все группы устройств из базы данных. $groups = SQLSelect("SELECT * FROM devices_groups WHERE 1"); $total = count($groups); $added_objects = array(); // Проходимся по всем группам. for ($i = 0; $i < $total; $i++) { // Добавляем объект класса 'SGroups' с указанным именем и описанием. $object_id = addClassObject('SGroups', 'group' . $groups[$i]['SYS_NAME'], 'group' . $groups[$i]['SYS_NAME']); $object_rec = SQLSelectOne("SELECT * FROM objects WHERE ID=" . $object_id); if ($object_rec['ID']) { // Обновляем описание объекта. $object_rec['DESCRIPTION'] = $groups[$i]['TITLE']; SQLUpdate('objects', $object_rec); } // Устанавливаем свойство 'groupName' для объекта. sg($object_rec['TITLE'] . '.groupName', $groups[$i]['SYS_NAME']); $added_objects[] = $object_id; } // Получаем все объекты класса 'SGroups'. $group_objects = getObjectsByClass('SGroups'); foreach ($group_objects as $rec) { // Если объект не присутствует в массиве добавленных объектов, удаляем его. if (!in_array($rec['ID'], $added_objects)) { deleteObject($rec['ID']); } } } /** * renderStructure * * Эта функция отвечает за рендеринг структуры устройств. * Она проходит по всем типам устройств и для каждого типа выполняет следующие действия: * - Добавляет класс устройства, если он не существует. * - Обновляет описание класса, если оно задано. * - Добавляет свойства класса, если они не существуют. * - Добавляет методы класса, если они не существуют. * - Добавляет объекты класса, если они не существуют. * - Подписывается на события 'COMMAND' и 'MINUTELY' для модуля устройств. * - Обновляет превью камер. * - Обновляет объекты устройств. * - Обновляет объекты групп устройств. * * @method void renderStructure() * @return void * @see module * @since 0.1 */ function renderStructure() { // Проверяем, определена ли константа DISABLE_SIMPLE_DEVICES и равна ли она 1 if (defined('DISABLE_SIMPLE_DEVICES') && DISABLE_SIMPLE_DEVICES == 1) { // Если условие выполняется, отписываемся от событий 'COMMAND' и 'MINUTELY' для модуля устройств и прекращаем выполнение функции unsubscribeFromEvent('devices', 'COMMAND'); unsubscribeFromEvent('devices', 'MINUTELY'); return; } // Проходимся по всем типам устройств foreach ($this->device_types as $k => $v) { // Класс устройства if (isset($v['PARENT_CLASS'])) { // Если у устройства есть родительский класс, добавляем класс с указанием родительского класса $class_id = addClass($v['CLASS'], $v['PARENT_CLASS']); } else { // Если у устройства нет родительского класса, добавляем класс без указания родительского класса $class_id = addClass($v['CLASS']); } // Если класс успешно добавлен if ($class_id) { // Получаем информацию о классе из базы данных $class = SQLSelectOne("SELECT * FROM classes WHERE ID=" . $class_id); // Если у устройства есть описание, обновляем описание класса в базе данных if (isset($v['DESCRIPTION'])) { $class['DESCRIPTION'] = $v['DESCRIPTION']; SQLUpdate('classes', $class); } } // Свойства класса if (isset($v['PROPERTIES']) && is_array($v['PROPERTIES'])) { // Проходимся по всем свойствам класса foreach ($v['PROPERTIES'] as $pk => $pv) { // Добавляем свойство класса с указанием, нужно ли сохранять историю изменений свойства $prop_id = addClassProperty($v['CLASS'], $pk, isset($pv['KEEP_HISTORY']) ? $pv['KEEP_HISTORY'] : 0); // Если свойство успешно добавлено if ($prop_id) { // Получаем информацию о свойстве из базы данных $property = SQLSelectOne("SELECT * FROM properties WHERE ID=" . $prop_id); // Если свойство является массивом if (is_array($pv)) { // Проходимся по всем элементам массива свойства foreach ($pv as $ppk => $ppv) { // Если ключ свойства начинается с символа подчеркивания, пропускаем его if (substr($ppk, 0, 1) == '_') continue; // Если свойство 'KEEP_HISTORY' уже установлено, пропускаем его if ($ppk == 'KEEP_HISTORY' && $property[$ppk]) continue; // Обновляем значение свойства $property[$ppk] = $ppv; } // Обновляем информацию о свойстве в базе данных SQLUpdate('properties', $property); } } } } // Методы класса if (isset($v['METHODS']) && is_array($v['METHODS'])) { // Проходимся по всем методам класса foreach ($v['METHODS'] as $mk => $mv) { // Добавляем метод класса с указанием кода метода и класса, к которому он принадлежит $method_id = addClassMethod($v['CLASS'], $mk, "require(DIR_MODULES.'devices/" . $v['CLASS'] . "_" . $mk . ".php');", 'SDevices'); // Если файл метода не существует, создаем его if (!file_exists(dirname(__FILE__) . '/' . $v['CLASS'] . "_" . $mk . ".php")) { $code = '<?php' . "\n\n"; @SaveFile(dirname(__FILE__) . "/" . $v['CLASS'] . "_" . $mk . ".php", $code); } // Если метод успешно добавлен if ($method_id) { // Получаем информацию о методе из базы данных $method = SQLSelectOne("SELECT * FROM methods WHERE ID=" . $method_id); // Если метод является массивом if (is_array($mv)) { // Проходимся по всем элементам массива метода foreach ($mv as $mmk => $mmv) { // Если ключ метода начинается с символа подчеркивания, пропускаем его if (substr($mmk, 0, 1) == '_') continue; // Обновляем значение метода $method[$mmk] = $mmv; } // Обновляем информацию о методе в базе данных SQLUpdate('methods', $method); } } } } // Внедрение методов if (isset($v['INJECTS']) && is_array($v['INJECTS'])) { // Проходимся по всем внедренным методам foreach ($v['INJECTS'] as $class_name => $methods) { // Добавляем класс addClass($class_name); // Проходимся по всем методам внедренного класса foreach ($methods as $mk => $mv) { // Разделяем имя объекта и имя метода list($object, $method_name) = explode('.', $mk); // Добавляем объект класса addClassObject($class_name, $object); // Если файл метода не существует, создаем его if (!file_exists(dirname(__FILE__) . "/" . $mv . ".php")) { $code = '<?php' . "\n\n"; @SaveFile(dirname(__FILE__) . "/" . $mv . ".php", $code); } // Внедряем код метода в объект injectObjectMethodCode($mk, 'SDevices', "require(DIR_MODULES.'devices/" . $mv . ".php');"); } } } } // Подписываемся на события 'COMMAND' и 'MINUTELY' для модуля устройств subscribeToEvent('devices', 'COMMAND'); subscribeToEvent('devices', 'MINUTELY'); // Обновление камер $objects = getObjectsByClass('SCameras'); $total = count($objects); for ($i = 0; $i < $total; $i++) { $ot = $objects[$i]['TITLE']; callMethod($ot . '.updatePreview'); } // Обновление объектов устройств $devices = SQLSelect("SELECT ID, LINKED_OBJECT FROM devices"); foreach ($devices as $device) { SQLExec("UPDATE objects SET `SYSTEM`='sdevice" . $device['ID'] . "' WHERE TITLE='" . DBSafe($device['LINKED_OBJECT']) . "' AND `SYSTEM`=''"); } // Обновление объектов групп устройств $this->updateGroupObjects(); } /** * processSubscription * * Эта функция обрабатывает подписки на события. * Она обрабатывает различные операции, такие как клик по устройству, получение устройства, получение устройств, * загрузка всех устройств HTML, клик по устройству и получение устройств. * * @method void processSubscription($event, &$details) * @param string $event Имя события * @param array $details Детали события * @return void * @see module * @since 0.1 */ function processSubscription($event, &$details) { // Если событие равно 'COMMAND' и есть идентификатор участника if ($event == 'COMMAND' && $details['member_id']) { // Включаем файл для обработки команды include_once(dirname(__FILE__) . '/processCommand.inc.php'); } // Если событие равно 'MINUTELY' if ($event == 'MINUTELY') { // Выбираем все активные точки планировщика устройств $points = SQLSelect("SELECT devices_scheduler_points.*, devices.LINKED_OBJECT FROM devices_scheduler_points LEFT JOIN devices ON devices_scheduler_points.DEVICE_ID=devices.ID WHERE ACTIVE=1"); $total = count($points); // Проходимся по всем точкам планировщика for ($i = 0; $i < $total; $i++) { $rec = $points[$i]; // Если дни не установлены, пропускаем текущую итерацию if ($rec['SET_DAYS'] === '') { continue; } // Разделяем дни на отдельные элементы $run_days = explode(',', $rec['SET_DAYS']); // Если текущий день не входит в список дней, пропускаем текущую итерацию if (!in_array(date('w'), $run_days)) { continue; } // Преобразуем время запуска в метку времени $tm = strtotime(date('Y-m-d') . ' ' . $rec['SET_TIME']); // Вычисляем разницу между текущим временем и временем запуска $diff = time() - $tm; // Преобразуем время последнего запуска в метку времени $latestRun = strtotime($rec['LATEST_RUN']); // Вычисляем разницу между текущим временем и временем последнего запуска $diff2 = time() - $latestRun; // Если разница меньше нуля, разница больше или равна 10 минутам или разница между последним запуском и текущим временем меньше или равна 10 минутам, пропускаем текущую итерацию if ($diff < 0 || $diff >= 10 * 60 || $diff2 <= 10 * 60) { continue; } // Проверяем доступ к точке планировщика if (!checkAccess('spoint', $rec['ID'])) continue; // Получаем связанный объект $linked_object = $rec['LINKED_OBJECT']; // Удаляем связанный объект из записи unset($rec['LINKED_OBJECT']); // Обновляем время последнего запуска $rec['LATEST_RUN'] = date('Y-m-d H:i:s'); // Обновляем запись в базе данных SQLUpdate('devices_scheduler_points', $rec); // Записываем сообщение о запуске точки планировщика DebMes("Running point: " . $linked_object . '.' . $rec['LINKED_METHOD'] . ' (' . $rec['VALUE'] . ')', 'devices_schedule'); // Если значение установлено, вызываем метод с этим значением if ($rec['VALUE'] != '') { callMethodSafe($linked_object . '.' . $rec['LINKED_METHOD'], array('value' => $rec['VALUE'])); } else { // Если значение не установлено, вызываем метод без параметров callMethodSafe($linked_object . '.' . $rec['LINKED_METHOD']); } } } } /** * computePermutations * * Эта функция вычисляет все возможные перестановки элементов массива. * Она использует рекурсивный подход для генерации всех возможных комбинаций. * * @method array computePermutations($array) * @param array $array Массив для перестановок * @return array Все возможные перестановки элементов массива * @see module * @since 0.1 */ function computePermutations($array) { $result = []; $recurse = function ($array, $start_i = 0) use (&$result, &$recurse) { // Если индекс начала равен индексу последнего элемента массива, добавляем текущую перестановку в результат if ($start_i === count($array) - 1) { array_push($result, $array); } // Проходимся по всем элементам массива, начиная с указанного индекса for ($i = $start_i; $i < count($array); $i++) { // Меняем местами элементы массива по индексам $i и $start_i $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; // Рекурсивно вызываем функцию для следующего индекса $recurse($array, $start_i + 1); // Восстанавливаем исходный порядок элементов массива $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; } }; // Вызываем рекурсивную функцию для начального массива $recurse($array); // Возвращаем результат, содержащий все возможные перестановки return $result; } /** * generate_combinations * * Эта функция генерирует все возможные комбинации среди набора вложенных массивов. * * @method array generate_combinations(array $data, array &$all = array(), array $group = array(), $value = null, $i = 0) * @param array $data Входной массив-контейнер. * @param array $all Конечный контейнер (используется внутри функции). * @param array $group Подконтейнер (используется внутри функции). * @param mixed $val Значение для добавления (используется внутри функции). * @param int $i Индекс ключа (используется внутри функции). * @return array Все возможные комбинации среди набора вложенных массивов. * @see module * @since 0.1 */ function generate_combinations(array $data, array &$all = array(), array $group = array(), $value = null, $i = 0, $key = null) { $keys = array_keys($data); // Если значение установлено, добавляем его в подконтейнер if (isset($value) === true) { $group[$key] = $value; } // Если индекс больше или равен количеству элементов в данных, добавляем подконтейнер в конечный контейнер if ($i >= count($data)) { array_push($all, $group); } else { $currentKey = $keys[$i]; $currentElement = $data[$currentKey]; // Если в текущем элементе данных нет элементов, рекурсивно вызываем функцию для следующего индекса if (count($data[$currentKey]) <= 0) { $this->generate_combinations($data, $all, $group, null, $i + 1, $currentKey); } elseif (is_array($currentElement)) { // Если текущий элемент является массивом, проходимся по всем его элементам и рекурсивно вызываем функцию для каждого из них foreach ($currentElement as $val) { $this->generate_combinations($data, $all, $group, $val, $i + 1, $currentKey); } } } // Возвращаем конечный контейнер с всеми сгенерированными комбинациями return $all; } /** * homebridgeSync * * Эта функция синхронизирует устройства с HomeBridge. * Она проверяет доступность HomeBridge и, если доступна, включает файл для синхронизации. * * @method void homebridgeSync($device_id = 0, $force_refresh = 0) * @param int $device_id Идентификатор устройства (по умолчанию 0). * @param int $force_refresh Принудительное обновление (по умолчанию 0). * @return void * @see module * @since 0.1 */ function homebridgeSync($device_id = 0, $force_refresh = 0) { // Проверяем доступность HomeBridge if ($this->isHomeBridgeAvailable()) { // Включаем файл для синхронизации с HomeBridge include_once(dirname(__FILE__) . '/homebridgeSync.inc.php'); } } /** * admin * * Функция обрабатывает административный интерфейс модуля. * Она обрабатывает различные действия, такие как синхронизация с HomeBridge, поиск устройств, управление группами, * управление расписанием, редактирование устройств, быстрое редактирование, * рендеринг структуры и удаление устройств. * * @method void admin(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function admin(&$out) { // Проверяем, установлен ли источник данных и не переданы ли параметры GET или POST if (isset($this->data_source) && !$_GET['data_source'] && !$_POST['data_source']) { $out['SET_DATASOURCE'] = 1; } // Если источник данных равен 'devices' или пустой строке if ($this->data_source == 'devices' || $this->data_source == '') { // Если режим равен 'homebridgesync', вызываем функцию синхронизации с HomeBridge и перенаправляем на главную страницу if ($this->mode == 'homebridgesync') { $this->homebridgeSync(); $this->redirect("?"); } // Если режим просмотра пустой или равен 'search_devices', вызываем функцию поиска устройств if ($this->view_mode == '' || $this->view_mode == 'search_devices') { $this->search_devices($out); // Если доступна HomeBridge, устанавливаем флаг ENABLE_HOMEBRIDGE if ($this->isHomeBridgeAvailable()) { $out['ENABLE_HOMEBRIDGE'] = 1; } } // Если режим просмотра равен 'manage_groups', вызываем функцию управления группами if ($this->view_mode == 'manage_groups') { $this->manage_groups($out); } // Если режим просмотра равен 'schedule', вызываем функцию управления расписанием if ($this->view_mode == 'schedule') { $this->manage_schedule($out); } // Если режим просмотра равен 'edit_devices', вызываем функцию редактирования устройств if ($this->view_mode == 'edit_devices') { $this->edit_devices($out, $this->id); } // Если режим просмотра равен 'quick_edit', вызываем функцию быстрого редактирования if ($this->view_mode == 'quick_edit') { $this->quick_edit($out); } // Если режим просмотра равен 'render_structure', вызываем функцию рендеринга структуры if ($this->view_mode == 'render_structure') { $this->renderStructure(); $this->redirect("?"); } // Если режим просмотра равен 'delete_devices', вызываем функцию удаления устройств if ($this->view_mode == 'delete_devices') { $this->delete_devices($this->id); $this->redirect("?type=" . gr('type') . '&location_id=' . gr('location_id') . '&group_name=' . gr('group_name')); } } } /** * isHomeBridgeAvailable * * Эта функция проверяет доступность HomeBridge. * Она выполняет запрос к базе данных, чтобы найти объект с названием 'HomeBridge'. * Если такой объект существует, функция возвращает true, иначе false. * * @method bool isHomeBridgeAvailable() * @return bool true если HomeBridge доступна, иначе false * @see module * @since 0.1 */ function isHomeBridgeAvailable() { //return true; // temporary // Выполняем запрос к базе данных, чтобы найти объект с названием 'HomeBridge' $tmp = SQLSelectOne("SELECT ID FROM objects WHERE TITLE='HomeBridge'"); // Если объект найден, возвращаем true if (isset($tmp['ID'])) { return true; } else { // Если объект не найден, возвращаем false return false; } } /** * manage_groups * * Эта функция управляет группами устройств. * Она включает файл для управления группами устройств. * * @method void manage_groups(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function manage_groups(&$out) { // Включаем файл для управления группами устройств require(dirname(__FILE__) . '/devices_manage_groups.inc.php'); } /** * manage_schedule * * Эта функция управляет расписанием устройств. * Она включает файл для управления расписанием устройств. * * @method void manage_schedule(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function manage_schedule(&$out) { // Включаем файл для управления расписанием устройств require(dirname(__FILE__) . '/devices_manage_schedule.inc.php'); } /** * usual * * Эта функция отвечает за обычный режим работы модуля. * Она обрабатывает различные действия, такие как поиск устройств, управление группами, * управление расписанием, редактирование устройств, быстрое редактирование, * удаление устройств и рендеринг структуры. * * @method void usual(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function usual(&$out) { // Получаем значение параметра 'view' из запроса $view = gr('view'); // Если значение параметра 'view' установлено, присваиваем его свойству класса if ($view) $this->view = $view; // Проверяем, является ли текущий запрос AJAX-запросом if ($this->ajax) { // Устанавливаем заголовки ответа header("HTTP/1.0: 200 OK\n"); header('Content-Type: text/html; charset=utf-8'); // Получаем значение параметра 'op' из запроса $op = gr('op'); // Инициализируем массив для хранения результатов $res = array(); // Если значение параметра 'op' равно 'clicked' if ($op == 'clicked') { // Получаем значение параметра 'object' из запроса $object = gr('object'); // Если значение параметра 'object' не пустое if ($object != '') { // Выполняем запрос к базе данных, чтобы найти устройство с соответствующим связанным объектом $device_rec = SQLSelectOne("SELECT ID, TITLE FROM devices WHERE LINKED_OBJECT='" . DBSafe($object) . "'"); // Если устройство найдено if ($device_rec['ID']) { // Обновляем время последнего клика по устройству в базе данных SQLExec("UPDATE devices SET CLICKED=NOW() WHERE ID='" . $device_rec['ID'] . "'"); // Записываем действие в журнал logAction('device_clicked', $device_rec['TITLE']); } } } // Если значение параметра 'op' равно 'get_device' if ($op == 'get_device') { // Получаем значение параметра 'id' из запроса $id = gr('id'); // Обрабатываем устройство с указанным идентификатором и сохраняем результат в массив результатов $res = $this->processDevice($id, $view); } // Если значение параметра 'op' равно 'get_devices' if ($op == 'get_devices') { // Получаем значение параметра 'ids' из запроса $ids = gr('ids'); // Разделяем строку идентификаторов на отдельные элементы $tmp = explode(',', $ids); // Инициализируем массив для хранения результатов $res = array(); // Проходимся по всем идентификаторам foreach ($tmp as $id) { // Если идентификатор пустой, пропускаем текущую итерацию if (!$id) continue; // Обрабатываем устройство с указанным идентификатором $record = $this->processDevice($id, $view); // Если устройство не найдено, пропускаем текущую итерацию if (!$record['DEVICE_ID']) continue; // Добавляем обработанное устройство в массив результатов $res['devices'][] = $record; } } // Если значение параметра 'op' равно 'loadAllDevicesHTML' if ($op == 'loadAllDevicesHTML') { // Выполняем запрос к базе данных, чтобы получить все устройства, которые не являются системными и не архивированы $devices = SQLSelect("SELECT ID, LINKED_OBJECT FROM devices WHERE SYSTEM_DEVICE=0 AND ARCHIVED=0"); // Получаем общее количество устройств $total = count($devices); // Проходимся по всем устройствам for ($i = 0; $i < $total; $i++) { // Если устройство имеет связанный объект if ($devices[$i]['LINKED_OBJECT']) { // Обрабатываем устройство и сохраняем результат в переменную $processed = $this->processDevice($devices[$i]['ID'], $view); // Сохраняем HTML-представление устройства в массиве устройств $devices[$i]['HTML'] = $processed['HTML']; } } // Сохраняем массив устройств в массив результатов $res['DEVICES'] = $devices; } // Выводим результаты в формате JSON и завершаем выполнение скрипта echo json_encode($res); exit; } // Проверяем, является ли текущее действие родительского объекта 'apps' if ($this->owner->action == 'apps') { // Если действие равно 'apps', перенаправляем на страницу модуля устройств //$this->redirect(ROOTHTML."module/devices.html"); } // Получаем значение параметра 'location_id' из запроса $location_id = gr('location_id'); // Получаем значение параметра 'type' из запроса $type = gr('type'); // Получаем значение параметра 'collection' из запроса $collection = gr('collection'); // Инициализируем строку запроса $qry = "1"; // Получаем значение параметра 'linked_object' из запроса $linked_object = gr('linked_object'); // Если значение параметра 'linked_object' установлено if ($linked_object) { // Выполняем запрос к базе данных, чтобы найти устройство с соответствующим связанным объектом $device_rec = SQLSelectOne("SELECT ID FROM devices WHERE LINKED_OBJECT='" . DbSafe($linked_object) . "'"); // Если устройство найдено if ($device_rec['ID']) { // Присваиваем идентификатор устройства свойству класса $this->id = $device_rec['ID']; } } // Генерируем уникальный идентификатор для устройства if (isset($this->id)) { $out['UNIQ'] = uniqid('dev' . $this->id); // Добавляем условие в строку запроса, чтобы выбрать устройство с указанным идентификатором $qry .= " AND devices.ID=" . (int)$this->id; // Устанавливаем флаг, что мы работаем с одним устройством $out['SINGLE_DEVICE'] = 1; // Устанавливаем режим просмотра $out['VIEW'] = $this->view; } // Если установлены параметры 'location_id', 'type' или 'collection' if ($location_id || $type || $collection) { // Инициализируем строку запроса и порядок сортировки $qry = "1 AND SYSTEM_DEVICE=0 AND ARCHIVED=0"; $orderby = 'locations.PRIORITY DESC, LOCATION_ID, TYPE, TITLE'; // Если в параметре 'type' содержится идентификатор локации if (preg_match('/loc(\d+)/', $type, $m)) { // Извлекаем идентификатор локации и очищаем параметр 'type' $location_id = $m[1]; $type = ''; } // Если в параметре 'type' содержится имя коллекции if (preg_match('/col\_(\w+)/', $type, $m)) { // Извлекаем имя коллекции и очищаем параметр 'type' $collection = $m[1]; $type = ''; } // Если установлен параметр 'location_id' if ($location_id) { // Если 'location_id' не равен 'all' if ($location_id != 'all') { // Добавляем условие в строку запроса, чтобы выбрать устройства с указанным идентификатором локации $qry .= " AND devices.LOCATION_ID=" . (int)$location_id; // Выполняем запрос к базе данных, чтобы получить информацию о локации $location = SQLSelectOne("SELECT * FROM locations WHERE ID=" . (int)$location_id); // Сохраняем информацию о локации в массиве вывода foreach ($location as $k => $v) { $out['LOCATION_' . $k] = $v; } // Сохраняем название локации в массиве вывода $out['TITLE'] = $location['TITLE']; } else { // Если 'location_id' равен 'all', устанавливаем соответствующий флаг в массиве вывода $out['LOCATION_ID'] = 'All'; // Добавляем условие в строку запроса, чтобы выбрать все устройства $qry .= " AND 1"; } } // Если установлен параметр 'type' if ($type) { // Если 'type' не равен 'all' if ($type != 'all') { // Добавляем условие в строку запроса, чтобы выбрать устройства с указанным типом $qry .= " AND devices.TYPE LIKE '" . DBSafe($type) . "'"; // Сохраняем название типа устройства в массиве вывода $out['TITLE'] = $this->device_types[$type]['TITLE']; } else { // Если 'type' равен 'all', изменяем порядок сортировки $orderby = 'TYPE, locations.PRIORITY DESC, locations.TITLE, LOCATION_ID, TITLE'; } // Сохраняем тип устройства в массиве вывода $out['TYPE'] = $type; } // Если установлена коллекция if ($collection != '') { // Получаем идентификаторы устройств, входящих в коллекцию $ids = $this->getCollectionIds($collection); // Добавляем условие в строку запроса, чтобы выбрать устройства с указанными идентификаторами $qry .= " AND devices.ID IN (" . implode(',', $ids) . ")"; // Сохраняем название коллекции в массиве вывода $out['TITLE'] = constant('LANG_DEVICES_COLLECTION_' . strtoupper($collection)); // Сохраняем имя коллекции в массиве вывода $out['COLLECTION'] = $collection; } // Инициализируем переменные для хранения названий локации и типа устройства $location_title = ''; $type_title = ''; // Выполняем запрос к базе данных, чтобы получить устройства, соответствующие условиям запроса $devices = SQLSelect("SELECT devices.*, locations.TITLE as LOCATION_TITLE FROM devices LEFT JOIN locations ON devices.LOCATION_ID=locations.ID WHERE $qry ORDER BY $orderby"); // Получаем общее количество устройств $total = count($devices); // Проходимся по всем устройствам for ($i = 0; $i < $total; $i++) { // Если тип устройства равен 'all' if ($type == 'all') { // Получаем название типа устройства $devices[$i]['LOCATION_TITLE'] = $this->device_types[$devices[$i]['TYPE']]['TITLE']; // Если название типа устройства отличается от предыдущего if ($devices[$i]['LOCATION_TITLE'] != $location_title) { // Устанавливаем флаг, что это новая локация $devices[$i]['NEW_LOCATION'] = 1; // Сохраняем текущее название типа устройства $location_title = $devices[$i]['LOCATION_TITLE']; } } else { // Если название типа устройства отличается от предыдущего и не установлено название локации в массиве вывода if ($devices[$i]['LOCATION_TITLE'] != $location_title && !isset($out['LOCATION_TITLE'])) { // Устанавливаем флаг, что это новая локация $devices[$i]['NEW_LOCATION'] = 1; // Сохраняем текущее название типа устройства $location_title = $devices[$i]['LOCATION_TITLE']; } // Если название типа устройства отличается от предыдущего if ($this->device_types[$devices[$i]['TYPE']]['TITLE'] != $type_title) { // Сохраняем текущее название типа устройства $type_title = $this->device_types[$devices[$i]['TYPE']]['TITLE']; // Устанавливаем флаг, что это новый тип устройства $devices[$i]['NEW_TYPE'] = 1; } } } else { // Устанавливаем порядок сортировки $orderby = 'locations.PRIORITY DESC, locations.TITLE, LOCATION_ID, TYPE, TITLE'; // Добавляем условие в строку запроса, чтобы выбрать все устройства, которые не являются системными и не архивированы $qry .= " AND SYSTEM_DEVICE=0 AND ARCHIVED=0"; // Устанавливаем флаг, что мы работаем со всеми устройствами $out['ALL_DEVICES'] = 1; // Выполняем запрос к базе данных, чтобы получить все устройства, которые не являются системными и не архивированы $devices = SQLSelect("SELECT devices.*, locations.TITLE as LOCATION_TITLE FROM devices LEFT JOIN locations ON devices.LOCATION_ID=locations.ID WHERE $qry ORDER BY $orderby"); // Выполняем запрос к базе данных, чтобы получить недавно использованные устройства $recent_devices = SQLSelect("SELECT devices.* FROM devices WHERE !IsNull(CLICKED) ORDER BY CLICKED DESC LIMIT 10"); } // Если устройства найдены if ($devices[0]['ID']) { // Если установлены параметры 'location_id', 'type' или 'collection' if ($location_id || $type || 1) { // Получаем общее количество устройств $total = count($devices); // Проходимся по всем устройствам for ($i = 0; $i < $total; $i++) { // Если устройство имеет связанный объект if ($devices[$i]['LINKED_OBJECT']) { // Обрабатываем устройство и сохраняем результат в переменную $processed = $this->processDevice($devices[$i]['ID'], $this->view); // Сохраняем HTML-представление устройства в массиве устройств $devices[$i]['HTML'] = $processed['HTML']; } } } // Сохраняем массив устройств в массиве вывода $out['DEVICES'] = $devices; // Если свойство класса 'id' установлено if ($this->id) { // Завершаем выполнение функции return; } } // Выполняем запрос к базе данных, чтобы получить все локации, упорядоченные по приоритету и названию $locations = SQLSelect("SELECT ID, TITLE FROM locations ORDER BY PRIORITY DESC, TITLE"); // Получаем общее количество устройств $total_devices = count($devices); // Если устройства найдены if ($total_devices) { // Инициализируем массивы для хранения избранных, предупреждающих и проблемных устройств $favorite_devices = array(); $warning_devices = array(); $problem_devices = array(); // Проходимся по всем устройствам for ($idv = 0; $idv < $total_devices; $idv++) { // Если устройство помечено как избранное if ($devices[$idv]['FAVORITE']) { // Добавляем устройство в массив избранных устройств $favorite_devices[] = $devices[$idv]; } elseif ($devices[$idv]['LINKED_OBJECT']) { // Если устройство имеет связанный объект // Проверяем, является ли устройство предупреждающим if ( gg($devices[$idv]['LINKED_OBJECT'] . '.normalValue') == '0' && gg($devices[$idv]['LINKED_OBJECT'] . '.notify') == '1' ) { // Добавляем устройство в массив предупреждающих устройств $warning_devices[] = $devices[$idv]; // Устанавливаем флаг, что это новая секция $warning_devices[0]['NEW_SECTION'] = 1; // Устанавливаем название секции $warning_devices[0]['SECTION_TITLE'] = LANG_WARNING; } elseif ( // Проверяем, является ли устройство проблемным ($devices[$idv]['TYPE'] == 'motion' || $devices[$idv]['TYPE'] == 'openclose' || $devices[$idv]['TYPE'] == 'leak' || $devices[$idv]['TYPE'] == 'smoke' || $devices[$idv]['TYPE'] == 'counter' || preg_match('/^sensor/', $devices[$idv]['TYPE']) || $this->device_types[$devices[$idv]['TYPE']]['PARENT_CLASS'] == 'SSensors' || (int)gg($devices[$idv]['LINKED_OBJECT'] . '.aliveTimeout') > 0 ) && gg($devices[$idv]['LINKED_OBJECT'] . '.alive') === '0' ) { // Добавляем устройство в массив проблемных устройств $problem_devices[] = $devices[$idv]; // Устанавливаем флаг, что это новая секция $problem_devices[0]['NEW_SECTION'] = 1; // Устанавливаем название секции $problem_devices[0]['SECTION_TITLE'] = LANG_OFFLINE; } } } // Если в массиве избранных устройств есть элементы if (count($favorite_devices) > 0) { // Сортируем массив избранных устройств по значению 'FAVORITE' usort($favorite_devices, function ($a, $b) { if ($a['FAVORITE'] == $b['FAVORITE']) { return 0; } return ($a['FAVORITE'] > $b['FAVORITE']) ? -1 : 1; }); } // Добавляем предупреждающие устройства в массив избранных устройств foreach ($warning_devices as $device) { $favorite_devices[] = $device; } // Если в массиве недавно использованных устройств есть элементы if (isset($recent_devices[0]['ID'])) { // Устанавливаем флаг, что это новая секция $recent_devices[0]['NEW_SECTION'] = 1; // Устанавливаем название секции $recent_devices[0]['SECTION_TITLE'] = LANG_RECENTLY_USED; // Проходимся по всем недавно использованным устройствам foreach ($recent_devices as &$device) { // Если устройство имеет связанный объект if ($device['LINKED_OBJECT']) { // Обрабатываем устройство и сохраняем результат в переменную $processed = $this->processDevice($device['ID']); // Сохраняем HTML-представление устройства в массиве устройств $device['HTML'] = $processed['HTML']; } // Добавляем устройство в массив избранных устройств $favorite_devices[] = $device; } } // Добавляем проблемные устройства в массив избранных устройств foreach ($problem_devices as $device) { $favorite_devices[] = $device; } // Получаем общее количество устройств в массиве избранных устройств $devices_count = count($favorite_devices); // Если в массиве избранных устройств есть элементы if ($devices_count > 0) { // Создаем запись для секции избранных устройств $loc_rec = array(); $loc_rec['ID'] = 0; $loc_rec['TITLE'] = LANG_FAVORITES; $loc_rec['DEVICES'] = $favorite_devices; $loc_rec['DEVICES_TOTAL'] = $devices_count; // Добавляем запись в начало массива локаций array_unshift($locations, $loc_rec); } // Получаем общее количество локаций $total = count($locations); // Проходимся по всем локациям for ($i = 0; $i < $total; $i++) { // Если у локации есть идентификатор if ($locations[$i]['ID']) { // Инициализируем счетчик устройств $devices_count = 0; // Если есть устройства if ($total_devices) { // Проходимся по всем устройствам for ($idv = 0; $idv < $total_devices; $idv++) { // Если идентификатор локации устройства совпадает с идентификатором текущей локации if ($devices[$idv]['LOCATION_ID'] == $locations[$i]['ID']) { // Увеличиваем счетчик устройств $devices_count++; // Добавляем устройство в массив устройств текущей локации $locations[$i]['DEVICES'][] = $devices[$idv]; } } } // Сохраняем общее количество устройств в массиве локации $locations[$i]['DEVICES_TOTAL'] = $devices_count; } // Устанавливаем индекс текущей локации $locations[$i]['INDEX'] = $i; } // Сохраняем массив локаций в массиве вывода $out['GROUPS'] = $locations; // Инициализируем массив для хранения типов устройств $types = array(); // Если свойство класса 'device_types' является массивом if (is_array($this->device_types)) { // Проходимся по всем типам устройств foreach ($this->device_types as $k => $v) { // Если у типа устройства есть название if (isset($v['TITLE'])) { // Создаем запись для типа устройства $type_rec = array('NAME' => $k, 'TITLE' => $v['TITLE']); // Выполняем запрос к базе данных, чтобы получить количество устройств данного типа $tmp = SQLSelectOne("SELECT COUNT(*) AS TOTAL FROM devices WHERE SYSTEM_DEVICE=0 AND ARCHIVED=0 AND TYPE='" . $k . "'"); // Сохраняем количество устройств в записи $type_rec['TOTAL'] = (int)$tmp['TOTAL']; // Если количество устройств больше нуля if ($type_rec['TOTAL'] > 0) { // Добавляем запись в массив типов устройств $types[] = $type_rec; } } } // Сортируем массив типов устройств по названию usort($types, function ($a, $b) { return strcmp($a["TITLE"], $b["TITLE"]); }); } // Обрабатываем коллекции устройств $col_name = 'is_heating'; $col_ids = $this->getCollectionIds($col_name); $col_total = count($col_ids) - 1; $col = array('NAME' => 'col_' . $col_name, 'TITLE' => LANG_DEVICES_COLLECTION_IS_HEATING, 'TOTAL' => $col_total); if ($col_total > 0) array_unshift($types, $col); $col_name = 'is_on'; $col_ids = $this->getCollectionIds($col_name); $col_total = count($col_ids) - 1; $col = array('NAME' => 'col_' . $col_name, 'TITLE' => LANG_DEVICES_COLLECTION_IS_ON, 'TOTAL' => $col_total); if ($col_total > 0) array_unshift($types, $col); $col_name = 'is_open'; $col_ids = $this->getCollectionIds($col_name); $col_total = count($col_ids) - 1; $col = array('NAME' => 'col_' . $col_name, 'TITLE' => LANG_DEVICES_COLLECTION_IS_OPEN, 'TOTAL' => $col_total); if ($col_total > 0) array_unshift($types, $col); // Обрабатываем локации $list_locations = $locations; if (is_array($list_locations)) { // Сортируем массив локаций по названию usort($list_locations, function ($a, $b) { return strcmp($a["TITLE"], $b["TITLE"]); }); // Добавляем запись для локаций в массив типов устройств $types[] = array('NAME' => '', 'TITLE' => LANG_LOCATION); // Проходимся по всем локациям foreach ($list_locations as $location) { // Если название локации не равно 'Избранное' if ($location['TITLE'] == LANG_FAVORITES) continue; // Добавляем запись для локации в массив типов устройств $types[] = array('NAME' => 'loc' . $location['ID'], 'TITLE' => $location['TITLE'], 'TOTAL' => $location['DEVICES_TOTAL']); } } // Сохраняем массив типов устройств в массиве вывода $out['TYPES'] = $types; /** * getCollectionIds * * Эта функция возвращает идентификаторы устройств, которые входят в указанную коллекцию. * Она обрабатывает различные коллекции, такие как 'is_on', 'is_open', 'is_heating'. * * @method array getCollectionIds($collection) * @param string $collection Название коллекции * @return array Идентификаторы устройств, которые входят в указанную коллекцию * @see module * @since 0.1 */ function getCollectionIds($collection) { $ids = array(0); if ($collection == 'is_on') { // Выполняем запрос к базе данных, чтобы получить устройства типа 'relay', 'dimmer', 'rgb' $devices = SQLSelect("SELECT ID, LINKED_OBJECT FROM devices WHERE devices.TYPE IN ('relay','dimmer','rgb')"); // Проходимся по всем устройствам foreach ($devices as $device) { // Если статус устройства равен 'on' if (gg($device['LINKED_OBJECT'] . '.status')) { // Добавляем идентификатор устройства в массив идентификаторов $ids[] = $device['ID']; } } } elseif ($collection == 'is_open') { // Выполняем запрос к базе данных, чтобы получить устройства типа 'openable', 'openclose' $devices = SQLSelect("SELECT ID, LINKED_OBJECT FROM devices WHERE devices.TYPE IN ('openable','openclose')"); // Проходимся по всем устройствам foreach ($devices as $device) { // Если статус устройства равен 'off' if (!gg($device['LINKED_OBJECT'] . '.status')) { // Добавляем идентификатор устройства в массив идентификаторов $ids[] = $device['ID']; } } } elseif ($collection == 'is_heating') { // Выполняем запрос к базе данных, чтобы получить устройства типа 'thermostat' $devices = SQLSelect("SELECT ID, LINKED_OBJECT FROM devices WHERE devices.TYPE IN ('thermostat')"); // Проходимся по всем устройствам foreach ($devices as $device) { // Если статус устройства равен 'on' if (gg($device['LINKED_OBJECT'] . '.relay_status')) { // Добавляем идентификатор устройства в массив идентификаторов $ids[] = $device['ID']; } } } // Возвращаем массив идентификаторов устройств return $ids; } /** * devices search * * Функция для поиска устройств. * Она включает в себя логику поиска, определенную в файле 'devices_search.inc.php'. * * @method void devices_search(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function search_devices(&$out) { // Включаем файл 'devices_search.inc.php', который содержит логику поиска устройств require(dirname(__FILE__) . '/devices_search.inc.php'); } /** * devices edit/add * * Функция для редактирования или добавления устройств. * Она включает в себя логику редактирования/добавления, * определенную в файле 'devices_edit.inc.php'. * * @method void edit_devices(&$out, $id) * @param array $out Массив для вывода данных * @param int $id Идентификатор устройства для редактирования (0 для добавления нового устройства) * @return void * @see module * @since 0.1 */ function edit_devices(&$out, $id) { // Включаем файл 'devices_edit.inc.php', который содержит логику редактирования/добавления устройств require(dirname(__FILE__) . '/devices_edit.inc.php'); } /** * quick_edit * * Функция для быстрого редактирования устройств. * Она включает в себя логику быстрого редактирования, определенную в файле 'devices_quick_edit.inc.php'. * * @method void quick_edit(&$out) * @param array $out Массив для вывода данных * @return void * @see module * @since 0.1 */ function quick_edit(&$out) { // Включаем файл 'devices_quick_edit.inc.php', который содержит логику быстрого редактирования устройств require(dirname(__FILE__) . '/devices_quick_edit.inc.php'); } /** * devices delete record * * Функция для удаления записи об устройстве. * Она также выполняет дополнительные действия, связанные с удалением устройства, такие как удаление связанных элементов, объектов и команд. * * @method void delete_devices($id) * @param int $id Идентификатор устройства для удаления * @return void * @see module * @since 0.1 */ function delete_devices($id) { // Получаем запись об устройстве по идентификатору $rec = SQLSelectOne("SELECT * FROM devices WHERE ID='$id'"); // Подготовка данных для удаления устройства из HomeBridge $payload = array(); $payload['name'] = $rec['LINKED_OBJECT']; sg('HomeBridge.to_remove', json_encode($payload)); // Удаление связанных элементов $elements = SQLSelect("SELECT * FROM elements WHERE `SYSTEM`='sdevice" . $rec['ID'] . "'"); $total = count($elements); for ($i = 0; $i < $total; $i++) { SQLExec("DELETE FROM elm_states WHERE ELEMENT_ID=" . $elements[$i]['ID']); SQLExec("DELETE FROM elements WHERE ID=" . $elements[$i]['ID']); } // Удаление связанных объектов $objects = SQLSelect("SELECT ID FROM objects WHERE `SYSTEM`='sdevice" . $rec['ID'] . "'"); $total = count($objects); for ($i = 0; $i < $total; $i++) { deleteObject($objects[$i]['ID']); } // Удаление связанных команд $tables = array('commands'); $total = count($tables); for ($i = 0; $i < $total; $i++) { SQLExec("DELETE FROM " . $tables[$i] . " WHERE `SYSTEM`='sdevice" . $rec['ID'] . "'"); } // Удаление связей устройств SQLExec("DELETE FROM devices_linked WHERE DEVICE1_ID='" . $rec['ID'] . "' OR DEVICE2_ID='" . $rec['ID'] . "'"); // Удаление самого устройства SQLExec("DELETE FROM devices WHERE ID='" . $rec['ID'] . "'"); } /** * addDevice * * Функция для добавления нового устройства. * Она принимает тип устройства и опции для настройки устройства, такие как связанный объект, название, локация и другие параметры. * * @method int addDevice($device_type, $options = 0) * @param string $device_type Тип устройства * @param array $options Опции для настройки устройства * @return int Идентификатор добавленного устройства или 0 в случае ошибки * @see module * @since 0.1 */ function addDevice($device_type, $options = 0) { // Устанавливаем словарь устройств $this->setDictionary(); // Получаем детали типа устройства $type_details = $this->getTypeDetails($device_type); // Если опции не являются массивом, инициализируем их как пустой массив if (!is_array($options)) { $options = array(); } // Если тип устройства не определен в словаре устройств, возвращаем 0 if (!is_array($this->device_types[$device_type])) { return 0; } // Если указаны параметры для связанной таблицы и идентификатор записи в таблице if ($options['TABLE'] && $options['TABLE_ID']) { // Получаем запись из указанной таблицы $table_rec = SQLSelectOne("SELECT * FROM " . $options['TABLE'] . " WHERE ID=" . $options['TABLE_ID']); // Если запись не найдена, возвращаем 0 if (!$table_rec['ID']) { return 0; } } // Если указано связанное устройство if ($options['LINKED_OBJECT'] != '') { // Проверяем, существует ли уже устройство с таким связанным объектом $old_device = SQLSelectOne("SELECT ID FROM devices WHERE LINKED_OBJECT LIKE '" . DBSafe($options['LINKED_OBJECT']) . "'"); // Если устройство найдено, возвращаем его идентификатор if ($old_device['ID']) return $old_device['ID']; // Иначе, сохраняем связанный объект в записи $rec['LINKED_OBJECT'] = $options['LINKED_OBJECT']; } // Инициализируем запись для нового устройства $rec = array(); $rec['TYPE'] = $device_type; // Если указано название, сохраняем его в записи if ($options['TITLE']) { $rec['TITLE'] = $options['TITLE']; } else { // Иначе, устанавливаем название по умолчанию $rec['TITLE'] = 'New device ' . date('H:i'); } // Если указана локация, сохраняем ее в записи if ($options['LOCATION_ID']) { $rec['LOCATION_ID'] = $options['LOCATION_ID']; } // Добавляем запись в базу данных и сохраняем идентификатор нового устройства $rec['ID'] = SQLInsert('devices', $rec); // Если указана локация, получаем название локации if ($rec['LOCATION_ID']) { $location_title = getRoomObjectByLocation($rec['LOCATION_ID'], 1); } // Если не указан связанный объект, создаем новый объект для устройства if (!$rec['LINKED_OBJECT']) { $prefix = ucfirst($rec['TYPE']); $new_object_title = $prefix . $this->getNewObjectIndex($type_details['CLASS']); $object_id = addClassObject($type_details['CLASS'], $new_object_title, 'sdevice' . $rec['ID']); $rec['LINKED_OBJECT'] = $new_object_title; // Если название устройства по умолчанию, обновляем его на название связанного объекта if (preg_match('/New device .+/', $rec['TITLE'])) { $rec['TITLE'] = $rec['LINKED_OBJECT']; } // Обновляем запись в базе данных SQLUpdate('devices', $rec); } // Если указаны параметры для связанной таблицы и идентификатор записи в таблице if ($table_rec['ID']) { // Добавляем устройство в связанную таблицу $this->addDeviceToSourceTable($options['TABLE'], $table_rec['ID'], $rec['ID']); } // Если указано добавление устройства в меню if ($options['ADD_MENU']) { // Добавляем устройство в меню $this->addDeviceToMenu($rec['ID']); } // Если указано добавление устройства в сцену if ($options['ADD_SCENE']) { // Добавляем устройство в сцену $this->addDeviceToScene($rec['ID']); } // Возвращаем 1, указывая на успешное добавление устройства return 1; } /** * addDeviceToSourceTable * * Функция для добавления устройства в связанную таблицу. * Она принимает имя таблицы, идентификатор записи в таблице и идентификатор устройства. * Функция определяет свойства и методы связанного объекта устройства в зависимости от его типа и обновляет запись в таблице. * * @method void addDeviceToSourceTable($table_name, $table_id, $device_id) * @param string $table_name Имя таблицы * @param int $table_id Идентификатор записи в таблице * @param int $device_id Идентификатор устройства * @return void * @see module * @since 0.1 */ function addDeviceToSourceTable($table_name, $table_id, $device_id) { // Получаем запись об устройстве по идентификатору $rec = SQLSelectOne("SELECT * FROM devices WHERE ID=" . (int)$device_id); // Устанавливаем словарь устройств $this->setDictionary(); // Получаем детали типа устройства $type_details = $this->getTypeDetails($rec['TYPE']); // Если у устройства нет связанного объекта, возвращаем 0 if (!$rec['LINKED_OBJECT']) { return 0; } // Получаем запись из указанной таблицы $table_rec = SQLSelectOne("SELECT * FROM $table_name WHERE ID=" . DBSafe($table_id)); // Если запись не найдена, возвращаем 0 if (!$table_rec['ID']) { return 0; } // Инициализируем переменные для связанного объекта, свойства и метода $linked_object = $rec['LINKED_OBJECT']; $linked_property = ''; $linked_method = ''; // Определяем свойство и метод в зависимости от класса родителя устройства if ($type_details['PARENT_CLASS'] == 'SSensors') { $linked_property = 'value'; } elseif ($type_details['PARENT_CLASS'] == 'SControllers') { $linked_property = 'status'; } // Определяем свойство в зависимости от типа устройства if ($rec['TYPE'] == 'dimmer') { $linked_property = 'level'; } if ($rec['TYPE'] == 'rgb') { $linked_property = 'color'; } if ($rec['TYPE'] == 'motion') { $linked_property = 'status'; $linked_method = 'motionDetected'; } if ($rec['TYPE'] == 'button') { $linked_property = 'status'; $linked_method = 'pressed'; } if ($rec['TYPE'] == 'switch' || $rec['TYPE'] == 'openclose') { $linked_property = 'status'; } // Если запись найдена, обновляем ее с новыми значениями связанного объекта, свойства и метода if ($table_rec['ID']) { $table_rec['LINKED_OBJECT'] = $linked_object; $table_rec['LINKED_PROPERTY'] = $linked_property; $table_rec['LINKED_METHOD'] = $linked_method; SQLUpdate($table_name, $table_rec); } } /** * addDeviceToMenu * * Функция для добавления устройства в меню. * Она принимает идентификатор устройства и идентификатор родительского элемента меню (необязательно). * Функция определяет тип элемента меню в зависимости от типа устройства и обновляет или создает запись в таблице команд. * * @method void addDeviceToMenu($device_id, $parent_id = 0) * @param int $device_id Идентификатор устройства * @param int $parent_id Идентификатор родительского элемента меню (необязательно) * @return void * @see module * @since 0.1 */ function addDeviceToMenu($device_id, $add_menu_id = 0) { // Получаем запись об устройстве по идентификатору $rec = SQLSelectOne("SELECT * FROM devices WHERE ID=" . (int)$device_id); // Если запись не найдена, возвращаем 0 if (!$rec['ID']) { return 0; } // Получаем запись из таблицы команд, связанную с устройством $menu_rec = SQLSelectOne("SELECT * FROM commands WHERE `SYSTEM`='" . 'sdevice' . $rec['ID'] . "'"); // Если запись не найдена, инициализируем новую запись if (!$menu_rec['ID']) { $menu_rec = array(); } // Если заголовок не указан, используем название устройства if (!$menu_rec['TITLE']) { $menu_rec['TITLE'] = $rec['TITLE']; } // Устанавливаем идентификатор родительского элемента меню $menu_rec['PARENT_ID'] = (int)$add_menu_id; // Устанавливаем системный идентификатор $menu_rec['SYSTEM'] = 'sdevice' . $rec['ID']; // Устанавливаем связанный объект $menu_rec['LINKED_OBJECT'] = $rec['LINKED_OBJECT']; // Определяем тип элемента меню и связанное свойство в зависимости от типа устройства if ($rec['TYPE'] == 'relay' || $rec['TYPE'] == 'switch') { $menu_rec['TYPE'] = 'switch'; $menu_rec['LINKED_PROPERTY'] = 'status'; $menu_rec['CUR_VALUE'] = getGlobal($menu_rec['LINKED_OBJECT'] . '.' . $menu_rec['LINKED_PROPERTY']); } elseif ($rec['TYPE'] == 'button') { $menu_rec['TYPE'] = 'button'; $menu_rec['LINKED_PROPERTY'] = ''; $menu_rec['ONCHANGE_METHOD'] = 'pressed'; } elseif ($rec['TYPE'] == 'dimmer') { $menu_rec['TYPE'] = 'sliderbox'; $menu_rec['MIN_VALUE'] = '0'; $menu_rec['MAX_VALUE'] = '100'; $menu_rec['STEP_VALUE'] = '1'; $menu_rec['LINKED_PROPERTY'] = 'level'; $menu_rec['CUR_VALUE'] = getGlobal($menu_rec['LINKED_OBJECT'] . '.' . $menu_rec['LINKED_PROPERTY']); } else { $menu_rec['TYPE'] = 'object'; } // Если запись уже существует, обновляем ее if ($menu_rec['ID']) { SQLUpdate('commands', $menu_rec); } else { // Иначе, создаем новую запись $menu_rec['ID'] = SQLInsert('commands', $menu_rec); } // Возвращаем идентификатор созданного или обновленного элемента меню return $menu_rec['ID']; } /** * addDeviceToScene * * Функция для добавления устройства в сцену. * Она принимает идентификатор устройства и идентификатор сцены (необязательно). * Функция определяет тип элемента сцены в зависимости от типа устройства и обновляет или создает запись в таблице элементов. * * @method void addDeviceToScene($device_id, $scene_id = 0) * @param int $device_id Идентификатор устройства * @param int $scene_id Идентификатор сцены (необязательно) * @return void * @see module * @since 0.1 */ function addDeviceToScene($device_id, $add_scene_id = 0) { // Получаем запись об устройстве по идентификатору $rec = SQLSelectOne("SELECT * FROM devices WHERE ID=" . (int)$device_id); // Если запись не найдена, возвращаем 0 if (!$rec['ID']) { return 0; } // Если идентификатор сцены не указан, получаем первую сцену из базы данных if (!$add_scene_id) { $scene_rec = SQLSelectOne("SELECT ID FROM scenes ORDER BY ID LIMIT 1"); // Если сцена найдена, используем ее идентификатор if ($scene_rec['ID']) { $add_scene_id = $scene_rec['ID']; } else { return 0; } } // Получаем запись из таблицы элементов, связанную с устройством и сценой $element_rec = SQLSelectOne("SELECT * FROM elements WHERE SCENE_ID=" . (int)$add_scene_id . " AND `SYSTEM`='" . 'sdevice' . $rec['ID'] . "'"); // Если запись не найдена, инициализируем новую запись и данные мастера if (!$element_rec['ID']) { $element_rec = array(); $wizard_data = array(); } else { // Иначе, декодируем данные мастера из записи $wizard_data = json_decode($element_rec['WIZARD_DATA'], true); } // Устанавливаем идентификатор сцены $element_rec['SCENE_ID'] = (int)$add_scene_id; // Устанавливаем системный идентификатор $element_rec['SYSTEM'] = 'sdevice' . $rec['ID']; // Если координаты не указаны, задаем случайные значения if (!$element_rec['TOP'] && !$element_rec['LEFT']) { $element_rec['TOP'] = 10 + rand(0, 300); $element_rec['LEFT'] = 10 + rand(0, 300); } // Если стиль CSS не указан, используем стиль по умолчанию if (!$element_rec['CSS_STYLE']) { $element_rec['CSS_STYLE'] = 'default'; } // Кодируем данные мастера обратно в JSON $element_rec['WIZARD_DATA'] = json_encode($wizard_data) . ''; // Устанавливаем флаг фона $element_rec['BACKGROUND'] = 0; // Устанавливаем связанный объект $element_rec['LINKED_OBJECT'] = $rec['LINKED_OBJECT']; // Устанавливаем название $element_rec['TITLE'] = $rec['TITLE']; // Устанавливаем флаг легкой конфигурации $element_rec['EASY_CONFIG'] = 1; // Инициализируем единицу измерения связанного свойства $linked_property_unit = ''; // Устанавливаем тип элемента сцены $element_rec['TYPE'] = 'device'; // Устанавливаем идентификатор устройства $element_rec['DEVICE_ID'] = $rec['ID']; /* // Закомментированный код для определения типа элемента сцены и связанного свойства в зависимости от типа устройства if ($rec['TYPE']=='relay' || $rec['TYPE']=='dimmer' || $rec['TYPE']=='switch') { $element_rec['TYPE'] = 'switch'; $element_rec['LINKED_PROPERTY'] = 'status'; } elseif ($rec['TYPE']=='button') { $element_rec['TYPE'] = 'button'; } elseif ($rec['TYPE']=='motion') { $element_rec['TYPE'] = 'warning'; $element_rec['LINKED_PROPERTY'] = 'status'; $element_rec['CSS_STYLE']='motion'; } elseif ($rec['TYPE']=='sensor_temp') { $element_rec['CSS_STYLE']='temp'; $linked_property_unit='°C'; } elseif ($rec['TYPE']=='sensor_humidity') { $element_rec['CSS_STYLE']='humidity'; $linked_property_unit='%'; } else { $element_rec['TYPE']='object'; } // Дополнительная логика для типов датчиков температуры и влажности if ($rec['TYPE']=='sensor_temp' || $rec['TYPE']=='sensor_humidity') { $element_rec['TYPE'] = 'informer'; $element_rec['LINKED_PROPERTY'] = 'value'; $wizard_data['STATE_HIGH']=1; $wizard_data['STATE_HIGH_VALUE']='%'.$element_rec['LINKED_OBJECT'].'.maxValue%'; $wizard_data['STATE_LOW']=1; $wizard_data['STATE_LOW_VALUE']='%'.$element_rec['LINKED_OBJECT'].'.maxValue%'; $wizard_data['UNIT']=$linked_property_unit; } */ // Кодируем данные мастера обратно в JSON $element_rec['WIZARD_DATA'] = json_encode($wizard_data); // Если запись уже существует, обновляем ее if ($element_rec['ID']) { SQLUpdate('elements', $element_rec); } else { // Иначе, создаем новую запись $element_rec['ID'] = SQLInsert('elements', $element_rec); // Получаем связанный объект $linked_object = $rec['LINKED_OBJECT']; // Если тип элемента сцены - переключатель if ($element_rec['TYPE'] == 'switch') { // Создаем запись состояния для 'off' $state_rec = array(); $state_rec['TITLE'] = 'off'; $state_rec['HTML'] = $element_rec['TITLE']; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['IS_DYNAMIC'] = 1; $state_rec['LINKED_OBJECT'] = $rec['LINKED_OBJECT'] . ''; $state_rec['LINKED_PROPERTY'] = 'status'; $state_rec['CONDITION'] = 4; $state_rec['CONDITION_VALUE'] = 1; $state_rec['ACTION_OBJECT'] = $rec['LINKED_OBJECT'] . ''; $state_rec['ACTION_METHOD'] = 'turnOn'; $state_rec['ID'] = SQLInsert('elm_states', $state_rec); // Создаем запись состояния для 'on' $state_rec = array(); $state_rec['TITLE'] = 'on'; $state_rec['HTML'] = $element_rec['TITLE']; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['IS_DYNAMIC'] = 1; $state_rec['LINKED_OBJECT'] = $rec['LINKED_OBJECT'] . ''; $state_rec['LINKED_PROPERTY'] = 'status'; $state_rec['CONDITION'] = 1; $state_rec['CONDITION_VALUE'] = 1; $state_rec['ACTION_OBJECT'] = $rec['LINKED_OBJECT'] . ''; $state_rec['ACTION_METHOD'] = 'turnOff'; $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } elseif ($element_rec['TYPE'] == 'warning') { // Создаем запись состояния для 'default' $state_rec = array(); $state_rec['TITLE'] = 'default'; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['HTML'] = $element_rec['TITLE'] . '<br/>%' . $rec['LINKED_OBJECT'] . '.updatedText%'; $state_rec['LINKED_OBJECT'] = $rec['LINKED_OBJECT'] . ''; $state_rec['LINKED_PROPERTY'] = 'status'; $state_rec['IS_DYNAMIC'] = 1; $state_rec['CONDITION'] = 1; $state_rec['CONDITION_VALUE'] = 1; $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } elseif ($element_rec['TYPE'] == 'informer') { // Устанавливаем связанное свойство $linked_property = 'value'; // Устанавливаем флаги для высокого и низкого состояний $state_high = 1; $state_high_value = '%' . $linked_object . '.maxValue%'; // Если флаг высокого состояния установлен if ($state_high) { // Создаем запись состояния для 'high' $state_rec = array(); $state_rec['TITLE'] = 'high'; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['HTML'] = '%' . $linked_object . '.' . $linked_property . '%'; // Если установлена единица измерения, добавляем ее к HTML if ($linked_property_unit) { $state_rec['HTML'] .= ' ' . $linked_property_unit; } $state_rec['LINKED_OBJECT'] = $linked_object . ''; $state_rec['LINKED_PROPERTY'] = $linked_property . ''; $state_rec['IS_DYNAMIC'] = 1; // Если установлено значение высокого состояния, добавляем его к записи if ($state_high_value) { $state_rec['CONDITION'] = 2; $state_rec['CONDITION_VALUE'] = $state_high_value; } $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } // Устанавливаем флаги для низкого состояния $state_low = 1; $state_low_value = '%' . $linked_object . '.minValue%'; // Если флаг низкого состояния установлен if ($state_low) { // Создаем запись состояния для 'low' $state_rec = array(); $state_rec['TITLE'] = 'low'; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['HTML'] = '%' . $linked_object . '.' . $linked_property . '%'; // Если установлена единица измерения, добавляем ее к HTML if ($linked_property_unit) { $state_rec['HTML'] .= ' ' . $linked_property_unit; } $state_rec['LINKED_OBJECT'] = $linked_object . ''; $state_rec['LINKED_PROPERTY'] = $linked_property . ''; $state_rec['IS_DYNAMIC'] = 1; // Если установлено значение низкого состояния, добавляем его к записи if ($state_low_value) { $state_rec['CONDITION'] = 3; $state_rec['CONDITION_VALUE'] = $state_low_value; } $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } // Создаем запись состояния для 'default' $state_rec = array(); $state_rec['TITLE'] = 'default'; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['HTML'] = '%' . $linked_object . '.' . $linked_property . '%'; // Если установлена единица измерения, добавляем ее к HTML if ($linked_property_unit) { $state_rec['HTML'] .= ' ' . $linked_property_unit; } // Если установлены флаги высокого или низкого состояния if ($state_high || $state_low) { $state_rec['IS_DYNAMIC'] = 1; $state_rec['LINKED_OBJECT'] = $linked_object . ''; $state_rec['LINKED_PROPERTY'] = $linked_property . ''; // Если установлены оба флага, устанавливаем is_dynamic в 2 и добавляем условие if ($state_high && $state_low) { $state_rec['IS_DYNAMIC'] = 2; $state_rec['CONDITION_ADVANCED'] = 'if (gg(\'' . $linked_object . '.' . $linked_property . '\')>=gg(\'' . $linked_object . '.minValue\') && gg(\'' . $linked_object . '.' . $linked_property . '\')<=gg(\'' . $linked_object . '.maxValue\')) {' . "\n " . '$display=1;' . "\n" . '} else {' . "\n " . '$display=0;' . "\n" . '}'; } elseif ($state_high) { $state_rec['IS_DYNAMIC'] = 1; $state_rec['CONDITION'] = 3; $state_rec['CONDITION_VALUE'] = $state_high_value; } elseif ($state_low) { $state_rec['IS_DYNAMIC'] = 1; $state_rec['CONDITION'] = 2; $state_rec['CONDITION_VALUE'] = $state_low_value; } } $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } elseif ($element_rec['TYPE'] == 'button') { // Устанавливаем метод связанного объекта $linked_method = 'pressed'; // Создаем запись состояния для 'default' $state_rec = array(); $state_rec['TITLE'] = 'default'; $state_rec['ELEMENT_ID'] = $element_rec['ID']; $state_rec['HTML'] = $element_rec['TITLE']; // Если установлены связанный объект и метод, добавляем их к записи if ($linked_object && $linked_method) { $state_rec['ACTION_OBJECT'] = $linked_object; $state_rec['ACTION_METHOD'] = $linked_method; } $state_rec['ID'] = SQLInsert('elm_states', $state_rec); } } } /** * checkLinkedDevicesAction * * Функция для проверки действий связанных устройств. * Она принимает название связанного объекта и значение (необязательно). * Функция выполняет поиск устройств, связанных с указанным объектом, и включает файл с действиями связанных устройств. * * @method void checkLinkedDevicesAction($object_name, $value = '') * @param string $object_name Название связанного объекта * @param string $value Значение (необязательно) * @return void * @see module * @since 0.1 */ function checkLinkedDevicesAction($object_title, $value = 0) { // Начинаем измерение времени выполнения функции startMeasure('checkLinkedDevicesAction'); // Получаем запись об устройстве, связанном с указанным объектом $device1 = SQLSelectOne("SELECT * FROM devices WHERE LINKED_OBJECT LIKE '" . $object_title . "'"); // Если устройство не найдено, завершаем измерение времени и возвращаем 0 if (!$device1['ID']) { endMeasure('checkLinkedDevicesAction'); return 0; } // Включаем файл с действиями связанных устройств include(dirname(__FILE__) . '/devices_links_actions.inc.php'); // Завершаем измерение времени выполнения функции endMeasure('checkLinkedDevicesAction'); // Возвращаем 1, указывая на успешное выполнение функции return 1; } /** * Install * * Маршрутина установки модуля. * * @method void Install() * @return void * @see module * @since 0.1 *// function install($data = '') { // Выполняем установку родительского класса parent::install(); // Включаем файлы языка модуля для текущего языка сайта и по умолчанию if (file_exists(ROOT . 'languages/' . $this->name . '_' . SETTINGS_SITE_LANGUAGE . '.php')) { include_once(ROOT . 'languages/' . $this->name . '_' . SETTINGS_SITE_LANGUAGE . '.php'); } if (file_exists(ROOT . 'languages/' . $this->name . '_default' . '.php')) { include_once(ROOT . 'languages/' . $this->name . '_default' . '.php'); } // Обновляем заголовок модуля в таблице проектных модулей SQLExec("UPDATE project_modules SET TITLE='" . LANG_DEVICES_MODULE_TITLE . "' WHERE NAME='" . $this->name . "'"); // Устанавливаем словарь устройств $this->setDictionary(); // Рендерим структуру модуля $this->renderStructure(); // Синхронизируем с Homebridge $this->homebridgeSync(); } /** * Uninstall * * Маршрутина удаления модуля. * * @method void Uninstall() * @return void * @see module * @since 0.1 */ function uninstall() { // Удаляем таблицу устройств SQLDropTable('devices'); // Выполняем удаление родительского класса parent::uninstall(); } /** * dbInstall * * Маршрутина установки базы данных. * * @method void dbInstall() * @return void * @see module * @since 0.1 */ function dbInstall($data = '') { /* * Создание таблиц для устройств и связей между ними. */ $data = <<<EOD devices: ID int(10) unsigned NOT NULL auto_increment devices: TITLE varchar(100) NOT NULL DEFAULT '' devices: ALT_TITLES varchar(255) NOT NULL DEFAULT '' devices: TYPE varchar(100) NOT NULL DEFAULT '' devices: LINKED_OBJECT varchar(100) NOT NULL DEFAULT '' devices: LOCATION_ID int(10) unsigned NOT NULL DEFAULT 0 devices: FAVORITE int(3) unsigned NOT NULL DEFAULT 0 devices: SYSTEM_DEVICE int(3) unsigned NOT NULL DEFAULT 0 devices: CLICKED datetime DEFAULT NULL devices: ARCHIVED int(3) unsigned NOT NULL DEFAULT 0 devices: SYSTEM varchar(255) NOT NULL DEFAULT '' devices: SUBTYPE varchar(100) NOT NULL DEFAULT '' devices: ENDPOINT_MODULE varchar(255) NOT NULL DEFAULT '' devices: ENDPOINT_NAME varchar(255) NOT NULL DEFAULT '' devices: ENDPOINT_TITLE varchar(255) NOT NULL DEFAULT '' devices: ROLES varchar(100) NOT NULL DEFAULT '' devices_linked: ID int(10) unsigned NOT NULL auto_increment devices_linked: DEVICE1_ID int(10) unsigned NOT NULL DEFAULT 0 devices_linked: DEVICE2_ID int(10) unsigned NOT NULL DEFAULT 0 devices_linked: LINK_TYPE varchar(100) NOT NULL DEFAULT '' devices_linked: LINK_SETTINGS text devices_linked: COMMENT varchar(255) NOT NULL DEFAULT '' devices_groups: ID int(10) unsigned NOT NULL auto_increment devices_groups: SYS_NAME varchar(100) NOT NULL DEFAULT '' devices_groups: TITLE varchar(255) NOT NULL DEFAULT '' devices_groups: APPLY_TYPES text devices_scheduler_points: ID int(10) unsigned NOT NULL auto_increment devices_scheduler_points: LINKED_METHOD varchar(255) NOT NULL DEFAULT '' devices_scheduler_points: VALUE varchar(255) NOT NULL DEFAULT '' devices_scheduler_points: SET_TIME varchar(50) NOT NULL DEFAULT '' devices_scheduler_points: SET_DAYS varchar(50) NOT NULL DEFAULT '' devices_scheduler_points: DEVICE_ID int(10) NOT NULL DEFAULT '0' devices_scheduler_points: ACTIVE int(3) NOT NULL DEFAULT '1' devices_scheduler_points: LATEST_RUN datetime EOD; // Выполняем установку базы данных parent::dbInstall($data); } // -------------------------------------------------------------------- } /* * Зачем эта строка с кодировкой? не понятно. * TW9kdWxlIGNyZWF0ZWQgSnVsIDE5LCAyMDE2IHVzaW5nIFNlcmdlIEouIHdpemFyZCAoQWN0aXZlVW5pdCBJbmMgd3d3LmFjdGl2ZXVuaXQuY29tKQ== * */