IoBroker. Inline меню для Telegram бота

08 ноября 2018, 14:02

В этой статье мы с вами по шагам будем создавать меню для telegram бота Умного Дома. Рекомендую изучить минимальные азы по языку программирования JavaSсript, это облегчит понимание того, что тут вообще происходит.

Приготовления

Сначала необходимо установить драйвера Telegram и Script Engine. Вспомнить как это делается, можно в статье ioBroker - устанавливаем первый драйвер. Не забываем подключить своего бота к драйверу Telegram, как описано в начале статьи - ioBroker - уведомления.

При увеличении количества написанных скриптов, будет нарастать бардак в дереве, поэтому рекомендую сразу приучать себя разбивать скрипты по группам. 

Добавим новую группу в папку common

И назовем ее например Telegram. В этой группе в дальнейшем можно будет создавать все скрипты, которые будут относиться к работе с драйвером Telegram.

Вот теперь можно добавить наш будущий скрипт для меню. Для этого надо выделить созданную группу Telegram и нажать кнопку Новый скрипт

В появившемся окне выбираем нужный тип языка - JavaScript

Поменяем имя на Телеграм бот, и сохраним изменения.

Все готово к созданию меню для Умного дома. 


Создаем Меню

Предварительно необходимо на листике или в уме подготовить набросок древовидной структуры будущего меню

В этой статье попробуем реализовать подобную структуру меню. 

Не обязательно придерживаться разбиения по комнатам, рекомендую в основные ветки выносить управление тем, что чаще всего используется, т.к. допустим для запуска Сцены 3 в зале надо сделать целых 4 нажатия! 

Вызвать Меню - Зал - Сценарии - Сцена 3

Ежедневно запускать сценарий по такому длинному пути быстро надоест :)

Набросаем наше дерево в скрипте

