Голосовой помощник для пчеловода

Долгое время я терпеть не мог всяческие голосовые помощники для телефонов, по самым разным причинам, но в один прекрасный день все изменилось

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

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

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

Навык для Маруси я назвал просто "Пасечник". Алгоритм работы с пасечником выглядит следующим образом:

  1. Пользователь запускает навык командой "включи навык Пасечник". Маруся отвечает "Пасечник на связи"
  2. Пользователь называет номерь улья "улей 21". Маруся отвечает "Слушаю"
  3. Пользователь произносит команду "Запиши" и называет данные которые необходимо записать "Запиши забрал 1 рамку меда". Если все проходит успешно, Маруся отвечает "Готово". Если возникают какие-то ошибки Маруся сообщает, что пасечник занят.
  4. Пользователь может запросить результат за текущий день командой "Результат" либо все свои записи командой "экспорт", при этом пользователю приходит ссылка на результаты экспорта

Код навыка на php выглядит примерно так:

define('DB_PATH', './marusya.db');

function export_data($request_message=null,$period='curday'){
  $res="";
  $session_state="";

  if($request_message['session']['user_id']){
      $user_id =$request_message['session']['user_id'];
  }

  if($request_message['state']['session']['hive'] && $session_state=="")
  {
    $session_state=$request_message['state']['session']['hive'];
  }


  if(file_exists(DB_PATH) && $user_id && $user_id!=""){
    $db=new SQLite3(DB_PATH);

    $curdate = new \DateTime();
    $curtime=strtotime($curdate->format("d.m.Y"));

    if($period=='curday'){
      $sql="select * from hives where usr_id='$user_id' and edit_date='$curtime' ";
  
      //Если задан номер улья в сессии, то делаем выборку только по нему
      if($session_state!=""){
        $sql.=" and hive_num='$session_state'";
      }

      $q=$db->query($sql);
      while($row = $q->fetchArray(SQLITE3_ASSOC) ) {
        $res.=$row['hive_num'].' '.$row['data']."

"; } } else{ //Для экспорта формируем отдельную ссылку $res="Ссылка на загрузку данных localhost/export.php?id=".$user_id; } return $res; } return "Ничего не найдено"; } //Сохраняем данные в базе function save_request_data($request_message){ if(file_exists(DB_PATH)){ $nlu="";$data="";$user_id=""; $db=new SQLite3(DB_PATH); if($request_message['state']['session']['hive']){ //Форматируем число из слов в цифры (один-1) //Для коректной работы должен быть установлен php-intl $fmt = numfmt_create('ru_RU', NumberFormatter::SPELLOUT); $hive_num = numfmt_parse($fmt,$request_message['state']['session']['hive']); } if($request_message['session']['user_id']){ $user_id =$request_message['session']['user_id']; } if($request_message['request']['original_utterance']){ $data = trim(str_replace(["запиши","сохрани"],'',$request_message['request']['original_utterance'])); } if($hive_num!="" && $data!="" && $user_id!=""){ $curdate = new \DateTime(); $curtime=strtotime($curdate->format("d.m.Y")); //Для начала ищем в базе запись для заданного улья. Если дата совпадает, то обновляем текущую запись, добавляя новые данные //Если запись была сделана раньше, то создаем новую запись с текущей датой $sql= "select data from hives where usr_id='$user_id' and hive_num='$hive_num' and edit_date = '$curtime' "; $res = $db->query($sql); $r=$res->fetchArray(SQLITE3_ASSOC); if(is_array($r)){ $data= $r['data'].', '.$data; $d=$db->query("update hives set data='$data' where usr_id='$user_id' and hive_num='$hive_num' and edit_date = '$curtime'"); } else{ $d=$db->query("insert into hives values('$user_id','$hive_num','$data','$curtime')"); } if($d){ return true; } } } return false; } //Формируем ответ для Маруси function create_response($request_message,$text=null,$end_session=false,$session_state="") { if($text && $text!=""){ $response_text=$text; } else if($request_message['request']['original_utterance']) { $response_text=$request_message['request']['original_utterance']; } if($request_message['state']['session']['hive'] && $session_state=="") { $session_state=$request_message['state']['session']['hive']; } return [ 'response' => [ 'text' => $response_text, 'tts' => $response_text, 'end_session' => $end_session, ], 'session' => [ 'user_id' => $request_message['session']['user_id'], 'session_id' => $request_message['session']['session_id'], 'message_id' => $request_message['session']['message_id'], ], 'session_state'=> [ 'hive' => $session_state ], 'version' => $request_message['version'] ]; } $request_message = json_decode(file_get_contents('php://input'), true); $response_message = create_response($request_message,"Пасечник занят",false); //Обрабатываем входящий запрос if($request_message['request']['nlu']['tokens'] && $request_message['request']['command'] && array_key_exists('test',$request_message['request'])==false) { switch(strtolower($request_message['request']['nlu']['tokens'][0])){ case "включи": case "запусти": $response_message=create_response($request_message,"Пасечник на связи"); break; case "спасибо": case "конец": $response_message=create_response($request_message,"Отключаюсь",true); break; case "результат": $res=export_data($request_message); $response_message=create_response($request_message,"Результат за сегодня:
".$res); break; case "экспорт": $res=export_data($request_message,'all'); $response_message=create_response($request_message,$res); break; case "улей": $response_message=create_response($request_message,"Слушаю",false, trim(str_replace('улей','',$request_message['request']['command']))); break; case "запиши": case "сохрани": $s=save_request_data($request_message); if ($s==true){ $response_message = create_response($request_message,"Готово"); } break; } } header('Content-Type: application/json'); echo json_encode($response_message)

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