Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Русский
    3. ioBroker
    4. Скрипты
    5. ioBroker скрипты
    6. Голосовое меню

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    Голосовое меню

    This topic has been deleted. Only users with topic management privileges can see it.
    • I
      instalator last edited by

      Один из вариантов реализации голосового меню на примере меню для заказа воды.

      Сейчас скрипт получился такой

      ! ```
      `menu = [
      [
      ['Заказываем как обычно?', 'да', 'нет', [0,1]],
      ['На какой день?', 'сегодня', 'завтра'],
      ['На какое время?', 'утро', 'обед', 'вечер'],
      ['Я делаю заказ']

      ],
      [   
          ['Какое количество?', 'одну', 'две'],
          ['Какое количество?', 'одну', 'две'],
          ['На какой день?', 'сегодня', 'завтра'],
          ['На какое время?', 'утро', 'обед', 'вечер']
      ]
      

      ];
      var collect = {
      hours: {
      1: '9:00-13:00 (утро)',
      2: '13:01-17:00 (день)',
      3: '17:01-21:00 (вечер)',
      4: '18:01-21:00 (вечер)'
      },
      Product: {
      id: [1204],
      price: [190],
      discprice: [180]
      }
      };
      var order = {
      productsid: 1204,
      price: 190,
      discountprice: 180,
      count: 1,
      date: '16-03-2016',
      hours: '18:01-21:00 (вечер)',
      periodid: 4
      };
      ////////////////////////////////////
      function resultOrder(item, num){
      log('RESULT - ' + result);
      //var order = getState('javascript.0.WaterOrder.order').val;
      var prodid = JSON.parse(getState('javascript.0.WaterOrder.ProductsId').val);
      var price = JSON.parse(getState('javascript.0.WaterOrder.price').val);
      var discprice = JSON.parse(getState('javascript.0.WaterOrder.discountPrice').val);

      var date = new Date();
      var day = date.getDate();
      var month = ((date.getMonth()+1)<10?'0':'')+(date.getMonth()+1);
      var year = date.getFullYear();
      
      if (num === 0){
          var datedost;
          if (result[1] === 0){
              // productsid, price, discountprice, count, date, hours, periodid
              datedost = 'сегодня';
              order.date = day+ '-' +month+ '-' +year;
          }
          else if (result[1] === 1){
              datedost = 'завтра';
              order.date = day+1 + '-' +month+ '-' +year;
          }
          if (result[2] === 0){
              order.hours = collect.hours['1'];
              order.periodid = 1;
          }
          else if (result[2] === 1){
              order.hours = collect.hours['2'];
              order.periodid = 2;
          }
          else if (result[2] === 2){
              order.hours = collect.hours['4'];
              order.periodid = 4;
          }
      
          var e = prodid.indexOf(order.productsid);
          if (~e){
              order.price = price[e];
              order.discountprice = discprice[e];
          }
          var per = order.hours.replace(/-/g, ' часов до ');
          setState('sayit.0.tts.text',  'Я заказываю '+ order.count + ' бутыль за '+ order.count * order.price + ' руб. .' + 'Доставка ' + datedost + ' в период с ' + per);
          setState('javascript.0.WaterOrder.order', JSON.stringify(order));
          setState('javascript.0.WaterOrder.Start', true);
      }
      if (num === 1){
      
      }
      

      }
      /////////////////////////////////////////////////////////////////////////////////
      /////////////////////////////////////////////////////////////////////////////////
      var result = [];
      var item;
      var num;
      var wait;
      var сonf;
      var flag = false;
      var step;
      var notFound = 0;
      var voice = '';

      ! on({id: 'javascript.0.Voice.Command', change: 'any'}, function (obj) {
      voice = obj.newState.val.toLowerCase();
      VoiceCommands (voice);
      });
      ///////////////////////////////////////
      function verify(confirm){
      if (confirm === true){
      var lastAnswer = result[result.length - 1];
      if (lastAnswer === undefined) {
      console.error('No last answer found: ' + JSON.stringify(result));
      return;
      }
      var possibleValues = menu[num][item][menu[num][item].length - 1]; // always last element of submenu
      if (possibleValues === undefined) {
      console.error('No possible values found: ' + JSON.stringify(menu[num][item]));
      return;
      }
      if (typeof possibleValues === 'object' && possibleValues[lastAnswer] !== 0){
      clear();
      item = 0;
      step = 0;
      num = possibleValues[lastAnswer];
      whiles(item, num);
      log('Переходим в подменю меню');
      } else {
      //log('Подтверждение - ' + confirm);
      log('Массив результата - ' + JSON.stringify(result));
      item++;
      if (item >= menu[num].length -1){
      clear();
      resultOrder(item, num);
      } else {
      whiles(item, num);
      }
      }
      } else {
      setState('javascript.0.Voice.menu', false);
      clear();
      setState('sayit.0.tts.text', 'Ошибка! Заказ отменён.');
      }
      }
      ///////////////////////////////////////
      function whiles(item, num){
      log('Переменные: num-' + num +' item-'+ item);
      log('menu[num][item]' + menu[num][item]);
      //log('SayIt - ' + menu[num][item][0]);
      var say = menu[num][item].slice();
      if (typeof menu[num][item][menu[num][item].length-1] === 'object'){
      say.pop();
      }
      log('say ' + JSON.stringify(say));
      setState('sayit.0.tts.text', JSON.stringify(say));
      Confirm(num, item, function (confirm) {
      verify(confirm);
      //setState('sayit.0.tts.text', menu[i][0]);
      });
      }
      /////////////////////////////////////////////
      function clear(){
      log('Отписались');
      unsubscribe(сonf);
      clearTimeout(wait);
      }
      ///////////////////////////////////////
      function Confirm (num, item, callback){
      wait = setTimeout(function() {
      setState('javascript.0.Voice.menu', false);
      //setState('sayit.0.tts.text', 'Заказ отменён по таймауту');
      unsubscribe(сonf);
      callback (false);
      }, 40000);
      сonf = on({id: 'javascript.0.Voice.Command', change: 'any'}, function (obj) {
      var voice = obj.newState.val.toLowerCase();
      var Arrphrase = menu[num][item].slice();
      Arrphrase.splice(0, 1);
      var el;
      Arrphrase.forEach(function(itm, i, arr) {
      //log ('i - '+ i +' Ищем в '+voice+ ' - '+ itm+ ' Массив -' + arr);
      if (~voice.indexOf(itm)){
      flag = true;
      el = i;
      }
      if (i >= Arrphrase.length-1){
      if (flag){
      result[step] = el;
      log('Номер найденного элемента в массиве - ' + el);
      step++;
      flag = false;
      clear();
      callback (true);
      } else {
      flag = false;
      if (notFound > 0){
      clear();
      callback (false);
      } else {
      notFound++;
      setState('sayit.0.tts.text', 'Не поняла, повторите');
      clear();
      Confirm (num, item, function (confirm, cmd) {
      verify(confirm, menu);
      });
      whiles(item, num);
      }
      }
      }

          });
      });
      

      }
      ////////////////////////////////////////
      function VoiceCommands (voice){
      log('Вся переменная - ' + voice);
      if ((found('закаж') || found('заказ')) && found('вод')){
      setState('javascript.0.Voice.menu', true);
      setState('javascript.0.WaterOrder.Update', true);
      item = 0;
      num = 0;
      step = 0;
      whiles(item, num);
      }
      ///////
      function found (cmd){
      cmd = String(cmd);
      if (~voice.indexOf(cmd)){
      return true;
      } else {
      return false;
      }
      }
      }`

      ! Скрипт под спойлером обновлен.[/i]

      1 Reply Last reply Reply Quote 0
      • B
        bondrogeen last edited by

        Для себя пока решил обрабатывать голос сторонними программами, такими как АССИСТЕНТ ДУСЯ, а результат выводить в iobroker.

        1 Reply Last reply Reply Quote 0
        • I
          instalator last edited by

          @bondrogeen:

          Для себя пока решил обрабатывать голос сторонними программами, такими как АССИСТЕНТ ДУСЯ, а результат выводить в iobroker. `
          Ты сделал в дусе меню? Смысл темы сделать диалог с уд

          1 Reply Last reply Reply Quote 0
          • B
            bondrogeen last edited by

            @instalator:

            @bondrogeen:

            Для себя пока решил обрабатывать голос сторонними программами, такими как АССИСТЕНТ ДУСЯ, а результат выводить в iobroker. Ты сделал в дусе меню? Смысл темы сделать диалог с уд

            Нет.

            Пока задачи такой не стоит.

            Я имел ввиду переложить задачу на распознавание речи, обработку синтаксиса, диалог на сторонние программы.

            В процессе диалога забирать или отправлять команды iobroker.

            В дуси есть "скрипты" где можно как раз обрабатывать все это.

            П.С. что заказывать собрались?

            1 Reply Last reply Reply Quote 0
            • I
              instalator last edited by

              @bondrogeen:

              @instalator:

              @bondrogeen:

              Для себя пока решил обрабатывать голос сторонними программами, такими как АССИСТЕНТ ДУСЯ, а результат выводить в iobroker. Ты сделал в дусе меню? Смысл темы сделать диалог с уд
              Делаю голосовое управления скриптом заказа воды

              Нет.

              Пока задачи такой не стоит.

              Я имел ввиду переложить задачу на распознавание речи, обработку синтаксиса, диалог на сторонние программы.

              В процессе диалога забирать или отправлять команды iobroker.

              В дуси есть "скрипты" где можно как раз обрабатывать все это.

              П.С. что заказывать собрались? `
              Делаю голосовое управление скриптом заказа воды

              1 Reply Last reply Reply Quote 0
              • Bluefox
                Bluefox last edited by

                @instalator:

                PS/ BlueFox в переменных(состояниях) IoB можно же хранить объекты и массивы? `
                Да можно, как минимум в JSON.

                1 Reply Last reply Reply Quote 0
                • I
                  instalator last edited by

                  Обновил скрипт меню.

                  1 Reply Last reply Reply Quote 0
                  • H
                    Haus last edited by

                    @instalator:

                    Один из вариантов реализации голосового меню на примере меню для заказа воды.

                    Сейчас скрипт получился такой

                    ! ```
                    `menu = [
                    [
                    ['Заказываем как обычно?', 'да', 'нет', [0,1]],
                    ['На какой день?', 'сегодня', 'завтра'],
                    ['На какое время?', 'утро', 'обед', 'вечер'],
                    ['Я делаю заказ']

                    ],
                    [   
                        ['Какое количество?', 'одну', 'две'],
                        ['Какое количество?', 'одну', 'две'],
                        ['На какой день?', 'сегодня', 'завтра'],
                        ['На какое время?', 'утро', 'обед', 'вечер']
                    ]
                    

                    ];
                    var collect = {
                    hours: {
                    1: '9:00-13:00 (утро)',
                    2: '13:01-17:00 (день)',
                    3: '17:01-21:00 (вечер)',
                    4: '18:01-21:00 (вечер)'
                    },
                    Product: {
                    id: [1204],
                    price: [190],
                    discprice: [180]
                    }
                    };
                    var order = {
                    productsid: 1204,
                    price: 190,
                    discountprice: 180,
                    count: 1,
                    date: '16-03-2016',
                    hours: '18:01-21:00 (вечер)',
                    periodid: 4
                    };
                    ////////////////////////////////////
                    function resultOrder(item, num){
                    log('RESULT - ' + result);
                    //var order = getState('javascript.0.WaterOrder.order').val;
                    var prodid = JSON.parse(getState('javascript.0.WaterOrder.ProductsId').val);
                    var price = JSON.parse(getState('javascript.0.WaterOrder.price').val);
                    var discprice = JSON.parse(getState('javascript.0.WaterOrder.discountPrice').val);

                    var date = new Date();
                    var day = date.getDate();
                    var month = ((date.getMonth()+1)<10?'0':'')+(date.getMonth()+1);
                    var year = date.getFullYear();
                    
                    if (num === 0){
                        var datedost;
                        if (result[1] === 0){
                            // productsid, price, discountprice, count, date, hours, periodid
                            datedost = 'сегодня';
                            order.date = day+ '-' +month+ '-' +year;
                        }
                        else if (result[1] === 1){
                            datedost = 'завтра';
                            order.date = day+1 + '-' +month+ '-' +year;
                        }
                        if (result[2] === 0){
                            order.hours = collect.hours['1'];
                            order.periodid = 1;
                        }
                        else if (result[2] === 1){
                            order.hours = collect.hours['2'];
                            order.periodid = 2;
                        }
                        else if (result[2] === 2){
                            order.hours = collect.hours['4'];
                            order.periodid = 4;
                        }
                        
                        var e = prodid.indexOf(order.productsid);
                        if (~e){
                            order.price = price[e];
                            order.discountprice = discprice[e];
                        }
                        var per = order.hours.replace(/-/g, ' часов до ');
                        setState('sayit.0.tts.text',  'Я заказываю '+ order.count + ' бутыль за '+ order.count * order.price + ' руб. .' + 'Доставка ' + datedost + ' в период с ' + per);
                        setState('javascript.0.WaterOrder.order', JSON.stringify(order));
                        setState('javascript.0.WaterOrder.Start', true);
                    }
                    if (num === 1){
                        
                    }
                    

                    }
                    /////////////////////////////////////////////////////////////////////////////////
                    /////////////////////////////////////////////////////////////////////////////////
                    var result = [];
                    var item;
                    var num;
                    var wait;
                    var сonf;
                    var flag = false;
                    var step;
                    var notFound = 0;
                    var voice = '';

                    ! on({id: 'javascript.0.Voice.Command', change: 'any'}, function (obj) {
                    voice = obj.newState.val.toLowerCase();
                    VoiceCommands (voice);
                    });
                    ///////////////////////////////////////
                    function verify(confirm){
                    if (confirm === true){
                    var lastAnswer = result[result.length - 1];
                    if (lastAnswer === undefined) {
                    console.error('No last answer found: ' + JSON.stringify(result));
                    return;
                    }
                    var possibleValues = menu[num][item][menu[num][item].length - 1]; // always last element of submenu
                    if (possibleValues === undefined) {
                    console.error('No possible values found: ' + JSON.stringify(menu[num][item]));
                    return;
                    }
                    if (typeof possibleValues === 'object' && possibleValues[lastAnswer] !== 0){
                    clear();
                    item = 0;
                    step = 0;
                    num = possibleValues[lastAnswer];
                    whiles(item, num);
                    log('Переходим в подменю меню');
                    } else {
                    //log('Подтверждение - ' + confirm);
                    log('Массив результата - ' + JSON.stringify(result));
                    item++;
                    if (item >= menu[num].length -1){
                    clear();
                    resultOrder(item, num);
                    } else {
                    whiles(item, num);
                    }
                    }
                    } else {
                    setState('javascript.0.Voice.menu', false);
                    clear();
                    setState('sayit.0.tts.text', 'Ошибка! Заказ отменён.');
                    }
                    }
                    ///////////////////////////////////////
                    function whiles(item, num){
                    log('Переменные: num-' + num +' item-'+ item);
                    log('menu[num][item]' + menu[num][item]);
                    //log('SayIt - ' + menu[num][item][0]);
                    var say = menu[num][item].slice();
                    if (typeof menu[num][item][menu[num][item].length-1] === 'object'){
                    say.pop();
                    }
                    log('say ' + JSON.stringify(say));
                    setState('sayit.0.tts.text', JSON.stringify(say));
                    Confirm(num, item, function (confirm) {
                    verify(confirm);
                    //setState('sayit.0.tts.text', menu[i][0]);
                    });
                    }
                    /////////////////////////////////////////////
                    function clear(){
                    log('Отписались');
                    unsubscribe(сonf);
                    clearTimeout(wait);
                    }
                    ///////////////////////////////////////
                    function Confirm (num, item, callback){
                    wait = setTimeout(function() {
                    setState('javascript.0.Voice.menu', false);
                    //setState('sayit.0.tts.text', 'Заказ отменён по таймауту');
                    unsubscribe(сonf);
                    callback (false);
                    }, 40000);
                    сonf = on({id: 'javascript.0.Voice.Command', change: 'any'}, function (obj) {
                    var voice = obj.newState.val.toLowerCase();
                    var Arrphrase = menu[num][item].slice();
                    Arrphrase.splice(0, 1);
                    var el;
                    Arrphrase.forEach(function(itm, i, arr) {
                    //log ('i - '+ i +' Ищем в '+voice+ ' - '+ itm+ ' Массив -' + arr);
                    if (~voice.indexOf(itm)){
                    flag = true;
                    el = i;
                    }
                    if (i >= Arrphrase.length-1){
                    if (flag){
                    result[step] = el;
                    log('Номер найденного элемента в массиве - ' + el);
                    step++;
                    flag = false;
                    clear();
                    callback (true);
                    } else {
                    flag = false;
                    if (notFound > 0){
                    clear();
                    callback (false);
                    } else {
                    notFound++;
                    setState('sayit.0.tts.text', 'Не поняла, повторите');
                    clear();
                    Confirm (num, item, function (confirm, cmd) {
                    verify(confirm, menu);
                    });
                    whiles(item, num);
                    }
                    }
                    }

                        });
                    

                    });
                    }
                    ////////////////////////////////////////
                    function VoiceCommands (voice){
                    log('Вся переменная - ' + voice);
                    if ((found('закаж') || found('заказ')) && found('вод')){
                    setState('javascript.0.Voice.menu', true);
                    setState('javascript.0.WaterOrder.Update', true);
                    item = 0;
                    num = 0;
                    step = 0;
                    whiles(item, num);
                    }
                    ///////
                    function found (cmd){
                    cmd = String(cmd);
                    if (~voice.indexOf(cmd)){
                    return true;
                    } else {
                    return false;
                    }
                    }
                    }`

                    ! Скрипт под спойлером обновлен.
                    ! Привет пробую разобраться с твоим скриптом, создал переменные
                    ! ~~[code]~~createState('WaterOrder.order',''); createState('WaterOrder.ProductsId',''); createState('WaterOrder.price',''); createState('WaterOrder.discountPrice',''); createState('WaterOrder.Start',''); createState('Voice.Command',''); createState('Voice.menu',''); createState('WaterOrder.Update','');[/code]
                    ! ругается после ответа, что не так?
                    ! >! ~~[spoiler]~~17:31:56.229 [info] javascript.2 script.js.AWaterOrder: Вся переменная - заказ воды
                    ! 17:31:56.231 [info] javascript.2 script.js.AWaterOrder: Переменные: num-0 item-0
                    ! 17:31:56.231 [info] javascript.2 script.js.AWaterOrder: menu[num][item]Заказываем как обычно?,да,нет,0,1
                    ! 17:31:56.232 [info] javascript.2 script.js.AWaterOrder: say ["Заказываем как обычно?","да","нет"]
                    ! 17:32:09.189 [info] javascript.2 script.js.AWaterOrder: Вся переменная - да
                    ! 17:32:09.194 [info] javascript.2 script.js.AWaterOrder: Номер найденного элемента в массиве - 0
                    ! 17:32:09.194 [info] javascript.2 script.js.AWaterOrder: Отписались
                    ! 17:32:09.194 [info] javascript.2 script.js.AWaterOrder: Массив результата - [0]
                    ! 17:32:09.194 [info] javascript.2 script.js.AWaterOrder: Переменные: num-0 item-1
                    ! 17:32:09.194 [info] javascript.2 script.js.AWaterOrder: menu[num][item]На какой день?,сегодня,завтра
                    ! 17:32:09.195 [info] javascript.2 script.js.AWaterOrder: say ["На какой день?","сегодня","завтра"]
                    ! 17:32:21.366 [info] javascript.2 script.js.AWaterOrder: Вся переменная - сегодня
                    ! 17:32:21.367 [info] javascript.2 script.js.AWaterOrder: Номер найденного элемента в массиве - 0
                    ! 17:32:21.367 [info] javascript.2 script.js.AWaterOrder: Отписались
                    ! 17:32:21.369 [info] javascript.2 script.js.AWaterOrder: Массив результата - [0,0]
                    ! 17:32:21.369 [info] javascript.2 script.js.AWaterOrder: Переменные: num-0 item-2
                    ! 17:32:21.369 [info] javascript.2 script.js.AWaterOrder: menu[num][item]На какое время?,утро,обед,вечер
                    ! 17:32:21.370 [info] javascript.2 script.js.AWaterOrder: say ["На какое время?","утро","обед","вечер"]
                    ! 17:32:30.823 [info] javascript.2 script.js.AWaterOrder: Вся переменная - вечер
                    ! 17:32:30.823 [info] javascript.2 script.js.AWaterOrder: Номер найденного элемента в массиве - 2
                    ! 17:32:30.824 [info] javascript.2 script.js.AWaterOrder: Отписались
                    ! 17:32:30.824 [info] javascript.2 script.js.AWaterOrder: Массив результата - [0,0,2]
                    ! 17:32:30.824 [info] javascript.2 script.js.AWaterOrder: Отписались
                    ! 17:32:30.824 [info] javascript.2 script.js.AWaterOrder: RESULT - 0,0,2
                    ! 17:32:30.825 [error] SyntaxError: Unexpected end of input at Object.parse (native) at resultOrder (script.js.AWaterOrder:75:23) at verify (script.js.AWaterOrder:164:17) at script.js.AWaterOrder:187:13 at script.js.AWaterOrder:223:21 at Array.forEach (native) at Object. (script.js.AWaterOrder:210:18) at Object.subs.callback (/opt/iobroker/node_modules/iobroker.javascript/javascript.js:1206:48) at /opt/iobroker/node_modules/iobroker.javascript/javascript.js:541:48 at getObjectEnums (/opt/iobroker/node_modules/iobroker.javascript/javascript.js:2430:17)[/spoiler][/i]
                    ``` `

                    1 Reply Last reply Reply Quote 0
                    • I
                      instalator last edited by

                      @Haus:

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

                      createState('WaterOrder.order','');
                      createState('WaterOrder.ProductsId','');
                      createState('WaterOrder.price','');
                      createState('WaterOrder.discountPrice','');
                      createState('WaterOrder.Start','');
                      createState('Voice.Command','');
                      createState('Voice.menu','');
                      createState('WaterOrder.Update','');
                      

                      ругается после ответа, что не так? `

                      У тебя видимо объекты пустые, у меня там массивы данных
                      filename="е4343.png" index="0">~~
                      Смысл меню что в итоге получить массив с выбранными данными, а его уже обрабатывать можно как тебе нужно.

                      У меня есть вторая часть скрипта заказа, которая парсит сайт, наполняет эти объекты актуальными данными и отправляет сформированный заказ.

                      1 Reply Last reply Reply Quote 0
                      • H
                        Haus last edited by

                        @instalator:

                        @Haus:

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

                        createState('WaterOrder.order','');
                        createState('WaterOrder.ProductsId','');
                        createState('WaterOrder.price','');
                        createState('WaterOrder.discountPrice','');
                        createState('WaterOrder.Start','');
                        createState('Voice.Command','');
                        createState('Voice.menu','');
                        createState('WaterOrder.Update','');
                        

                        ругается после ответа, что не так? `

                        У тебя видимо объекты пустые, у меня там массивы данных
                        filename="е4343.png" index="0">~~
                        Смысл меню что в итоге получить массив с выбранными данными, а его уже обрабатывать можно как тебе нужно.

                        У меня есть вторая часть скрипта заказа, которая парсит сайт, наполняет эти объекты актуальными данными и отправляет сформированный заказ. `

                        Я так понял есть компания которая по заказу клиента доставляет воду в бутылях. Если вторая часть скрипта заказа не секретная выложи пожалуйста.

                        1 Reply Last reply Reply Quote 0
                        • I
                          instalator last edited by

                          @Haus:

                          Я так понял есть компания которая по заказу клиента доставляет воду в бутылях. Если вторая часть скрипта заказа не секретная выложи пожалуйста. `
                          266_new__0.rar

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post

                          Support us

                          ioBroker
                          Community Adapters
                          Donate

                          780
                          Online

                          31.7k
                          Users

                          79.6k
                          Topics

                          1.3m
                          Posts

                          4
                          11
                          2978
                          Loading More Posts
                          • Oldest to Newest
                          • Newest to Oldest
                          • Most Votes
                          Reply
                          • Reply as topic
                          Log in to reply
                          Community
                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                          The ioBroker Community 2014-2023
                          logo