var button = [{name: 'Меню',button: ['Зал', 'Кухня', 'Спальня', 'Ванная', 'Закрыть', 'Меню']},
                {name: 'Зал',button: ['Люстра', 'Зал. Кондиционер', 'Зал. Сцены', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Зал. Сцены', button: ['Кино', 'Весь свет', 'Приглушенный свет', 'Назад', 'Закрыть', 'Зал']},
                {name: 'Кухня', button: ['Кухня. Свет', 'Вентиляция', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Верхний свет', button: ['Включить', 'Выключить', 'Назад', 'Закрыть', 'Кухня']},
                {name: 'Спальня', button: ['Спальня. Свет', 'Спальня. Кондиционер', 'Спальня. Сцены', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Спальня. Свет', button: ['Верхний', 'Бра', 'Назад', 'Закрыть', 'Спальня']},
                    {name: 'Спальня. Сцены', button: ['Сцена 1', 'Сцена 2', 'Назад', 'Закрыть', 'Спальня']},
                {name: 'Ванная', button: ['Ванная. Свет', 'Бойлер', 'Вытяжка', 'Назад', 'Закрыть', 'Меню']},
    ];

Разберем подробнее

В первой строке в квадратных скобках перечисляются основные кнопки (ветки) меню, плюс дополнительно добавляется кнопка Закрыть. Она позволит закрывать меню в чате бота, чтобы у нас не получилось куча сообщений от бота с открытыми менюшками. Ну и в конце текст в кавычках 'Меню' тоже обязателен. В этом месте будет указываться название вышестоящей ветки меню, т.к. первая строка уже является верхушкой дерева, то текст в этом месте дублирует начало.

Вторая строка - переход по дереву ниже на ветку Зал. Соответственно в квадратных скобках уже перечислены кнопки меню Зал. К Закрыть, добавилась кнопка Назад, которая позволит подняться на одну ветку выше и в конце укажем куда - 'Меню'

Третья строка - переход еще ниже в меню Зал. Сцены. Для корректной работы кнопки Назад, в конце пишем - 'Зал'

Отступы для каждой строки сделаны лишь для удобства восприятия структуры меню и никакой функциональности не несут.

Внимательный читатель, надеюсь, обратил внимание что названия веток меню в наброске и в коде отличаются :) Почему так сделано, будет описано дальше.  

Дальнейший код будет описан только в объеме, необходимом для оформления своего меню, плюс краткое пояснение функций.

Добавляем в скрипт весь остальной код. 

var button = [{name: 'Меню',button: ['Зал', 'Кухня', 'Спальня', 'Ванная', 'Закрыть', 'Меню']},
                {name: 'Зал',button: ['Люстра', 'Зал. Кондиционер', 'Зал. Сцены', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Зал. Сцены', button: ['Кино', 'Весь свет', 'Приглушенный свет', 'Назад', 'Закрыть', 'Зал']},
                {name: 'Кухня', button: ['Кухня. Свет', 'Вентиляция', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Верхний свет', button: ['Включить', 'Выключить', 'Назад', 'Закрыть', 'Кухня']},
                {name: 'Спальня', button: ['Спальня. Свет', 'Спальня. Кондиционер', 'Спальня. Сцены', 'Назад', 'Закрыть', 'Меню']},
                    {name: 'Спальня. Свет', button: ['Верхний', 'Бра', 'Назад', 'Закрыть', 'Спальня']},
                    {name: 'Спальня. Сцены', button: ['Сцена 1', 'Сцена 2', 'Назад', 'Закрыть', 'Спальня']},
                {name: 'Ванная', button: ['Ванная. Свет', 'Бойлер', 'Вытяжка', 'Назад', 'Закрыть', 'Меню']},
    ];

var menuUp = 'Меню';
var first_tap = false;
var menu_current;
var topTextGlobal;

on({id: "telegram.0.communicate.request", change: 'any'}, function (obj) {
    command = obj.state.val.substring(obj.state.val.indexOf(']') + 1);
    user = obj.state.val.substring(obj.state.val.indexOf('[')+1, obj.state.val.indexOf(']'));
    //log(command);
    //log(user);
//************************************
// Меню
//************************************    
    if (command ==="/buttons" || command ==="кнопки" || command ==="Кнопки")
        sendTo('telegram', {
            user: user,
            text:   'Показать меню',
            reply_markup: {
                keyboard: [['Показать меню']],
                resize_keyboard:   true,
                one_time_keyboard: true
            }
        });
        
//************************************
//  меню inline
//************************************
    var menu = {
        reply_markup: {
            inline_keyboard: [[],[],[],[],[],[],[]],
        }
    };
    
    if (command === 'Показать меню') command = menuUp;
    log (command);
    if (command === 'Меню') first_tap = true;
    if (command === '◀️ Назад') command = menuUp;
    var but1 = getButtonArray(button, 'name', command).toString();
    
    if (but1.length > 0) {          // проверяем, что строка не пустая
        var but2 = but1.split(','); //преобразуем в массив
        menuUp = but2.pop();        //вырезаем последний элемент

        if (but2.length > 0) {      // проверяем что массив не пуст
            var index = 0;
            for (var i=0, len=but2.length; i<len; i++) {
                menu.reply_markup.inline_keyboard[index].push({ text: but2[i], callback_data: but2[i]});
                if ((i%3 >= 2)&&(index < 6)) index = ++index;
            }

            var topText = funcTopText(command);
            topTextGlobal = command;
            menu_current = menu.reply_markup;
            if (first_tap) {
                sendTo('telegram.0', {user: user, text: topText, parse_mode: 'markdown', reply_markup: menu.reply_markup});
                first_tap = false;
            } else {
                updateMenuButton(user, topText, menu.reply_markup);

            }
        }
    }
    //************************************
    // Команды
    //************************************
    // ищем в тексте команды 
    switch (command) {
    
        case "Закрыть":
            sendTo('telegram', {  
                user: user,
                deleteMessage: {
                    options: {
                        chat_id: getState("telegram.0.communicate.requestChatId").val, 
                        message_id: getState("telegram.0.communicate.requestMessageId").val,
                    }
                }
            });
            break;
    }
});


function updateMenuButton(user, topText, menu){
    sendTo('telegram', { 
        user: user, 
        text: topText,
        editMessageText: { 
            options: { 
                chat_id: getState("telegram.0.communicate.requestChatId").val, 
                message_id: getState("telegram.0.communicate.requestMessageId").val,
                parse_mode: 'markdown',
                reply_markup: menu
            }    
        } 
    });
}

function waitConfirmCommand(obj, command, timeout, ack = false){
    var mySubscription;
    
    var timeID = setTimeout(() => {
        unsubscribe(mySubscription);
        CommandDone('Не выполнено!');
    }, timeout);
    
    mySubscription = on({id: obj, change: 'ne'}, function (data) {
        // unsubscribe after first trigger
        if (ack === true)
            if (data.state.ack) ack = false;
        if (!ack) {    
            updateMenuButton(user, funcTopText(command), menu_current);
            unsubscribe(mySubscription);
            clearTimeout(timeID);
            CommandDone('Выполнено!');
        }
    });
}

function CommandDone(text){
    if (text === '') text = "Выполнено!";
    sendTo('telegram', {
        user: user,
        answerCallbackQuery: {
            text: text,
            showAlert: false
        }
    });
}

function getButtonArray(obj, keyName, Name) {
    var result = [];
    for (var attr in obj) {
        if (obj[attr] && typeof obj[attr] === 'object') {
            result = result.concat(getButtonArray(obj[attr], keyName, Name));
        }
        if (attr === keyName && obj[attr] === Name) {
            result.push(obj.button);
        }
    }
    return result;
}

function stateSelection(state){
    if ((state.val === false) || (state.val === 'false')) return "ВЫКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    if ((state.val === true) || (state.val === 'true')) return "ВКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    return "неопределено";
}

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: Температура 24.2 градуса \n"
            + "*Спальня*: Температура 23.5 градуса \n";
            break;
    }
    return text;
}

Уже на этом этапе можно проверить работу меню. Для этого сохраняем скрипт, запускаем и в Telegram отправляем боту слово Меню (внимание, слово должно быть с большой буквы) или Кнопки


Управление

Переходим к следующему шагу. Добавим в скрипт команды управления оборудованием (свет, бойлер, сценарии, насосы, краны, телевизор, кондиционер и т.д.). Для этого создадим виртуальный выключатель (код добавим в самое начало скрипта)

createState("Test.Switch.command", false);  // Тестовый выключатель света

Сохраним и команда createState создаст новый объект, который можно посмотреть на вкладке Объекты - javascript.0

Дальше вставим в скрипт команды управления светом на кухне на примере виртуального выключателя

case "Включить": //включить свет на кухне
setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, true);
CommandDone('Выполнено!');
break;

case "Выключить": //выключить свет на кухне
setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, false);
CommandDone('Выполнено!');
break;

Должно получиться так

Ранее в статье был момент, когда названия веток (кнопок) немного менялись и стали отличаться от наброска меню. Весь смысл в том, что названия всех кнопок в меню бота должны быть уникальны, это связано с особенностями API Telegram. Подробнее можно почитать тут. Иначе при нажатии на одинаковые названия кнопок в меню, всегда будут выполняться команды только для какой-то одной кнопки, даже если вы на нее не нажимали.

Разберем подробнее, что же мы сделали:

switch - сравнивает выражение со случаями, перечисленными внутри неё, а затем выполняет соответствующие инструкции. Подробнее тут.

command - будет содержать уникальное имя нажатой кнопки

case "Включить" - сравниваем со всеми описанными в секции switch именами кнопок и ищем команды для нажатой кнопки Выключить.

setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, true); - записываем значение True в объект Test.Switch.command. Дополнительно по команде setState можно почитать тут.

CommandDone('Выполнено!'); - вызываем функцию CommandDone и передаем ей текст, который хотим отобразить во всплывающем сообщении при нажатии кнопки. В данном случае текст будет Выполнено!

break; - указываем, что выполнение операций для кнопки Включить завершаем. Если не добавить команду break, JS начнет выполнение следующего case.

Для кнопки Выключить все то же самое, только записываемое значение false.

Сохраняем и пробуем! (результат можно увидеть на вкладке Объекты. Будет меняться значение виртуального выключателя). 

Для элементарного меню и простых команд этого уже достаточно. А для тех, кто хочет большего, продолжим.


Красивости

Начнем добавлять различные красивости: отображение текущего состояния, обновление состояний при нажатии кнопки, красивый вывод состояний, эмодзи и т.д.

За вывод подобной информации отвечает функция funcTopText

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: Температура 24.2 градуса \n"
            + "*Спальня*: Температура 23.5 градуса \n";
            break;
    }
    return text;
}

В функцию автоматически через переменную command передается нажатая кнопка дерева (например Меню, Спальня, Кухня. Свет и т.д.), самый нижний уровень дереве меню передать нельзя (например Кино, Бойлер, Бра и т.д.). И уже в самой функции с помощью уже знакомой конструкции  switch (command) можно добавить вывод необходимой информации о состоянии оборудования или просто какой-то текст для выбранной ветки меню. В примере выше указана ветка Меню, в ней выводим температуры (пока как простой текст) в комнатах Зал и Спальня.

Как сделать форматирование текста жирным или курсивом, можно почитать тут.

Выведем температуру в зале из реального объекта. Надо выбрать из ваших существующих объектов, иначе будет ошибка.

case 'Меню':
            text =  "*Зал*: Температура " + getState("mysensors.0.70.3_TEMP.V_TEMP").val + "°C, \n"
            + "*Спальня*: Температура 23.5 градуса \n";

Из объекта  mysensors.0.70.3_TEMP.V_TEMP считываем значение температуры и подставляем его в строку (подробнее о команде getState тут).

\n – символ новой строки, является эквивалентом символа перевода строки.

Добавим в меню Кухня отображение состояния виртуального выключателя

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: Температура " + getState("mysensors.0.70.3_TEMP.V_TEMP").val + "°C, \n"
            + "*Спальня*: Температура 23.5 градуса \n";
            break;
        case 'Кухня':
            text =  "*Свет*: состояние: " + getState("Test.Switch.command").val + "\n";
            break;
    }
    return text;
}

