Modules/devices/processCommand inc php: различия между версиями

Материал из MajorDoMo инфо
(Заготовка)
 
(+ файл processCommand.inc.php)
 
Строка 3: Строка 3:


<pre>
<pre>
Заготовка
<?php
 
// Проверяет, отключены ли простые устройства в файле конфигурации ROOT . config.php. Если да, прекращает выполнение скрипта.
if (defined('DISABLE_SIMPLE_DEVICES') && DISABLE_SIMPLE_DEVICES == 1) return;
 
/*
* array('level' => $level, 'message' => $ph, 'member_id' => $member_id, 'source' => $source)
* $details['BREAK'] = 1 / 0
*/
// Этот блок кода, вероятно, предназначен для обработки лога или отладки, так как он содержит пример использования переменной $details['BREAK'].
// Однако, поскольку он закомментирован, его выполнение в текущем состоянии скрипта не происходит.
 
// Включаем файлы локализации для текущего языка сайта и по умолчанию.
@include_once(ROOT . 'languages/' . $this->name . '_' . SETTINGS_SITE_LANGUAGE . '.php');
@include_once(ROOT . 'languages/' . $this->name . '_default' . '.php');
 
// Если в деталях команды указано источник (терминал), то получаем информацию о терминале.
if ($details['source']) {
    $terminal = getTerminalByID(str_replace("terminal", "", $details['source']));
    // Если у терминала есть ID местоположения, сохраняем его.
    if ($terminal['LOCATION_ID']) {
        $location_id = $terminal['LOCATION_ID'];
    }
}
 
/*
* Этот фрагмент кода анализирует входящую команду на предмет наличия указаний на задержку выполнения,
* или продолжительность выполнения действия.
* Если такие указания присутствуют, код вычисляет соответствующие значения в секундах
* и сохраняет их в переменных $period_delay и $period_run_for соответственно.
* Также код сохраняет найденное совпадение в переменную $add_phrase, чтобы потом добавить его к сообщению.
*/
// Получаем текстовую команду из массива $details, который содержит детали сообщения.
$command = $details['message'];
// Инициализируем переменную $run_code как пустую строку. Эта переменная будет использоваться для хранения кода, который нужно выполнить.
$run_code = '';
// Инициализируем переменную $opposite_code как пустую строку. Эта переменная будет использоваться для хранения кода, который будет выполнен в качестве противодействия к основному коду.
$opposite_code = '';
// Инициализируем переменную $add_phrase как пустую строку. Эта переменная будет использоваться для хранения дополнительной фразы, которая будет добавлена к сообщению.
$add_phrase = '';
// Инициализируем переменную $period_delay как 0. Эта переменная будет использоваться для хранения задержки выполнения команды в секундах.
$period_delay = 0;
// Инициализируем переменную $period_run_for как 0. Эта переменная будет использоваться для хранения продолжительности выполнения команды в секундах.
$period_run_for = 0;
 
// Проверяем, содержит ли команда фразу, указывающую на необходимость выполнения действия после определенного времени.
if (preg_match('/' . LANG_PATTERN_DO_AFTER . ' (\d+?) (' . LANG_PATTERN_SECOND . '|' . LANG_PATTERN_MINUTE . '|' . LANG_PATTERN_HOUR . ')/uis', textToNumbers($command), $m)) {
    // Получаем числовое значение времени задержки из найденного совпадения.
    $period_number = $m[1];
    // Добавляем найденное совпадение к переменной $add_phrase, чтобы потом добавить его к сообщению.
    $add_phrase = ' ' . $m[0];
    // Определяем единицу измерения времени (секунды, минуты, часы) и вычисляем задержку в секундах.
    if (preg_match('/' . LANG_PATTERN_SECOND . '/uis', $m[2])) {
        // Если единица измерения - секунды, то задержка равна числовому значению.
        $period_delay = $period_number;
    } elseif (preg_match('/' . LANG_PATTERN_MINUTE . '/uis', $m[2])) {
        // Если единица измерения - минуты, то задержка равна числовому значению, умноженному на 60.
        $period_delay = $period_number * 60;
    } elseif (preg_match('/' . LANG_PATTERN_HOUR . '/uis', $m[2])) {
        // Если единица измерения - часы, то задержка равна числовому значению, умноженному на 3600.
        $period_delay = $period_number * 60 * 60;
    }
    // Удаляем обработанную часть команды, чтобы оставить только саму команду без указания времени задержки.
    $command = trim(str_replace($m[0], '', textToNumbers($command)));
} elseif (preg_match('/' . LANG_PATTERN_DO_FOR . ' (\d+?) (' . LANG_PATTERN_SECOND . '|' . LANG_PATTERN_MINUTE . '|' . LANG_PATTERN_HOUR . ')/uis', textToNumbers($command), $m)) {
    // Получаем числовое значение времени выполнения из найденного совпадения.
    $period_number = $m[1];
    // Добавляем найденное совпадение к переменной $add_phrase, чтобы потом добавить его к сообщению.
    $add_phrase = ' ' . $m[0];
    // Определяем единицу измерения времени (секунды, минуты, часы) и вычисляем продолжительность выполнения в секундах.
    if (preg_match('/' . LANG_PATTERN_SECOND . '/uis', $m[2])) {
        // Если единица измерения - секунды, то продолжительность выполнения равна числовому значению.
        $period_run_for = $period_number;
    } elseif (preg_match('/' . LANG_PATTERN_MINUTE . '/uis', $m[2])) {
        // Если единица измерения - минуты, то продолжительность выполнения равна числовому значению, умноженному на 60.
        $period_run_for = $period_number * 60;
    } elseif (preg_match('/' . LANG_PATTERN_HOUR . '/uis', $m[2])) {
        // Если единица измерения - часы, то продолжительность выполнения равна числовому значению, умноженному на 3600.
        $period_run_for = $period_number * 60 * 60;
    }
    // Удаляем обработанную часть команды, чтобы оставить только саму команду без указания времени выполнения.
    $command = trim(str_replace($m[0], '', textToNumbers($command)));
}
 
// Инициализируем переменную $processed как 0. Эта переменная будет использоваться для отслеживания, была ли команда обработана.
$processed = 0;
// Инициализируем переменную $reply_confirm как 0. Эта переменная будет использоваться для подтверждения выполнения команды.
$reply_confirm = 0;
// Инициализируем переменную $reply_say как пустую строку. Эта переменная будет использоваться для хранения сообщения, которое будет отправлено в ответ на команду.
$reply_say = '';
// Инициализируем переменную $phpmorphy_loaded как 0. Эта переменная будет использоваться для отслеживания, загружена ли библиотека phpMorphy.
$phpmorphy_loaded = 0;
 