Состояние false, как-то скучно…  Да объясняй потом жене, ребенку, друзьям что за false такой…

Используем функцию stateSelection(state), которая будет вместо false/true возвращать нормальный текст Вкл/Выкл. и дополнительно выводить время подачи команды (или любой другой текст на ваше усмотрение)

function stateSelection(state){
    if ((state.val === false) || (state.val === 'false')) return "ВЫКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    if ((state.val === true) || (state.val === 'true')) return "ВКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    return "неопределено";
}

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: Температура " + getState("mysensors.0.70.3_TEMP.V_TEMP").val + "°C, \n"
            + "*Спальня*: Температура 23.5 градуса \n";
            break;
        case 'Кухня':
            text =  "*Свет*: состояние: " + stateSelection(getState("Test.Switch.command")) + "\n";
            break;
    }
    return text;
}

Так гораздо лучше и понятней смотрится!

В функцию stateSelection(state) при необходимости можно добавить другие типы состояний, если они будут выводиться в заголовок меню.

Может еще улучшим? Сделаем, чтобы при нажатии кнопки Кухня. Свет сразу в заголовке менялось состояние, для этого воспользуемся функцией

function waitConfirmCommand(obj, command, timeout, ack = false){
    var mySubscription;
    
    var timeID = setTimeout(() => {
        unsubscribe(mySubscription);
        CommandDone('Не выполнено!');
    }, timeout);
    
    mySubscription = on({id: obj, change: 'ne'}, function (data) {
        // unsubscribe after first trigger
        if (ack === true)
            if (data.state.ack) ack = false;
        if (!ack) {    
            updateMenuButton(user, funcTopText(command), menu_current);
            unsubscribe(mySubscription);
            clearTimeout(timeID);
            CommandDone('Выполнено!');
        }
    });
}

Функция подписывается на переданный объект obj и в течение заданного количества миллисекунд timeout ожидает изменения объекта obj. Если событие произошло и была задана дополнительно (но не обязательно) проверка флага ACK, проверяется на условие ack = true. Если совпало или не была задана проверка флага ACK – отобразится текст Выполнено. Если не совпало – Не выполнено. После этого функция отписывается от объекта, чтобы в будущем снова не реагировать на него.

Внесем изменения в скрипт

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: Температура " + getState("mysensors.0.70.3_TEMP.V_TEMP").val + "°C, \n"
            + "*Спальня*: Температура 23.5 градуса \n";
            break;
case 'Кухня':
            text =  "*Свет*: " + stateSelection(getState("javascript.0.Test.Switch.command")) + "\n";
            break;
        
case 'Кухня. Свет':
            text =  "*Свет*: " + stateSelection(getState("javascript.0.Test.Switch.command")) + "\n";
            break;
    }
    return text;
}
case "Включить": //включить свет на кухне
            setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, true);
            waitConfirmCommand("javascript.0.Test.Switch.command", topTextGlobal, 2000, true);
        break;
        
        case "Выключить": //выключить свет на кухне
            setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, false);
            waitConfirmCommand("javascript.0.Test.Switch.command", topTextGlobal, 2000);
        break;