// Проверяем, существует ли файл библиотеки phpMorphy.
if (file_exists(ROOT . "lib/phpmorphy/common.php")) {
    // Подключаем файл библиотеки phpMorphy.
    require_once(ROOT . "lib/phpmorphy/common.php");
    $opts = array(
        // Указываем тип хранения данных.
        'storage' => PHPMORPHY_STORAGE_MEM,
        // Включаем предсказание по суффиксу.
        'predict_by_suffix' => true,
        // Включаем предсказание по базе данных.
        'predict_by_db' => true,
        // Указываем, что грамматическая информация должна быть представлена в виде текста.
        'graminfo_as_text' => true,
    );
    // Указываем путь к словарям phpMorphy.
    $dir = ROOT . 'lib/phpmorphy/dicts';
    // Получаем код текущего языка сайта.
    $lang = SETTINGS_SITE_LANGUAGE_CODE;
    try {
        // Создаем экземпляр класса phpMorphy.
        $morphy = new phpMorphy($dir, $lang, $opts);
        // Сохраняем ссылку на созданный экземпляр в свойстве объекта.
        $this->morphy = &$morphy;
    } catch (phpMorphy_Exception $e) {
        // Выводим сообщение об ошибке и прерываем выполнение скрипта.
        die('Error occured while creating phpMorphy instance: ' . PHP_EOL . $e);
    }
    // Разбиваем команду на слова.
    $words = explode(' ', $command);
    // Инициализируем массив для хранения отфильтрованных слов.
    $words_filtered = array();
    // Инициализируем счетчик отфильтрованных слов.
    $filtered_count = 0;
    // Инициализируем массив для хранения базовых форм слов.
    $base_forms = array();
    // Инициализируем массив для хранения базовых форм отфильтрованных слов.
    $base_forms_filtered = array();
    // Получаем общее количество слов в команде.
    $totals = count($words);
    for ($is = 0; $is < $totals; $is++) {
        // Инициализируем переменную для отслеживания, было ли слово фильтровано.
        $filtered = 0;
        // Преобразуем слово в верхний регистр.
        $upper = mb_strtoupper($words[$is], 'UTF-8');
        // Получаем длину слова.
        $len = mb_strlen($words[$is], 'UTF-8');
        // Если длина слова больше или равна 3, считаем его достаточно длинным для анализа.
        if ($len >= 3) {
            // Добавляем слово в массив отфильтрованных слов.
            $words_filtered[] = $words[$is];
            // Помечаем слово как отфильтрованное.
            $filtered = 1;
            // Увеличиваем счетчик отфильтрованных слов.
            $filtered_count++;
        }
        // Если слово является числом, добавляем его в массив базовых форм.
        if (preg_match('/^(\d+)$/', $words[$is])) {
            $base_forms[$is] = array($words[$is]);
            // Если слово не содержит специальных символов, получаем его базовую форму.
        } elseif (!preg_match('/[\(\)\+\.]/', $words[$is])) {
            // Преобразуем слово в верхний регистр.
            $Word = mb_strtoupper($words[$is], 'UTF-8');
            // Получаем базовую форму слова.
            $base_forms[$is] = $morphy->getBaseForm($Word);
            // Добавляем исходное слово в массив базовых форм.
            $base_forms[$is][] = $words[$is];
            // Если слово содержит специальные символы, добавляем его в массив базовых форм без изменений.
        } else {
            $base_forms[$is] = array($words[$is]);
        }
        // Если слово было отфильтровано, добавляем его базовую форму в массив отфильтрованных базовых форм.
        if ($filtered) {
            $base_forms_filtered[$filtered_count - 1] = $base_forms[$is];
        }
    }
    // Генерируем все возможные комбинации базовых форм слов.
    $combos = $this->generate_combinations($base_forms);
 
    // Если количество отфильтрованных слов меньше общего количества слов, генерируем дополнительные комбинации.
    if ($filtered_count < $totals) {
        // Генерируем комбинации для отфильтрованных базовых форм.
        $add_combos = $this->generate_combinations($base_forms_filtered);
        // Добавляем каждую сгенерированную комбинацию в общий массив комбинаций.
        foreach ($add_combos as $cmb) {
            $combos[] = $cmb;
        }
    }
 
    // Инициализируем массив для хранения строк, полученных из комбинаций.
    $lines = array();
    // Получаем общее количество комбинаций.
    $totals = count($combos);
    // Преобразуем каждую комбинацию в строку и добавляем ее в массив строк.
    for ($is = 0; $is < $totals; $is++) {
        $lines[] = implode(' ', $combos[$is]);
    }
    // Помечаем, что библиотека phpMorphy была успешно загружена.
    $phpmorphy_loaded = 1;
}
 
// Получаем список устройств из базы данных.
$devices = SQLSelect("SELECT ID, TITLE, ALT_TITLES, TYPE, LINKED_OBJECT, LOCATION_ID FROM devices");
// Проходим по каждому устройству в полученном списке.
foreach ($devices as $device) {
    // Если у устройства есть альтернативные названия.
    if (trim($device['ALT_TITLES']) != '') {
        // Разбиваем альтернативные названия по запятой.
        $nicknames = explode(',', trim($device['ALT_TITLES']));
        // Проходим по каждому альтернативному названию.
        foreach ($nicknames as $nickname) {
            // Копируем текущее устройство.
            $add_rec = $device;
            // Заменяем название устройства на альтернативное.
            $add_rec['TITLE'] = $nickname;
            // Добавляем измененное устройство обратно в список устройств.
            $devices[] = $add_rec;
        }
    }
}
// Получаем список групп устройств из базы данных.
$groups = SQLSelect("SELECT * FROM devices_groups");
// Получаем общее количество групп.
$total = count($groups);
// Проходим по каждой группе в полученном списке.
for ($i = 0; $i < $total; $i++) {
    // Копируем текущую группу.
    $add_rec = $groups[$i];
    // Устанавливаем тип группы как 'group'.
    $add_rec['TYPE'] = 'group';
    // Добавляем группу обратно в список устройств.
    $devices[] = $add_rec;
}
// Получаем список комнат с устройствами из базы данных.
$rooms = SQLSelect("SELECT locations.ID, locations.TITLE, COUNT(*) AS TOTAL FROM locations, devices WHERE locations.ID=devices.LOCATION_ID GROUP BY locations.ID");
// Проходим по каждой комнате в полученном списке.
foreach ($rooms as $room) {
    //lights
    //if ($room['TITLE']=='Кабинет') {
    // Инициализируем массив для хранения типов устройств в комнате.
    $device_types = array();
    // Получаем список устройств типа 'relay' в текущей комнате.
    $room_devices = SQLSelect("SELECT * FROM devices WHERE LOCATION_ID=" . $room['ID'] . " AND TYPE='relay'");
    // Проходим по каждому устройству в полученном списке.
    foreach ($room_devices as $device) {
        // Получаем тип нагрузки устройства.
        $loadType = gg($device['LINKED_OBJECT'] . '.loadType');
        // Добавляем устройство в массив типов устройств.
        $device_types[$loadType][] = $device;
    }
    // Если в комнате есть устройства типа 'light'.
    if (isset($device_types['light'])) {
        // Инициализируем массив для новой группы устройств.
        $add_rec = array();
        // Устанавливаем тип группы как 'group'.
        $add_rec['TYPE'] = 'group';
        // Устанавливаем название группы.
        $add_rec['TITLE'] = LANG_DEVICES_LOADTYPE_LIGHT . ' ' . $room['TITLE'];
        // Добавляем устройства типа 'light' в группу.
        $add_rec['DEVICES'] = $device_types['light'];
        // Устанавливаем типы применения группы.
        $add_rec['APPLY_TYPES'] = 'relay';
        // Добавляем группу обратно в список устройств.
        $devices[] = $add_rec;
 
        // Инициализируем массив для новой группы устройств.
        $add_rec = array();
        // Устанавливаем тип группы как 'group'.
        $add_rec['TYPE'] = 'group';
        // Устанавливаем альтернативное название группы.
        $add_rec['TITLE'] = LANG_DEVICES_LOADTYPE_LIGHT_ALT . ' ' . $room['TITLE'];
        // Добавляем устройства типа 'light' в группу.
        $add_rec['DEVICES'] = $device_types['light'];
        // Устанавливаем типы применения группы.
        $add_rec['APPLY_TYPES'] = 'relay';
        // Добавляем группу обратно в список устройств.
        $devices[] = $add_rec;
    }
    //}
}
// Если библиотека phpMorphy была успешно загружена.
if ($phpmorphy_loaded) {
    // Получаем общее количество устройств.
    $total = count($devices);
    // Инициализируем массив для хранения дополнительных устройств.
    $add_devices = array();
    // Проходим по каждому устройству в полученном списке.
    for ($i = 0; $i < $total; $i++) {
        // Получаем название устройства.
        $device_title = $devices[$i]['TITLE'];
        // Разбиваем название устройства на слова и преобразуем их в верхний регистр.
        $words = explode(' ', mb_strtoupper($device_title, 'UTF-8'));
        // Инициализируем массив для хранения базовых форм слов.
        $base_forms = array();
        // Получаем общее количество слов.
        $totals = count($words);
        // Проходим по каждому слову в полученном списке.
        for ($is = 0; $is < $totals; $is++) {
            // Если слово является числом, добавляем его в массив базовых форм.
            if (preg_match('/^(\d+)$/', $words[$is])) {
               
                $base_forms[$is] = array($words[$is]);
            // Если слово не содержит специальных символов, получаем его базовую форму.
            } elseif (!preg_match('/[\(\)\+\.]/', $words[$is])) {
                // Преобразуем слово в верхний регистр.
                $Word = mb_strtoupper($words[$is], 'UTF-8');
                // Получаем базовую форму слова.
                $base_form = $morphy->getBaseForm($Word);
                // Если базовая форма представлена в виде массива.
                if (is_array($base_form)) {
                    $base_forms[$is] = $base_form;
                // Если базовая форма не представлена в виде массива.
                } else {
                    $base_forms[$is] = array();
                }
                // Если исходное слово не содержится в массиве базовых форм.
                if (!in_array($words[$is], $base_forms[$is])) {
                    // Добавляем исходное слово в массив базовых форм.
                    $base_forms[$is][] = $words[$is];
                }
            // Если слово содержит специальные символы, добавляем его в массив базовых форм без изменений.
            } else {
                $base_forms[$is] = array($words[$is]);
            }
        }
        // Генерируем все возможные комбинации базовых форм слов.
        $combos = $this->generate_combinations($base_forms);
        // Инициализируем массив для хранения фраз, полученных из комбинаций.
        $phrases = array();
        // Проходим по каждой комбинации в полученном списке.
        foreach ($combos as $combo) {
            // Вычисляем все возможные перестановки для текущей комбинации.
            $mutations = $this->computePermutations($combo);
            // Проходим по каждой перестановке в полученном списке.
            foreach ($mutations as $m) {
                // Преобразуем каждую перестановку в строку и добавляем ее в массив фраз.
                $phrases[] = implode(' ', $m);
            }
        }
        // Инициализируем массив для хранения новых названий устройств.
        $device_titles = array();
        // Получаем общее количество фраз.
        $totals = count($phrases);
        // Проходим по каждой фразе в полученном списке.
        for ($is = 0; $is < $totals; $is++) {
            // Получаем новое название устройства.
            $new_title = $phrases[$is];
            // Добавляем новое название устройства в массив новых названий.
            $device_titles[] = $new_title;
            // Копируем текущее устройство.
            $new_device = $devices[$i];
            // Заменяем название устройства на новое.
            $new_device['TITLE'] = $new_title;
            // Сохраняем оригинальное название устройства.
            $new_device['ORIGINAL_TITLE'] = $device_title;
            // Добавляем измененное устройство в массив дополнительных устройств.
            $add_devices[] = $new_device;
        }
    }
    // Проходим по каждому устройству в массиве дополнительных устройств.
    foreach ($add_devices as $device) {
        // Добавляем устройство обратно в список устройств.
        $devices[] = $device;
    }
}
 
// Выводим содержимое переменной $lines для отладки.
//dprint($lines,false);
// Выводим содержимое переменной $devices для отладки.
//dprint($devices);
// Инициализируем переменную $compare_title командой.
$compare_title = $command;
 
 
// Проверяем, содержит ли команда фразу, указывающую на необходимость включения устройства.
if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $compare_title, $m)) {
    // Если да, удаляем эту фразу из команды.
    $compare_title = trim(str_replace($m[0], ' ', $compare_title));
}
 
// Проверяем, содержит ли команда фразу, указывающую на необходимость выключения устройства.
if (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $compare_title, $m)) {
    // Если да, удаляем эту фразу из команды.
    $compare_title = trim(str_replace($m[0], ' ', $compare_title));
}
 
// Удаляем начальные слова "ть" и "те" из команды, если они присутствуют.
$compare_title = trim(preg_replace('/^ть /', '', $compare_title));
$compare_title = trim(preg_replace('/^те /', '', $compare_title));
 
// Если после всех преобразований команда стала пустой, прекращаем выполнение скрипта.
if ($compare_title == '') {
    return;
}
 
// Получаем общее количество устройств.
$total = count($devices);
// Проходим по каждому устройству в полученном списке.
for ($i = 0; $i < $total; $i++) {
    // Инициализируем переменную для отслеживания, было ли найдено соответствие устройства.
    $device_matched = 0;
 
    // Если указано местоположение терминала.
    if ($location_id) {
        // Ищем строгое соответствие по названию и местоположению.
        foreach ($devices as $key => $value) {
            // Если ID местоположения устройства совпадает с ID местоположения терминала и название устройства совпадает с командой.
            if (in_array($location_id, $value) && strtolower($devices[$key]['TITLE']) == strtolower($compare_title)) {
 
                // Обновляем индекс текущего устройства.
                $i = $key;
                // Помечаем, что устройство найдено.
                $device_matched = 1;
                // Прерываем цикл.
                break;
            }
        }
        // Если устройство не найдено.
        if (!$device_matched) {
            // Ищем строгое соответствие по названию.
            foreach ($devices as $key => $value) {
                // Если название устройства совпадает с командой.
                if (in_array(strtolower($compare_title), strtolower($value))) {
                    // Обновляем индекс текущего устройства.
                    $i = $key;
                    // Помечаем, что устройство найдено.
                    $device_matched = 1;
                    // Прерываем цикл.
                    break;
                }
            }
        }
    }
 
    // Ищем по старому принципу.
    if (preg_match('/' . preg_quote($devices[$i]['TITLE']) . '/uis', $compare_title)) {
        // Помечаем, что устройство найдено.
        $device_matched = 1;
    } elseif (preg_match('/' . preg_quote($compare_title) . '/uis', $devices[$i]['TITLE'])) {
        // Помечаем, что устройство найдено.
        $device_matched = 1;
 
    // Если библиотека phpMorphy была успешно загружена.
    } elseif ($phpmorphy_loaded) {
        if (preg_match('/' . preg_quote($devices[$i]['TITLE']) . '/isu', implode('@@@@', $lines), $matches)) {
            // Помечаем, что устройство найдено.
            $device_matched = 1;
        }
    }
 
    /*
    if (preg_match('/свет над столом/uis',$devices[$i]['TITLE'])) {
        dprint($devices[$i]['TITLE'].' - '.$compare_title.': '.$device_matched);
    }
    */
 
    // Если устройство было найдено.
    if ($device_matched) {
 
        // Найдено устройство.
        // Получаем ID устройства.
        $device_id = $devices[$i]['ID'];
        // Получаем тип устройства.
        $device_type = $devices[$i]['TYPE'];
        // Если у устройства есть оригинальное название.
        if ($devices[$i]['ORIGINAL_TITLE'] != '') {
            // Используем оригинальное название.
            $device_title = $devices[$i]['ORIGINAL_TITLE'];
        // Если оригинального названия нет.
        } else {
            // Используем текущее название.
            $device_title = $devices[$i]['TITLE'];
        }
 
        // Записываем сообщение о том, что устройство было найдено.
        DebMes("Device found for $command ($device_title)", 'simple_devices');
 
        // Получаем связанный объект устройства.
        $linked_object = $devices[$i]['LINKED_OBJECT'];
        if ($device_type == 'sensor_percentage' || $device_type == 'sensor_humidity') {
            // Формируем сообщение для датчиков с процентами и влажностью.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . '%';
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'sensor_light') {
            // Формируем сообщение для датчиков света.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value');
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'sensor_temp') {
            // Формируем сообщение для датчиков температуры.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . ' ' . LANG_DEVICES_DEGREES;
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif (preg_match('/sensor/', $device_type)) {
            // Формируем сообщение для других датчиков.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . '';
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'counter') {
            // Формируем сообщение для счетчиков.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . ' ' . gg($linked_object . '.unit');
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'openclose') {
            // Формируем сообщение для устройств открытия/закрытия.
            $reply_say = $device_title . ' ' . (gg($linked_object . '.status') ? LANG_DEVICES_STATUS_CLOSED : LANG_DEVICES_STATUS_OPEN);
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'smoke' || $device_type == 'leak') {
            // Формируем сообщение для датчиков дыма и утечек.
            $reply_say = $device_title . ' ' . (gg($linked_object . '.status') ? LANG_DEVICES_STATUS_ALARM : LANG_DEVICES_NORMAL_VALUE);
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'button') {
            // Добавляем код для имитации нажатия кнопки.
            $run_code .= "callMethod('$linked_object.pressed');";
            // Помечаем, что команда была обработана.
            $processed = 1;
            // Помечаем, что необходимо подтверждение выполнения команды.
            $reply_confirm = 1;
        } elseif (
            $device_type == 'controller' ||
            $device_type == 'relay' ||
            $device_type == 'dimmer' ||
            $device_type == 'rgb'
        ) {
            if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $command)) {
                // Формируем сообщение для включения устройства.
                $reply_say = LANG_TURNING_ON . ' ' . $device_title . $add_phrase;
                // Добавляем код для включения устройства.
                $run_code .= "callMethod('$linked_object.turnOn');";
                // Добавляем код для выключения устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.turnOff');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            } elseif (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $command)) {
                // Формируем сообщение для выключения устройства.
                $reply_say = LANG_TURNING_OFF . ' ' . $device_title . $add_phrase;
                // Добавляем код для выключения устройства.
                $run_code .= "callMethod('$linked_object.turnOff');";
                // Добавляем код для включения устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.turnOn');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            }
        } elseif ($device_type == 'openable') {
            if (preg_match('/' . LANG_DEVICES_PATTERN_OPEN . '/uis', $command)) {
                // Формируем сообщение для открытия устройства.
                $reply_say = LANG_TURNING_OPEN . ' ' . $device_title . $add_phrase;
                // Добавляем код для открытия устройства.
                $run_code .= "callMethod('$linked_object.Open');";
                // Добавляем код для закрытия устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.Close');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            } elseif (preg_match('/' . LANG_DEVICES_PATTERN_CLOSE . '/uis', $command)) {
                // Формируем сообщение для закрытия устройства.
                $reply_say = LANG_TURNING_CLOSE . ' ' . $device_title . $add_phrase;
                // Добавляем код для закрытия устройства.
                $run_code .= "callMethod('$linked_object.Close');";
                // Добавляем код для открытия устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.Open');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            }
        } elseif ($device_type == 'group') {
            // Разбиваем типы применения группы по запятой.
            $applies_to = explode(',', $devices[$i]['APPLY_TYPES']);
            // Если устройства группы представлены в виде массива.
            if (is_array($devices[$i]['DEVICES'])) {
                // Инициализируем массив для хранения связанных объектов устройств в группе.
                $devices_in_group = array();
                // Проходим по каждому устройству в группе.
                foreach ($devices[$i]['DEVICES'] as $group_device) {
                    // Добавляем связанный объект устройства в массив.
                    $devices_in_group[] = $group_device['LINKED_OBJECT'];
                }
            // Если устройства группы не представлены в виде массива.
            } else {
                // Получаем связанные объекты устройств в группе по свойству.
                $devices_in_group = getObjectsByProperty('group' . $devices[$i]['SYS_NAME'], 1);
            }
            // Выводим содержимое переменной $devices_in_group для отладки.
            //dprint($devices_in_group);
 
            // Если устройства группы не представлены в виде массива, пропускаем текущую итерацию цикла.
            if (!is_array($devices_in_group)) continue;
 
            if (
                in_array('relay', $applies_to) ||
                in_array('dimmer', $applies_to) ||
                in_array('rgb', $applies_to) ||
                0
            ) {
                if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $command)) {
                    // Формируем сообщение для включения устройства.
                    $reply_say = LANG_TURNING_ON . ' ' . $device_title . $add_phrase;
                    foreach ($devices_in_group as $linked_object) {
                        // Добавляем код для включения каждого устройства в группе.
                        $run_code .= "callMethod('$linked_object.turnOn');";
                        // Добавляем код для выключения каждого устройства в группе в качестве противодействия.
                        $opposite_code .= "callMethod('$linked_object.turnOff');";
                    }
                    // Помечаем, что команда была обработана.
                    $processed = 1;
                    // Помечаем, что необходимо подтверждение выполнения команды.
                    //$reply_confirm = 1;
                } elseif (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $command)) {
                    // Формируем сообщение для выключения устройства.
                    $reply_say = LANG_TURNING_OFF . ' ' . $device_title . $add_phrase;
                    foreach ($devices_in_group as $linked_object) {
                        // Добавляем код для выключения каждого устройства в группе.
                        $run_code .= "callMethod('$linked_object.turnOff');";
                        // Добавляем код для включения каждого устройства в группе в качестве противодействия.
                        $opposite_code .= "callMethod('$linked_object.turnOn');";
                    }
                    // Помечаем, что команда была обработана.
                    $processed = 1;
                    // Помечаем, что необходимо подтверждение выполнения команды.
                    //$reply_confirm = 1;
                }
            }
 
            // Определяем путь к директории дополнений.
            $addons_dir = dirname(__FILE__) . '/addons';
            // Если директория дополнений существует.
            if (is_dir($addons_dir)) {
                // Получаем список файлов в директории дополнений.
                $addon_files = scandir($addons_dir);
                // Проходим по каждому файлу в полученном списке.
                foreach ($addon_files as $file) {
                    // Если имя файла соответствует шаблону.
                    if (preg_match('/\_commands\.php$/', $file)) {
                        // Подключаем файл дополнения.
                        require($addons_dir . '/' . $file);
                    }
                }
            }
 
            // Если команда была обработана, прерываем цикл.
            if ($processed) break;
 
            if ($run_code != '' && $period_delay > 0) {
                // Если есть код для выполнения и задержка больше 0, устанавливаем таймер для выполнения кода после задержки.
                setTimeout('delay' . md5($run_code), $run_code, $period_delay);
            } elseif ($run_code != '' && $period_run_for > 0 && $opposite_code != '') {
                // Если есть код для выполнения, продолжительность выполнения больше 0 и есть код для противодействия, выполняем код.
                eval($run_code);
                // Устанавливаем таймер для выполнения кода противодействия после указанного времени.
                setTimeout('opposite' . md5($run_code), $opposite_code, $period_run_for);
            } elseif ($run_code != '') {
                // Если есть код для выполнения, выполняем его.
                eval($run_code);
                /*
                * TODO использовать eval считается небезопасным, поскольку он выполняет произвольный PHP-код, представленный в виде строки.
                * Это может создать уязвимости в безопасности и снизить производительность, поскольку он компилирует и выполняет код каждый раз,
                * когда он вызывается. Это может привести к увеличению нагрузки на сервер и снижению производительности.
                */
            }
 
            if ($reply_say != '') {
                // Если есть сообщение для ответа, отправляем его.
                sayReplySafe($reply_say, 2);
            }
 
            if ($reply_confirm) {
                // Разбиваем строку подтверждения команды по символу '|'.
                $items = explode('|', LANG_DEVICES_COMMAND_CONFIRMATION);
                // Удаляем пробелы в начале и конце каждого элемента массива.
                $items = array_map('trim', $items);
                // Отправляем случайное сообщение из массива подтверждений.
                sayReplySafe($items[array_rand($items)], 2);
            }
 
            if ($processed) {
                // Если команда была обработана, помечаем это.
                $details['PROCESSED'] = 1;
                // Помечаем, что необходимо прервать выполнение.
                $details['BREAK'] = 1;
            }
        }
    }
}
 