javascript.0.Test.Switch.command – объект для проверки выполнения команды. Т.к. у нас нет отдельного объекта для обратной связи, используем для этой цели объект-команду.

2000 – время ожидания в миллисекундах

topTextGlobalэту переменную ставим всегда!

Для кнопки Включить добавили проверку флага ACK, для кнопки Выключить нет. Сохраняем и пробуем что получилось.

Уберем проверку флага ACK для кнопки Включить и снова пробуем.

Не забываем смотреть на всплывающие сообщения!

Надеюсь, суть уловили ?


Эмодзи

Пришла очередь добавить символы Эмодзи. Для этого заходим в библиотеку один или два. Выбираем подходящие символы для меню. Возьмем для кнопки Назад следующий символ

Выделяем, копируем и вставим в наш скрипт.

Внимание! Менять надо во всех местах скрипта, где использовано слово Назад.

Замену лучше делать через Поиск комбинацией клавиш ctrl+H

Сохраняем и любуемся результатом

Надеюсь у вас все получилось ?

Итоговый скрипт меню

createState("Test.Switch.command", false);  // Тестовый выключатель света


var button = [{name: 'Меню',button: ['Зал', 'Кухня', '?', 'Ванная', 'Закрыть', 'Меню']},
                {name: 'Зал',button: ['Люстра', 'Зал. Кондиционер', 'Зал. Сцены', '◀️ Назад', 'Закрыть', 'Меню']},
                    {name: 'Зал. Сцены', button: ['Кино', 'Весь свет', 'Приглушенный свет', '◀️ Назад', 'Закрыть', 'Зал']},
                {name: 'Кухня', button: ['Кухня ?', 'Вентиляция', '◀️ Назад', 'Закрыть', 'Меню']},
                    {name: 'Кухня ?', button: ['Включить', 'Выключить', '◀️ Назад', 'Закрыть', 'Кухня']},
                {name: '?', button: ['? Спальня. Свет', '? Кондиционер', '? Сцены', '◀️ Назад', 'Закрыть', 'Меню']},
                    {name: '? Свет', button: ['Верхний', 'Бра', '◀️ Назад', 'Закрыть', '?']},
                    {name: '? Сцены', button: ['Сцена 1', 'Сцена 2', '◀️ Назад', 'Закрыть', '?']},
                {name: 'Ванная', button: ['Ванная. Свет', 'Бойлер', 'Вытяжка', '◀️ Назад', 'Закрыть', 'Меню']},
    ];