</pre>
</pre>

Текущая версия от 21:38, 9 апреля 2024

ᐂ В корневой раздел ᐃ В директорию расположения файла

<?php

// Проверяет, отключены ли простые устройства в файле конфигурации ROOT . config.php. Если да, прекращает выполнение скрипта.
if (defined('DISABLE_SIMPLE_DEVICES') && DISABLE_SIMPLE_DEVICES == 1) return;

/*
 * array('level' => $level, 'message' => $ph, 'member_id' => $member_id, 'source' => $source)
 * $details['BREAK'] = 1 / 0
 */
// Этот блок кода, вероятно, предназначен для обработки лога или отладки, так как он содержит пример использования переменной $details['BREAK'].
// Однако, поскольку он закомментирован, его выполнение в текущем состоянии скрипта не происходит.

// Включаем файлы локализации для текущего языка сайта и по умолчанию.
@include_once(ROOT . 'languages/' . $this->name . '_' . SETTINGS_SITE_LANGUAGE . '.php');
@include_once(ROOT . 'languages/' . $this->name . '_default' . '.php');

// Если в деталях команды указано источник (терминал), то получаем информацию о терминале.
if ($details['source']) {
    $terminal = getTerminalByID(str_replace("terminal", "", $details['source']));
    // Если у терминала есть ID местоположения, сохраняем его.
    if ($terminal['LOCATION_ID']) {
        $location_id = $terminal['LOCATION_ID'];
    }
}