var menuUp = 'Меню';
var first_tap = false;
var menu_current;
var topTextGlobal;

on({id: "telegram.0.communicate.request", change: 'any'}, function (obj) {
    command = obj.state.val.substring(obj.state.val.indexOf(']') + 1);
    user = obj.state.val.substring(obj.state.val.indexOf('[')+1, obj.state.val.indexOf(']'));
    //log(command);
    //log(user);
//************************************
// Меню
//************************************    
    if (command ==="/buttons" || command ==="кнопки" || command ==="Кнопки")
        sendTo('telegram', {
            user: user,
            text:   'Показать меню',
            reply_markup: {
                keyboard: [['Показать меню']],
                resize_keyboard:   true,
                one_time_keyboard: true
            }
        });
        
//************************************
//  меню inline
//************************************
    var menu = {
        reply_markup: {
            inline_keyboard: [[],[],[],[],[],[],[]],
        }
    };
    
    if (command === 'Показать меню') command = menuUp;
    log (command);
    if (command === 'Меню') first_tap = true;
    if (command === '◀️ Назад') command = menuUp;
    var but1 = getButtonArray(button, 'name', command).toString();
    
    if (but1.length > 0) {          // проверяем, что строка не пустая
        var but2 = but1.split(','); //преобразуем в массив
        menuUp = but2.pop();        //вырезаем последний элемент

        if (but2.length > 0) {      // проверяем что массив не пуст
            var index = 0;
            for (var i=0, len=but2.length; i<len; i++) {
                menu.reply_markup.inline_keyboard[index].push({ text: but2[i], callback_data: but2[i]});
                if ((i%3 >= 2)&&(index < 6)) index = ++index;
            }

            var topText = funcTopText(command);
            topTextGlobal = command;
            menu_current = menu.reply_markup;
            if (first_tap) {
                sendTo('telegram.0', {user: user, text: topText, parse_mode: 'markdown', reply_markup: menu.reply_markup});
                first_tap = false;
            } else {
                updateMenuButton(user, topText, menu.reply_markup);

            }
        }
    }
    //************************************
    // Команды
    //************************************
    // ищем в тексте команды 
    switch (command) {
        case "Включить": //включить свет на кухне
            setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, true);
            waitConfirmCommand("javascript.0.Test.Switch.command", topTextGlobal, 2000, true);
        break;
        
        case "Выключить": //выключить свет на кухне
            setState("javascript.0.Test.Switch.command"/*Test.Switch.command*/, false);
            waitConfirmCommand("javascript.0.Test.Switch.command", topTextGlobal, 2000);
        break; 
        
        case "Закрыть":
            sendTo('telegram', {  
                user: user,
                deleteMessage: {
                    options: {
                        chat_id: getState("telegram.0.communicate.requestChatId").val, 
                        message_id: getState("telegram.0.communicate.requestMessageId").val,
                    }
                }
            });
            break;
    }
});


function updateMenuButton(user, topText, menu){
    sendTo('telegram', { 
        user: user, 
        text: topText,
        editMessageText: { 
            options: { 
                chat_id: getState("telegram.0.communicate.requestChatId").val, 
                message_id: getState("telegram.0.communicate.requestMessageId").val,
                parse_mode: 'markdown',
                reply_markup: menu
            }    
        } 
    });
}

function waitConfirmCommand(obj, command, timeout, ack = false){
    var mySubscription;
    
    var timeID = setTimeout(() => {
        unsubscribe(mySubscription);
        CommandDone('Не выполнено!');
    }, timeout);

    mySubscription = on({id: obj, change: 'any'}, function (data) {
        // unsubscribe after first trigger
        if (ack === true)
            if (data.state.ack) ack = false;
        if (!ack) {    
            updateMenuButton(user, funcTopText(command), menu_current);
            unsubscribe(mySubscription);
            clearTimeout(timeID);
            CommandDone('Выполнено!');
        }
    });
}