/*
* Этот фрагмент кода анализирует входящую команду на предмет наличия указаний на задержку выполнения,
* или продолжительность выполнения действия.
* Если такие указания присутствуют, код вычисляет соответствующие значения в секундах
* и сохраняет их в переменных $period_delay и $period_run_for соответственно.
* Также код сохраняет найденное совпадение в переменную $add_phrase, чтобы потом добавить его к сообщению.
*/
// Получаем текстовую команду из массива $details, который содержит детали сообщения.
$command = $details['message'];
// Инициализируем переменную $run_code как пустую строку. Эта переменная будет использоваться для хранения кода, который нужно выполнить.
$run_code = '';
// Инициализируем переменную $opposite_code как пустую строку. Эта переменная будет использоваться для хранения кода, который будет выполнен в качестве противодействия к основному коду.
$opposite_code = '';
// Инициализируем переменную $add_phrase как пустую строку. Эта переменная будет использоваться для хранения дополнительной фразы, которая будет добавлена к сообщению.
$add_phrase = '';
// Инициализируем переменную $period_delay как 0. Эта переменная будет использоваться для хранения задержки выполнения команды в секундах.
$period_delay = 0;
// Инициализируем переменную $period_run_for как 0. Эта переменная будет использоваться для хранения продолжительности выполнения команды в секундах.
$period_run_for = 0;

// Проверяем, содержит ли команда фразу, указывающую на необходимость выполнения действия после определенного времени.
if (preg_match('/' . LANG_PATTERN_DO_AFTER . ' (\d+?) (' . LANG_PATTERN_SECOND . '|' . LANG_PATTERN_MINUTE . '|' . LANG_PATTERN_HOUR . ')/uis', textToNumbers($command), $m)) {
    // Получаем числовое значение времени задержки из найденного совпадения.
    $period_number = $m[1];
    // Добавляем найденное совпадение к переменной $add_phrase, чтобы потом добавить его к сообщению.
    $add_phrase = ' ' . $m[0];
    // Определяем единицу измерения времени (секунды, минуты, часы) и вычисляем задержку в секундах.
    if (preg_match('/' . LANG_PATTERN_SECOND . '/uis', $m[2])) {
        // Если единица измерения - секунды, то задержка равна числовому значению.
        $period_delay = $period_number;
    } elseif (preg_match('/' . LANG_PATTERN_MINUTE . '/uis', $m[2])) {
        // Если единица измерения - минуты, то задержка равна числовому значению, умноженному на 60.
        $period_delay = $period_number * 60;
    } elseif (preg_match('/' . LANG_PATTERN_HOUR . '/uis', $m[2])) {
        // Если единица измерения - часы, то задержка равна числовому значению, умноженному на 3600.
        $period_delay = $period_number * 60 * 60;
    }
    // Удаляем обработанную часть команды, чтобы оставить только саму команду без указания времени задержки.
    $command = trim(str_replace($m[0], '', textToNumbers($command)));
} elseif (preg_match('/' . LANG_PATTERN_DO_FOR . ' (\d+?) (' . LANG_PATTERN_SECOND . '|' . LANG_PATTERN_MINUTE . '|' . LANG_PATTERN_HOUR . ')/uis', textToNumbers($command), $m)) {
    // Получаем числовое значение времени выполнения из найденного совпадения.
    $period_number = $m[1];
    // Добавляем найденное совпадение к переменной $add_phrase, чтобы потом добавить его к сообщению.
    $add_phrase = ' ' . $m[0];
    // Определяем единицу измерения времени (секунды, минуты, часы) и вычисляем продолжительность выполнения в секундах.
    if (preg_match('/' . LANG_PATTERN_SECOND . '/uis', $m[2])) {
        // Если единица измерения - секунды, то продолжительность выполнения равна числовому значению.
        $period_run_for = $period_number;
    } elseif (preg_match('/' . LANG_PATTERN_MINUTE . '/uis', $m[2])) {
        // Если единица измерения - минуты, то продолжительность выполнения равна числовому значению, умноженному на 60.
        $period_run_for = $period_number * 60;
    } elseif (preg_match('/' . LANG_PATTERN_HOUR . '/uis', $m[2])) {
        // Если единица измерения - часы, то продолжительность выполнения равна числовому значению, умноженному на 3600.
        $period_run_for = $period_number * 60 * 60;
    }
    // Удаляем обработанную часть команды, чтобы оставить только саму команду без указания времени выполнения.
    $command = trim(str_replace($m[0], '', textToNumbers($command)));
}

// Инициализируем переменную $processed как 0. Эта переменная будет использоваться для отслеживания, была ли команда обработана.
$processed = 0;
// Инициализируем переменную $reply_confirm как 0. Эта переменная будет использоваться для подтверждения выполнения команды.
$reply_confirm = 0;
// Инициализируем переменную $reply_say как пустую строку. Эта переменная будет использоваться для хранения сообщения, которое будет отправлено в ответ на команду.
$reply_say = '';
// Инициализируем переменную $phpmorphy_loaded как 0. Эта переменная будет использоваться для отслеживания, загружена ли библиотека phpMorphy.
$phpmorphy_loaded = 0;