function CommandDone(text){
    if (text === '') text = "Выполнено!";
    sendTo('telegram', {
        user: user,
        answerCallbackQuery: {
            text: text,
            showAlert: false
        }
    });
}

function getButtonArray(obj, keyName, Name) {
    var result = [];
    for (var attr in obj) {
        if (obj[attr] && typeof obj[attr] === 'object') {
            result = result.concat(getButtonArray(obj[attr], keyName, Name));
        }
        if (attr === keyName && obj[attr] === Name) {
            result.push(obj.button);
        }
    }
    return result;
}

function stateSelection(state){
    if ((state.val === false) || (state.val === 'false')) return "ВЫКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    if ((state.val === true) || (state.val === 'true')) return "ВКЛ. в " + formatDate(state.lc, "SS:mm:ss TT.MM");
    return "неопределено";
}

function funcTopText (command){
    var text = command;
    switch (command) {
        case 'Меню':
            text =  "*Зал*: ?️ " + getState("mysensors.0.70.3_TEMP.V_TEMP").val + "°C, \n"
            + "?  ? 23.5 градуса \n";
            break;
        case 'Кухня':
            text =  "? " + stateSelection(getState("javascript.0.Test.Switch.command")) + "\n";
            break;
        case 'Кухня ?':
            text =  "? " + stateSelection(getState("javascript.0.Test.Switch.command")) + "\n";
            break;
    }
    return text;
}

Лайфхак

Через @botFather можно добавить команду вызова меню 

Тогда для вызова меню будет достаточно ввести символ / и в выпадающем меню выбрать /buttons. В результате появится кнопочка вызова меню рядом с меню смайликов.


Все новости мира умных домов - t.me/SprutAI_News

Остались вопросы? Мы в Telegram - t.me/soprut

Хочешь умный дом но нет времени разбираться?
Посмотри примеры работ и выбери себе интегратора.
  1. Магомед Сападиев (Sapadiev)

    много нового для себя исчерпал! 👍

К списку статей

Похожие статьи

02 ноября 2018, 09:32
установка ioBroker на ОС Linux
06 ноября 2018, 13:21
ioBroker - уведомления через Телеграм и вывод звука через окно браузера
03 ноября 2018, 10:02
Продолжени статей по ioBroker. ioBroker - Визуализация.
07 ноября 2018, 07:58
ioBroker - управление через бота телеграм, меню кнопок для телеграм. Короткий обзор алтернативных методов.
15 марта 2019, 10:08
Расскажу как автоматизировать списки покупок, дел и т.д. через Home Assistant + TODOist и слать красивые списки в Telegram. Примеры из личной жизни прилагаются.
26 июня 2018, 07:52
У многих, кто интересуется домашней автоматизацией (“Умный дом”), на слуху известные открытые системы автоматизации, на которых можно построить управление: OpenHab, MajorDoMo, Home Assistant, Domoticz, HomeBridge. Но если немного расширить диапазон наблюдения, то можно обнаружить множество программных систем, которые могут предложить схожий функционал, а местами даже больший. Сегодня, расскажу об одной из них — ioBroker. Расскажу обзорно, без погружения в подробности реализации, т.к. каждый элемент потребует отдельной статьи (если потребуется — будет статья).
08 ноября 2018, 17:42
ioBroker - ham - HomeKit использование плагинов для homebridge в ioBroker
02 ноября 2018, 09:37
обзор админки ioBroker
31 октября 2018, 08:58
Вводная статья о возможностях IoBroker. Как установить. Как завести в Apple Homekit. Как собрать в едино совершенно разные системы и протоколы.
03 ноября 2018, 09:50
ioBroker - устанавливаем первый драйвер на примере яваскрипт + поробуем создать примитивную логику работы скрипта