// Проверяем, существует ли файл библиотеки phpMorphy.
if (file_exists(ROOT . "lib/phpmorphy/common.php")) {
    // Подключаем файл библиотеки phpMorphy.
    require_once(ROOT . "lib/phpmorphy/common.php");
    $opts = array(
        // Указываем тип хранения данных.
        'storage' => PHPMORPHY_STORAGE_MEM,
        // Включаем предсказание по суффиксу.
        'predict_by_suffix' => true,
        // Включаем предсказание по базе данных.
        'predict_by_db' => true,
        // Указываем, что грамматическая информация должна быть представлена в виде текста.
        'graminfo_as_text' => true,
    );
    // Указываем путь к словарям phpMorphy.
    $dir = ROOT . 'lib/phpmorphy/dicts';
    // Получаем код текущего языка сайта.
    $lang = SETTINGS_SITE_LANGUAGE_CODE;
    try {
        // Создаем экземпляр класса phpMorphy.
        $morphy = new phpMorphy($dir, $lang, $opts);
        // Сохраняем ссылку на созданный экземпляр в свойстве объекта.
        $this->morphy = &$morphy;
    } catch (phpMorphy_Exception $e) {
        // Выводим сообщение об ошибке и прерываем выполнение скрипта.
        die('Error occured while creating phpMorphy instance: ' . PHP_EOL . $e);
    }
    // Разбиваем команду на слова.
    $words = explode(' ', $command);
    // Инициализируем массив для хранения отфильтрованных слов.
    $words_filtered = array();
    // Инициализируем счетчик отфильтрованных слов.
    $filtered_count = 0;
    // Инициализируем массив для хранения базовых форм слов.
    $base_forms = array();
    // Инициализируем массив для хранения базовых форм отфильтрованных слов.
    $base_forms_filtered = array();
    // Получаем общее количество слов в команде.
    $totals = count($words);
    for ($is = 0; $is < $totals; $is++) {
        // Инициализируем переменную для отслеживания, было ли слово фильтровано.
        $filtered = 0;
        // Преобразуем слово в верхний регистр.
        $upper = mb_strtoupper($words[$is], 'UTF-8');
        // Получаем длину слова.
        $len = mb_strlen($words[$is], 'UTF-8');
        // Если длина слова больше или равна 3, считаем его достаточно длинным для анализа.
        if ($len >= 3) {
            // Добавляем слово в массив отфильтрованных слов.
            $words_filtered[] = $words[$is];
            // Помечаем слово как отфильтрованное.
            $filtered = 1;
            // Увеличиваем счетчик отфильтрованных слов.
            $filtered_count++;
        }
        // Если слово является числом, добавляем его в массив базовых форм.
        if (preg_match('/^(\d+)$/', $words[$is])) {
            $base_forms[$is] = array($words[$is]);
            // Если слово не содержит специальных символов, получаем его базовую форму.
        } elseif (!preg_match('/[\(\)\+\.]/', $words[$is])) {
            // Преобразуем слово в верхний регистр.
            $Word = mb_strtoupper($words[$is], 'UTF-8');
            // Получаем базовую форму слова.
            $base_forms[$is] = $morphy->getBaseForm($Word);
            // Добавляем исходное слово в массив базовых форм.
            $base_forms[$is][] = $words[$is];
            // Если слово содержит специальные символы, добавляем его в массив базовых форм без изменений.
        } else {
            $base_forms[$is] = array($words[$is]);
        }
        // Если слово было отфильтровано, добавляем его базовую форму в массив отфильтрованных базовых форм.
        if ($filtered) {
            $base_forms_filtered[$filtered_count - 1] = $base_forms[$is];
        }
    }
    // Генерируем все возможные комбинации базовых форм слов.
    $combos = $this->generate_combinations($base_forms);

    // Если количество отфильтрованных слов меньше общего количества слов, генерируем дополнительные комбинации.
    if ($filtered_count < $totals) {
        // Генерируем комбинации для отфильтрованных базовых форм.
        $add_combos = $this->generate_combinations($base_forms_filtered);
        // Добавляем каждую сгенерированную комбинацию в общий массив комбинаций.
        foreach ($add_combos as $cmb) {
            $combos[] = $cmb;
        }
    }

    // Инициализируем массив для хранения строк, полученных из комбинаций.
    $lines = array();
    // Получаем общее количество комбинаций.
    $totals = count($combos);
    // Преобразуем каждую комбинацию в строку и добавляем ее в массив строк.
    for ($is = 0; $is < $totals; $is++) {
        $lines[] = implode(' ', $combos[$is]);
    }
    // Помечаем, что библиотека phpMorphy была успешно загружена.
    $phpmorphy_loaded = 1;
}

// Получаем список устройств из базы данных.
$devices = SQLSelect("SELECT ID, TITLE, ALT_TITLES, TYPE, LINKED_OBJECT, LOCATION_ID FROM devices");
// Проходим по каждому устройству в полученном списке.
foreach ($devices as $device) {
    // Если у устройства есть альтернативные названия.
    if (trim($device['ALT_TITLES']) != '') {
        // Разбиваем альтернативные названия по запятой.
        $nicknames = explode(',', trim($device['ALT_TITLES']));
        // Проходим по каждому альтернативному названию.
        foreach ($nicknames as $nickname) {
            // Копируем текущее устройство.
            $add_rec = $device;
            // Заменяем название устройства на альтернативное.
            $add_rec['TITLE'] = $nickname;
            // Добавляем измененное устройство обратно в список устройств.
            $devices[] = $add_rec; 
        }
    }
}
// Получаем список групп устройств из базы данных.
$groups = SQLSelect("SELECT * FROM devices_groups");
// Получаем общее количество групп.
$total = count($groups);
// Проходим по каждой группе в полученном списке.
for ($i = 0; $i < $total; $i++) {
    // Копируем текущую группу.
    $add_rec = $groups[$i];
    // Устанавливаем тип группы как 'group'.
    $add_rec['TYPE'] = 'group';
    // Добавляем группу обратно в список устройств.
    $devices[] = $add_rec;
}
// Получаем список комнат с устройствами из базы данных.
$rooms = SQLSelect("SELECT locations.ID, locations.TITLE, COUNT(*) AS TOTAL FROM locations, devices WHERE locations.ID=devices.LOCATION_ID GROUP BY locations.ID"); 
// Проходим по каждой комнате в полученном списке.
foreach ($rooms as $room) {
    //lights
    //if ($room['TITLE']=='Кабинет') {
    // Инициализируем массив для хранения типов устройств в комнате.
    $device_types = array();
    // Получаем список устройств типа 'relay' в текущей комнате.
    $room_devices = SQLSelect("SELECT * FROM devices WHERE LOCATION_ID=" . $room['ID'] . " AND TYPE='relay'");
    // Проходим по каждому устройству в полученном списке.
    foreach ($room_devices as $device) {
        // Получаем тип нагрузки устройства.
        $loadType = gg($device['LINKED_OBJECT'] . '.loadType');
        // Добавляем устройство в массив типов устройств.
        $device_types[$loadType][] = $device;
    }
    // Если в комнате есть устройства типа 'light'.
    if (isset($device_types['light'])) {
        // Инициализируем массив для новой группы устройств.
        $add_rec = array();
        // Устанавливаем тип группы как 'group'.
        $add_rec['TYPE'] = 'group';
        // Устанавливаем название группы.
        $add_rec['TITLE'] = LANG_DEVICES_LOADTYPE_LIGHT . ' ' . $room['TITLE'];
        // Добавляем устройства типа 'light' в группу.
        $add_rec['DEVICES'] = $device_types['light'];
        // Устанавливаем типы применения группы.
        $add_rec['APPLY_TYPES'] = 'relay';
        // Добавляем группу обратно в список устройств.
        $devices[] = $add_rec;

        // Инициализируем массив для новой группы устройств.
        $add_rec = array();
        // Устанавливаем тип группы как 'group'.
        $add_rec['TYPE'] = 'group';
        // Устанавливаем альтернативное название группы.
        $add_rec['TITLE'] = LANG_DEVICES_LOADTYPE_LIGHT_ALT . ' ' . $room['TITLE'];
        // Добавляем устройства типа 'light' в группу.
        $add_rec['DEVICES'] = $device_types['light'];
        // Устанавливаем типы применения группы.
        $add_rec['APPLY_TYPES'] = 'relay';
        // Добавляем группу обратно в список устройств.
        $devices[] = $add_rec;
    }
    //}
}
// Если библиотека phpMorphy была успешно загружена.
if ($phpmorphy_loaded) { 
    // Получаем общее количество устройств.
    $total = count($devices);
    // Инициализируем массив для хранения дополнительных устройств.
    $add_devices = array();
    // Проходим по каждому устройству в полученном списке.
    for ($i = 0; $i < $total; $i++) {
        // Получаем название устройства.
        $device_title = $devices[$i]['TITLE'];
        // Разбиваем название устройства на слова и преобразуем их в верхний регистр.
        $words = explode(' ', mb_strtoupper($device_title, 'UTF-8'));
        // Инициализируем массив для хранения базовых форм слов.
        $base_forms = array();
        // Получаем общее количество слов.
        $totals = count($words);
        // Проходим по каждому слову в полученном списке.
        for ($is = 0; $is < $totals; $is++) {
            // Если слово является числом, добавляем его в массив базовых форм.
            if (preg_match('/^(\d+)$/', $words[$is])) {
                
                $base_forms[$is] = array($words[$is]);
            // Если слово не содержит специальных символов, получаем его базовую форму.
            } elseif (!preg_match('/[\(\)\+\.]/', $words[$is])) {
                // Преобразуем слово в верхний регистр.
                $Word = mb_strtoupper($words[$is], 'UTF-8');
                // Получаем базовую форму слова.
                $base_form = $morphy->getBaseForm($Word);
                // Если базовая форма представлена в виде массива.
                if (is_array($base_form)) {
                    $base_forms[$is] = $base_form;
                // Если базовая форма не представлена в виде массива.
                } else {
                    $base_forms[$is] = array();
                }
                // Если исходное слово не содержится в массиве базовых форм.
                if (!in_array($words[$is], $base_forms[$is])) {
                    // Добавляем исходное слово в массив базовых форм.
                    $base_forms[$is][] = $words[$is];
                }
            // Если слово содержит специальные символы, добавляем его в массив базовых форм без изменений.
            } else { 
                $base_forms[$is] = array($words[$is]);
            }
        }
        // Генерируем все возможные комбинации базовых форм слов.
        $combos = $this->generate_combinations($base_forms);
        // Инициализируем массив для хранения фраз, полученных из комбинаций.
        $phrases = array();
        // Проходим по каждой комбинации в полученном списке.
        foreach ($combos as $combo) {
            // Вычисляем все возможные перестановки для текущей комбинации.
            $mutations = $this->computePermutations($combo);
            // Проходим по каждой перестановке в полученном списке.
            foreach ($mutations as $m) {
                // Преобразуем каждую перестановку в строку и добавляем ее в массив фраз.
                $phrases[] = implode(' ', $m);
            }
        }
        // Инициализируем массив для хранения новых названий устройств.
        $device_titles = array();
        // Получаем общее количество фраз.
        $totals = count($phrases);
        // Проходим по каждой фразе в полученном списке.
        for ($is = 0; $is < $totals; $is++) { 
            // Получаем новое название устройства.
            $new_title = $phrases[$is];
            // Добавляем новое название устройства в массив новых названий.
            $device_titles[] = $new_title;
            // Копируем текущее устройство.
            $new_device = $devices[$i];
            // Заменяем название устройства на новое.
            $new_device['TITLE'] = $new_title;
            // Сохраняем оригинальное название устройства.
            $new_device['ORIGINAL_TITLE'] = $device_title;
            // Добавляем измененное устройство в массив дополнительных устройств.
            $add_devices[] = $new_device;
        }
    }
    // Проходим по каждому устройству в массиве дополнительных устройств.
    foreach ($add_devices as $device) {
        // Добавляем устройство обратно в список устройств.
        $devices[] = $device;
    }
}

// Выводим содержимое переменной $lines для отладки.
//dprint($lines,false); 
// Выводим содержимое переменной $devices для отладки.
//dprint($devices); 
// Инициализируем переменную $compare_title командой.
$compare_title = $command; 


// Проверяем, содержит ли команда фразу, указывающую на необходимость включения устройства.
if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $compare_title, $m)) {
    // Если да, удаляем эту фразу из команды.
    $compare_title = trim(str_replace($m[0], ' ', $compare_title));
}

// Проверяем, содержит ли команда фразу, указывающую на необходимость выключения устройства.
if (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $compare_title, $m)) {
    // Если да, удаляем эту фразу из команды.
    $compare_title = trim(str_replace($m[0], ' ', $compare_title));
}

// Удаляем начальные слова "ть" и "те" из команды, если они присутствуют.
$compare_title = trim(preg_replace('/^ть /', '', $compare_title));
$compare_title = trim(preg_replace('/^те /', '', $compare_title));

// Если после всех преобразований команда стала пустой, прекращаем выполнение скрипта.
if ($compare_title == '') {
    return;
}

// Получаем общее количество устройств.
$total = count($devices);
// Проходим по каждому устройству в полученном списке.
for ($i = 0; $i < $total; $i++) {
    // Инициализируем переменную для отслеживания, было ли найдено соответствие устройства.
    $device_matched = 0;

    // Если указано местоположение терминала.
    if ($location_id) {
        // Ищем строгое соответствие по названию и местоположению.
        foreach ($devices as $key => $value) {
            // Если ID местоположения устройства совпадает с ID местоположения терминала и название устройства совпадает с командой.
            if (in_array($location_id, $value) && strtolower($devices[$key]['TITLE']) == strtolower($compare_title)) {

                // Обновляем индекс текущего устройства.
                $i = $key; 
                // Помечаем, что устройство найдено.
                $device_matched = 1;
                // Прерываем цикл.
                break;
            }
        }
        // Если устройство не найдено.
        if (!$device_matched) {
            // Ищем строгое соответствие по названию.
            foreach ($devices as $key => $value) {
                // Если название устройства совпадает с командой.
                if (in_array(strtolower($compare_title), strtolower($value))) {
                    // Обновляем индекс текущего устройства.
                    $i = $key;
                    // Помечаем, что устройство найдено.
                    $device_matched = 1;
                    // Прерываем цикл.
                    break;
                }
            }
        }
    }

    // Ищем по старому принципу.
    if (preg_match('/' . preg_quote($devices[$i]['TITLE']) . '/uis', $compare_title)) {
        // Помечаем, что устройство найдено.
        $device_matched = 1;
    } elseif (preg_match('/' . preg_quote($compare_title) . '/uis', $devices[$i]['TITLE'])) {
        // Помечаем, что устройство найдено.
        $device_matched = 1;

    // Если библиотека phpMorphy была успешно загружена.
    } elseif ($phpmorphy_loaded) {
        if (preg_match('/' . preg_quote($devices[$i]['TITLE']) . '/isu', implode('@@@@', $lines), $matches)) {
            // Помечаем, что устройство найдено.
            $device_matched = 1;
        }
    }

    /*
    if (preg_match('/свет над столом/uis',$devices[$i]['TITLE'])) {
        dprint($devices[$i]['TITLE'].' - '.$compare_title.': '.$device_matched);
    }
    */

    // Если устройство было найдено.
    if ($device_matched) {

        // Найдено устройство.
        // Получаем ID устройства.
        $device_id = $devices[$i]['ID'];
        // Получаем тип устройства.
        $device_type = $devices[$i]['TYPE'];
        // Если у устройства есть оригинальное название.
        if ($devices[$i]['ORIGINAL_TITLE'] != '') {
            // Используем оригинальное название.
            $device_title = $devices[$i]['ORIGINAL_TITLE'];
        // Если оригинального названия нет.
        } else {
            // Используем текущее название.
            $device_title = $devices[$i]['TITLE'];
        }

        // Записываем сообщение о том, что устройство было найдено.
        DebMes("Device found for $command ($device_title)", 'simple_devices');

        // Получаем связанный объект устройства.
        $linked_object = $devices[$i]['LINKED_OBJECT'];
        if ($device_type == 'sensor_percentage' || $device_type == 'sensor_humidity') {
            // Формируем сообщение для датчиков с процентами и влажностью.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . '%';
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'sensor_light') {
            // Формируем сообщение для датчиков света.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value');
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'sensor_temp') {
            // Формируем сообщение для датчиков температуры.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . ' ' . LANG_DEVICES_DEGREES;
            // Помечаем, что команда была обработана.
            $processed = 1; 
        } elseif (preg_match('/sensor/', $device_type)) {
            // Формируем сообщение для других датчиков.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . ''; 
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'counter') {
            // Формируем сообщение для счетчиков.
            $reply_say = $device_title . ' ' . gg($linked_object . '.value') . ' ' . gg($linked_object . '.unit');
            // Помечаем, что команда была обработана.
            $processed = 1; 
        } elseif ($device_type == 'openclose') {
            // Формируем сообщение для устройств открытия/закрытия.
            $reply_say = $device_title . ' ' . (gg($linked_object . '.status') ? LANG_DEVICES_STATUS_CLOSED : LANG_DEVICES_STATUS_OPEN);
            // Помечаем, что команда была обработана.
            $processed = 1; 
        } elseif ($device_type == 'smoke' || $device_type == 'leak') {
            // Формируем сообщение для датчиков дыма и утечек.
            $reply_say = $device_title . ' ' . (gg($linked_object . '.status') ? LANG_DEVICES_STATUS_ALARM : LANG_DEVICES_NORMAL_VALUE);
            // Помечаем, что команда была обработана.
            $processed = 1;
        } elseif ($device_type == 'button') {
            // Добавляем код для имитации нажатия кнопки.
            $run_code .= "callMethod('$linked_object.pressed');";
            // Помечаем, что команда была обработана.
            $processed = 1;
            // Помечаем, что необходимо подтверждение выполнения команды.
            $reply_confirm = 1;
        } elseif (
            $device_type == 'controller' ||
            $device_type == 'relay' ||
            $device_type == 'dimmer' ||
            $device_type == 'rgb'
        ) {
            if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $command)) {
                // Формируем сообщение для включения устройства.
                $reply_say = LANG_TURNING_ON . ' ' . $device_title . $add_phrase;
                // Добавляем код для включения устройства.
                $run_code .= "callMethod('$linked_object.turnOn');";
                // Добавляем код для выключения устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.turnOff');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            } elseif (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $command)) {
                // Формируем сообщение для выключения устройства.
                $reply_say = LANG_TURNING_OFF . ' ' . $device_title . $add_phrase;
                // Добавляем код для выключения устройства.
                $run_code .= "callMethod('$linked_object.turnOff');";
                // Добавляем код для включения устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.turnOn');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            }
        } elseif ($device_type == 'openable') {
            if (preg_match('/' . LANG_DEVICES_PATTERN_OPEN . '/uis', $command)) {
                // Формируем сообщение для открытия устройства.
                $reply_say = LANG_TURNING_OPEN . ' ' . $device_title . $add_phrase;
                // Добавляем код для открытия устройства.
                $run_code .= "callMethod('$linked_object.Open');";
                // Добавляем код для закрытия устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.Close');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            } elseif (preg_match('/' . LANG_DEVICES_PATTERN_CLOSE . '/uis', $command)) {
                // Формируем сообщение для закрытия устройства.
                $reply_say = LANG_TURNING_CLOSE . ' ' . $device_title . $add_phrase;
                // Добавляем код для закрытия устройства.
                $run_code .= "callMethod('$linked_object.Close');";
                // Добавляем код для открытия устройства в качестве противодействия.
                $opposite_code .= "callMethod('$linked_object.Open');";
                // Помечаем, что команда была обработана.
                $processed = 1;
                // Помечаем, что необходимо подтверждение выполнения команды.
                //$reply_confirm = 1;
            }
        } elseif ($device_type == 'group') {
            // Разбиваем типы применения группы по запятой.
            $applies_to = explode(',', $devices[$i]['APPLY_TYPES']);
            // Если устройства группы представлены в виде массива.
            if (is_array($devices[$i]['DEVICES'])) {
                // Инициализируем массив для хранения связанных объектов устройств в группе.
                $devices_in_group = array();
                // Проходим по каждому устройству в группе.
                foreach ($devices[$i]['DEVICES'] as $group_device) {
                    // Добавляем связанный объект устройства в массив.
                    $devices_in_group[] = $group_device['LINKED_OBJECT'];
                }
            // Если устройства группы не представлены в виде массива.
            } else {
                // Получаем связанные объекты устройств в группе по свойству.
                $devices_in_group = getObjectsByProperty('group' . $devices[$i]['SYS_NAME'], 1);
            }
            // Выводим содержимое переменной $devices_in_group для отладки.
            //dprint($devices_in_group); 

            // Если устройства группы не представлены в виде массива, пропускаем текущую итерацию цикла.
            if (!is_array($devices_in_group)) continue;

            if (
                in_array('relay', $applies_to) ||
                in_array('dimmer', $applies_to) ||
                in_array('rgb', $applies_to) ||
                0
            ) {
                if (preg_match('/' . LANG_DEVICES_PATTERN_TURNON . '/uis', $command)) {
                    // Формируем сообщение для включения устройства.
                    $reply_say = LANG_TURNING_ON . ' ' . $device_title . $add_phrase;
                    foreach ($devices_in_group as $linked_object) {
                        // Добавляем код для включения каждого устройства в группе.
                        $run_code .= "callMethod('$linked_object.turnOn');";
                        // Добавляем код для выключения каждого устройства в группе в качестве противодействия.
                        $opposite_code .= "callMethod('$linked_object.turnOff');";
                    }
                    // Помечаем, что команда была обработана.
                    $processed = 1;
                    // Помечаем, что необходимо подтверждение выполнения команды.
                    //$reply_confirm = 1;
                } elseif (preg_match('/' . LANG_DEVICES_PATTERN_TURNOFF . '/uis', $command)) {
                    // Формируем сообщение для выключения устройства.
                    $reply_say = LANG_TURNING_OFF . ' ' . $device_title . $add_phrase;
                    foreach ($devices_in_group as $linked_object) {
                        // Добавляем код для выключения каждого устройства в группе.
                        $run_code .= "callMethod('$linked_object.turnOff');";
                        // Добавляем код для включения каждого устройства в группе в качестве противодействия.
                        $opposite_code .= "callMethod('$linked_object.turnOn');";
                    }
                    // Помечаем, что команда была обработана.
                    $processed = 1;
                    // Помечаем, что необходимо подтверждение выполнения команды.
                    //$reply_confirm = 1;
                }
            }

            // Определяем путь к директории дополнений.
            $addons_dir = dirname(__FILE__) . '/addons';
            // Если директория дополнений существует.
            if (is_dir($addons_dir)) {
                // Получаем список файлов в директории дополнений.
                $addon_files = scandir($addons_dir);
                // Проходим по каждому файлу в полученном списке.
                foreach ($addon_files as $file) {
                    // Если имя файла соответствует шаблону.
                    if (preg_match('/\_commands\.php$/', $file)) {
                        // Подключаем файл дополнения.
                        require($addons_dir . '/' . $file);
                    }
                }
            }

            // Если команда была обработана, прерываем цикл.
            if ($processed) break; 

            if ($run_code != '' && $period_delay > 0) {
                // Если есть код для выполнения и задержка больше 0, устанавливаем таймер для выполнения кода после задержки.
                setTimeout('delay' . md5($run_code), $run_code, $period_delay); 
            } elseif ($run_code != '' && $period_run_for > 0 && $opposite_code != '') {
                // Если есть код для выполнения, продолжительность выполнения больше 0 и есть код для противодействия, выполняем код.
                eval($run_code); 
                // Устанавливаем таймер для выполнения кода противодействия после указанного времени.
                setTimeout('opposite' . md5($run_code), $opposite_code, $period_run_for); 
            } elseif ($run_code != '') {
                // Если есть код для выполнения, выполняем его.
                eval($run_code);
                /*
                * TODO использовать eval считается небезопасным, поскольку он выполняет произвольный PHP-код, представленный в виде строки. 
                * Это может создать уязвимости в безопасности и снизить производительность, поскольку он компилирует и выполняет код каждый раз,
                * когда он вызывается. Это может привести к увеличению нагрузки на сервер и снижению производительности.
                */
            }

            if ($reply_say != '') {
                // Если есть сообщение для ответа, отправляем его.
                sayReplySafe($reply_say, 2);
            }

            if ($reply_confirm) {
                // Разбиваем строку подтверждения команды по символу '|'.
                $items = explode('|', LANG_DEVICES_COMMAND_CONFIRMATION);
                // Удаляем пробелы в начале и конце каждого элемента массива.
                $items = array_map('trim', $items);
                // Отправляем случайное сообщение из массива подтверждений.
                sayReplySafe($items[array_rand($items)], 2);
            }

            if ($processed) {
                // Если команда была обработана, помечаем это.
                $details['PROCESSED'] = 1;
                // Помечаем, что необходимо прервать выполнение.
                $details['BREAK'] = 1;
            }
        }
    }
}