/ tea2adt_source / tea2adt
tea2adt
  1  #!/bin/bash
  2  
  3  # read tmp path
  4  TMP_PATH=$(head -n 1 cfg/tmp_path)
  5  
  6  # read version
  7  VERSION=$(head -n 1 version)
  8  
  9  # install dependencies?
 10  INSTALL_DEPENDENCIES=$(head -n 1 cfg/install_dependencies)
 11  if [ "${INSTALL_DEPENDENCIES}" == true ] ; then
 12      read -p "Do you want to install dependencies(y/Y)? " -n 1 -r
 13      echo    # (optional) move to a new line
 14      if [[ $REPLY =~ ^[Yy]$ ]]
 15      then
 16          echo "-------------------------------------------"
 17          ${TERMINAL} ./install_dependencies.sh
 18          echo "-------------------------------------------"
 19      else
 20          read -p "Please install dependencies by hand as described in the documentation, cfg/install_dependencies will be set to false"
 21      fi
 22      echo "false" > cfg/install_dependencies
 23  fi 
 24          
 25  # first remove previous temporary working folder
 26  if [ -d "${HOME}${TMP_PATH}" ]; then
 27      rm -r "${HOME}${TMP_PATH}"
 28  fi
 29  
 30  # create new temporary working folder
 31  mkdir -p "${HOME}${TMP_PATH}"
 32  
 33  # copy files to tmp path
 34  cp -r "./cfg" "${HOME}${TMP_PATH}"
 35  cp -r "./state" "${HOME}${TMP_PATH}"
 36  cp -r "./tmp" "${HOME}${TMP_PATH}"
 37  
 38  # read configuration from files copied in tmp path
 39  BAUD=$(head -n 1 ${HOME}${TMP_PATH}/cfg/baud)
 40  SYNCBYTE=$(head -n 1 ${HOME}${TMP_PATH}/cfg/syncbyte)
 41  CONFIDENCE=$(head -n 1 ${HOME}${TMP_PATH}/cfg/confidence)
 42  LIMIT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/limit)
 43  TERMINAL=$(head -n 1 ${HOME}${TMP_PATH}/cfg/terminal)
 44  PROBE_MSG=$(head -n 1 ${HOME}${TMP_PATH}/cfg/probe_msg)
 45  PROBE_SLEEP=$(head -n 1 ${HOME}${TMP_PATH}/cfg/probe_sleep)
 46  HALF_DUPLEX=$(head -n 1 ${HOME}${TMP_PATH}/cfg/half_duplex)
 47  VOLUME_SPEAKER_LEFT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/volume_speaker_left)
 48  VOLUME_SPEAKER_RIGHT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/volume_speaker_right)
 49  VOLUME_MICROPHONE=$(head -n 1 ${HOME}${TMP_PATH}/cfg/volume_microphone)
 50  TEXT_TO_SPEECH=$(head -n 1 ${HOME}${TMP_PATH}/cfg/text_to_speech)
 51  SPEECH_TO_TEXT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/speech_to_text)
 52  INTERFACE_INDEX_TTS_OUT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/interface_index_tts_out)
 53  INTERFACE_INDEX_STT_IN=$(head -n 1 ${HOME}${TMP_PATH}/cfg/interface_index_stt_in)
 54  INTERFACE_INDEX_MINIMODEM_OUT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/interface_index_minimodem_out)
 55  INTERFACE_INDEX_MINIMODEM_IN=$(head -n 1 ${HOME}${TMP_PATH}/cfg/interface_index_minimodem_in)
 56  VOLUME_STT_IN=$(head -n 1 ${HOME}${TMP_PATH}/cfg/volume_stt_in)
 57  VOLUME_TTS_OUT=$(head -n 1 ${HOME}${TMP_PATH}/cfg/volume_tts_out)
 58  LLM_CMD=$(head -n 1 ${HOME}${TMP_PATH}/cfg/llm_cmd)
 59  
 60  # SET_COMM_IFS
 61  # WORKAROUND: for now, clear audio interfaces for communication in case they were set altough not yet supported
 62  if [ "${INTERFACE_INDEX_MINIMODEM_IN}" != "" ] ; then
 63      printf "INTERFACE_INDEX_MINIMODEM_IN is not supported yet. The default audio interfaces will be used instead."
 64      INTERFACE_INDEX_MINIMODEM_IN=
 65  fi
 66  if [ "${INTERFACE_INDEX_MINIMODEM_OUT}" != "" ] ; then
 67      printf "INTERFACE_INDEX_MINIMODEM_OUT is not supported yet. The default audio interfaces will be used instead."
 68      INTERFACE_INDEX_MINIMODEM_OUT=
 69  fi
 70  
 71  trap ctrl_c INT
 72  
 73  function ctrl_c()
 74  {
 75      # TODO: kill child/started processes before ctrl-c
 76      # kill $(ps -s $$ -o pid=)
 77      # pkill -P $$
 78      # restore mic volume
 79      ${TERMINAL} ./restore_audio_settings.sh &
 80      # TODO: brute-force kill?
 81      ./killtea2adt.sh 2> /dev/null &
 82      # and now ctr-c
 83      trap - INT
 84      kill -INT $$
 85  }
 86  
 87  # SET_COMM_IFS
 88  set_interfaces()
 89  {
 90      # speaker / sink (communication)
 91      if [ "${INTERFACE_INDEX_MINIMODEM_OUT}" != "" ] ; then
 92          # get sink-input
 93          while true; do
 94              SINK_INPUT=$(pacmd list-sink-inputs | awk '/index:/{idx=$2} /application.process.binary = "minimodem"/{print idx; exit}')
 95              if [ -n "$SINK_INPUT" ]; then
 96                  break
 97              fi
 98              sleep 1
 99          done
100          # set configured sink for this specific sink-input
101          pactl move-sink-input ${SINK_INPUT} ${INTERFACE_INDEX_MINIMODEM_OUT}
102          echo "set minimodem output with sink input ${SINK_INPUT} to interface index ${INTERFACE_INDEX_MINIMODEM_OUT}"
103      fi
104      # microphone / source (communication)
105      if [ "${INTERFACE_INDEX_MINIMODEM_IN}" != "" ] ; then
106          # get source-input
107          while true; do
108              SOURCE_OUTPUT=$(pacmd list-source-outputs | awk '/index:/{idx=$2} /application.process.binary = "minimodem"/{print idx; exit}')
109              if [ -n "$SOURCE_OUTPUT" ]; then
110                  break
111              fi
112              sleep 1
113          done
114          # set configured source for this specific source-output
115          pactl move-source-output ${SOURCE_OUTPUT} ${INTERFACE_INDEX_MINIMODEM_IN}
116          echo "set minimodem input with source output ${SOURCE_OUTPUT} to interface index ${INTERFACE_INDEX_MINIMODEM_IN}"
117      fi
118  }
119  
120  set_volumes()
121  {
122      # speaker
123      if [ "${INTERFACE_INDEX_MINIMODEM_OUT}" != "" ] ; then
124          if [ "${VOLUME_SPEAKER_LEFT}" != "" ] ; then
125              pactl -- set-sink-volume ${INTERFACE_INDEX_MINIMODEM_OUT} ${VOLUME_SPEAKER_LEFT} ${VOLUME_SPEAKER_RIGHT}
126              echo "setting output/speaker volume to L,R = ${VOLUME_SPEAKER_LEFT},${VOLUME_SPEAKER_RIGHT}"
127          fi
128          echo "configured output/speaker, index = " ${INTERFACE_INDEX_MINIMODEM_OUT}
129      else
130          if [ "${VOLUME_SPEAKER_LEFT}" != "" ] ; then
131              default_sink_index=$(pacmd list-sinks | grep "* index: " | grep -o '...$')
132              if [ "${default_sink_index:0:1}" == ":" ] ; then
133                   default_sink_index=$(pacmd list-sinks | grep "* index: " | grep -o '..$')
134              fi
135              pactl -- set-sink-volume ${default_sink_index} ${VOLUME_SPEAKER_LEFT} ${VOLUME_SPEAKER_RIGHT}
136              echo "setting default output/speaker volume to L,R = "${VOLUME_SPEAKER_LEFT}","${VOLUME_SPEAKER_RIGHT}
137              # echo "(leave cfg/volume_speaker_left empty in order to not set input volume automtatically)"
138          fi
139          # echo default settings for communication
140          echo "default output/speaker, index = " ${default_sink_index}
141      fi
142      # mic
143      if [ "${INTERFACE_INDEX_MINIMODEM_IN}" != "" ] ; then
144          if [ "${VOLUME_MICROPHONE}" != "" ] ; then   
145              pactl -- set-source-volume ${INTERFACE_INDEX_MINIMODEM_IN} ${VOLUME_MICROPHONE}
146              echo "setting input/microphone volume to "${VOLUME_MICROPHONE}
147              # echo "(leave cfg/volume_microphone empty in order to not set output volume automtatically)"
148          fi
149          echo "configured input/microphone, index = " ${INTERFACE_INDEX_MINIMODEM_IN}
150      else
151          if [ "${VOLUME_MICROPHONE}" != "" ] ; then
152              default_source_index=$(pacmd list-sources | grep "* index: " | grep -o '...$')
153              if [ "${default_source_index:0:1}" == ":" ] ; then
154                   default_source_index=$(pacmd list-sources | grep "* index: " | grep -o '..$')
155              fi    
156              pactl -- set-source-volume ${default_source_index} ${VOLUME_MICROPHONE}
157              echo "setting input/microphone volume to "${VOLUME_MICROPHONE}
158              # echo "(leave cfg/volume_microphone empty in order to not set output volume automtatically)"
159          fi
160          echo "default input/microphone, index = " ${default_source_index}
161      fi
162      # TTS
163      if [ "${TEXT_TO_SPEECH}" != "" ] ; then
164          if [ "${VOLUME_TTS_OUT}" != "" ] ; then
165              pactl -- set-sink-volume ${INTERFACE_INDEX_TTS_OUT} ${VOLUME_TTS_OUT}
166              echo "setting TTS output/speaker volume = "${VOLUME_TTS_OUT}
167          fi
168      fi
169      # STT
170      if [ "${SPEECH_TO_TEXT}" == true ] ; then
171          if [ "${VOLUME_STT_IN}" != "" ] ; then
172              pactl -- set-source-volume ${INTERFACE_INDEX_STT_IN} ${VOLUME_STT_IN}
173              echo "setting STT input/microphone volume = "${VOLUME_STT_IN}
174          fi
175      fi
176      # echo TTS and STT settings
177      echo "TTS command = "${TEXT_TO_SPEECH}
178      echo "TTS output/speaker, index = "${INTERFACE_INDEX_TTS_OUT}
179      echo "STT input/microphone, index = "${INTERFACE_INDEX_STT_IN}
180  }
181  
182  create_fifos()
183  {
184      if [ "$1" == "-l" ] || [ "$1" == "--llm" ] || [ "$1" == "--llm-chat" ] || [ "$1" == "--llm-prompt" ] ; then
185          PIPE_LLM_OUT="${HOME}${TMP_PATH}/tmp/pipe_llm_out"
186          if [ -p ${PIPE_LLM_OUT} ] || [ -f ${PIPE_LLM_OUT} ]; then
187            rm ${PIPE_LLM_OUT}
188          fi
189          mkfifo ${PIPE_LLM_OUT}
190      elif [ "$1" == "-s" ] || [ "$1" == "--rs" ] || [ "$1" == "--remote-shell" ] || [ "$1" == "--reverse-shell" ] ; then
191          PIPE_SHELL_IN="${HOME}${TMP_PATH}/tmp/pipe_shell_in"
192          PIPE_SHELL_OUT="${HOME}${TMP_PATH}/tmp/pipe_shell_out"
193          if [ -p ${PIPE_SHELL_IN} ] || [ -f ${PIPE_SHELL_IN} ]; then
194            rm ${PIPE_SHELL_IN}
195          fi
196          if [ -p ${PIPE_SHELL_OUT} ] || [ -f ${PIPE_SHELL_OUT} ]; then
197            rm ${PIPE_SHELL_OUT}
198          fi
199          mkfifo ${PIPE_SHELL_IN}
200          mkfifo ${PIPE_SHELL_OUT}
201      elif [ "$1" == "-f" ] || [ "$1" == "--file" ] ; then
202          PIPE_FILE_IN="${HOME}${TMP_PATH}/tmp/pipe_file_in"
203          PIPE_FILE_OUT="${HOME}${TMP_PATH}/tmp/pipe_file_out"
204          if [ -p ${PIPE_FILE_IN} ] || [ -f ${PIPE_FILE_IN} ]; then
205            rm ${PIPE_FILE_IN}
206          fi
207          if [ -p ${PIPE_FILE_OUT} ] || [ -f ${PIPE_FILE_OUT} ]; then
208            rm ${PIPE_FILE_OUT}
209          fi
210          mkfifo ${PIPE_FILE_IN}
211          mkfifo ${PIPE_FILE_OUT}
212      fi
213  }
214  
215  ask_password()
216  {
217      while true
218      do
219          while true
220          do
221              echo -n "password: "
222              read -ers PASSWORD1
223              echo ""
224              # 2> /dev/null shall prevent showing the password if some error occurs
225              if [ ! "${PASSWORD1}" == "" ] 2> /dev/null
226              then
227                  break
228              else
229                  echo "Password cannot be empty!"
230              fi
231          done
232          echo -n "confirm password: "
233          read -ers PASSWORD
234          echo ""
235          # 2> /dev/null shall prevent showing the password if some error occurs
236          [ "${PASSWORD1}" == "${PASSWORD}" ] 2> /dev/null && break
237          echo "Passwords dont match!"
238      done
239  }
240  
241  # SET_COMM_IFS
242  # TODO: if we get feature # SET_COMM_IFS to work, then we need to update this function to show the correct comm. interfaces
243  #       which will then be defined by INTERFACE_INDEX_MINIMODEM_IN and INTERFACE_INDEX_MINIMODEM_OUT
244  echo_audio_interfaces()
245  {
246      # list audio output interfaces
247      echo "audio output interfaces"
248      echo "-----------------------"
249      echo "$(pactl list short sinks)"
250      # list audio input interfaces
251      echo ""
252      echo "audio input interfaces"
253      echo "----------------------"    
254      echo "$(pactl list short sources)"
255      # default audio output / speaker
256      default_sink_index=$(pacmd list-sinks | grep "* index: " | grep -o '...$')
257      if [ "${default_sink_index:0:1}" == ":" ] ; then
258           default_sink_index=$(pacmd list-sinks | grep "* index: " | grep -o '..$')
259      fi
260      # default audio input / mic
261      default_source_index=$(pacmd list-sources | grep "* index: " | grep -o '...$')
262      if [ "${default_source_index:0:1}" == ":" ] ; then
263           default_source_index=$(pacmd list-sources | grep "* index: " | grep -o '..$')
264      fi    
265      # echo settings
266      echo ""
267      echo "audio settings for communication"
268      echo "--------------------------------"
269      echo "default output/speaker, index = " ${default_sink_index}
270      echo "default input/microphone, index = " ${default_source_index}
271      echo ""
272      echo "audio settings for TTS and STT"
273      echo "--------------------------------"
274      echo "TTS output/speaker, index = " ${INTERFACE_INDEX_TTS_OUT}
275      echo "STT input/microphone, index = " ${INTERFACE_INDEX_STT_IN}
276      echo ""
277  }
278  
279  echo_help()
280  {
281      echo "usage: tea2adt [-c|-s]    		 
282      		 -c, --chat
283      		 -l, --llm, --llm-chat, --llm-prompt
284      		 -s, --rs, --remote-shell, --reverse-shell (to execute a command as sudo type: echo \"<pwd>\" | sudo -k -S <cmd>)
285      		 -f, --file, --file-transfer
286      		 -p, --probe (send periodic test messages to check connectivity and adjust the volumes if required)
287      		 -g, --gui (only for configuration)
288      		 -a, --audio-settings
289      		 -d, --directory (show installation path)
290      		 -V, --version
291      		 -h, --help
292      		 -i, --image
293      		 
294  tea2adt is a command-line utility for Chat, Remote Shell, Remote AI Prompt and File Transfer, that reads and writes encrypted data across peer-to-peer or broadcast audio connections, using minimodem and gpg.
295  It is a powerful tool that can be combined with any audio infrastructure (like PSTN, cellular network, internet, radio) to provide a secure communication channel through an audio tunnel.
296  The audio interfaces behave like data-diodes, each allowing unidirectional data transmission only, thus preventing data-leaks and malware-injection.
297  This enables an enhanced-end-to-end encryption (E-E2EE) which notably increases security and privacy, especially when the end devices are completely offline (air-gapped-system), providing an effective barrier against legal or illegal client-side-scanning!
298  Homepage: https://github.com/ClarkFieseln/tea2adt"
299  }
300  
301  echo_image()
302  {
303      echo "
304  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
305  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
306  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
307  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓█░█▓▓▓▓▓▓▓▓▓▓███░▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓█░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
308  ▓▓▓▓▓▓▓▓▓▓▓▓▓█░░█▓▓▓▓▓▓▓▓▓▓███████▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▒█░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓
309  ▓▓▓▓▓▓▓▓▓▓▓▓▒░░░▓█▓▓▓▓▓▓▓▓▓███████░██▓█▓▓▓▓▓▓▓▓▓█░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓█▓▓▓▓▓▓▓▓▓▓
310  ▓▓▓▓▓▓▓▓▓▓▓▓█░░░▓███████████████████░██░░░░░░░░░█░░░░▓▓▓▓▓▓▓█▓▓▓▓█▓▓▓▓█▓▓▓▓▓▓▓▓▓
311  ▓▓▓▓▓▓▓▓▓▓▓▓▒░░░██▓▓▓▓▓▓▓▓▓██████▒█▒▓▓█▓▓▓▓▓▓▓▓▓██░░░▓▓▓▓▓▓▓▓█▓▓█▓▓▓▓▓▓▓▓█▓▓▓▓▓▓
312  ▓▓▓▓▓▓▓▓▓▓▓▓▓█░░█▓▓▓▓▓▓▓▓▓▓█████▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▒█░█▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓██▓▓▓▓▓▓▓
313  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒██▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
314  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
315  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
316  ▓▓▓▓▓▓▓▓▓▓▓▓▓░░▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
317  ▓▓▓▓▓▓▓▓▓▓▓▓▓▒░█▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▒█░██▒▓▓▓▓▓▓▓▓▓▓▓░░█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓
318  ▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░██▓▓▓▓▓▓▓▓▓█▓▓▓█░░█████▓▓▓▓▓▓▓▓▓░█░░░█▓▓▓▓▓▓▓▓██▓▓▓▓█▓█▓▓▓▓▓▓▓▓▓
319  ▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░█▒░░░░░░░░░██░░██████████████████░░░░█▓▓▓▓▓███▓█▓███▓▓█▓▓▓██▓▓▓▓
320  ▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░█▒▓▓▓▓▓▓▓▓▓█▓▓█████████▓▓▓▓▓▓▓▓▓▓▒░░░█▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓█▓█▓▓▓▓▓▓
321  ▓▓▓▓▓▓▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▒██████▓▓▓▓▓▓▓▓▓▓▓░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓
322  ▓▓▓▓▓▓▓▓▓▓▓▓▓░█▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓░█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
323  ▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
324  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
325  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
326  "
327  }
328  
329  echo_parameters()
330  {
331      echo "baud = $BAUD"
332      echo "half_duplex = $HALF_DUPLEX"
333      echo "syncbyte = $SYNCBYTE"
334      echo "confidence = $CONFIDENCE"
335      echo "limit = $LIMIT"
336      echo "terminal = $TERMINAL"
337      echo "volume speaker left = $VOLUME_SPEAKER_LEFT"
338      echo "volume speaker right = $VOLUME_SPEAKER_RIGHT"
339      echo "volume microphone = $VOLUME_MICROPHONE"
340      echo "text to speech = $TEXT_TO_SPEECH"
341      echo "speech to text = $SPEECH_TO_TEXT"
342      echo "volume TTS out = $VOLUME_TTS_OUT"
343      echo "volume STT in = $VOLUME_STT_IN"
344  }
345  
346  echo_version()
347  {
348      echo "tea2adt ${VERSION}
349  MIT License: <https://opensource.org/license/mit>.    
350  Copyright (C) 2025 Clark Fieseln
351  This is free software: you are free to change and redistribute it.
352  There is NO WARRANTY, to the extent permitted by law."
353  }
354  
355  : '
356  # SET_COMM_IFS
357  ---------------------------------------------
358  NOTE: set audio interfaces for communication:
359  ---------------------------------------------
360      in the tested solution minimodem is running in a background tmux session (or a screen session) with a pipe to cat | minimodem
361      (in that case the sink-source remains always the same, i.e. interface_index_minimodem_in/out are set once and are working as expected)
362      but in that case the communication gets very unstable due to synchronization issues in minimodem, and probably also due to buffering in pipe, cat or tmux/screen
363      alternatives using a named pipe instead of cat, or a temporary variable or file did not work as minimodem gets called several times
364      A "dynamic" solution which detects the current sink-source used by minimodem which each call was not implemented
365      because it may bring timing-problems as is the case when setting the TTS sink based on the sink-input
366      A generic solution using virtual sinks and load-module in pulse audio may also not work when the sink-input changes with each call to minimodem.
367  init_minimodem()
368  {
369      # init minimodem process in new tmux session which can then be written with tmux send-keys -t session_mmtx
370      tmux new-session -d -s session_mmtx "cat | minimodem --tx --ascii --quiet --startbits 1 --stopbits 1.0 --sync-byte ${SYNCBYTE} --volume 1.0 ${BAUD}" &
371      # alternative:
372      # screen -dmS session_mmtx bash -c "cat | minimodem --tx --ascii --quiet --startbits 1 --stopbits 1.0 --sync-byte 0x7F --volume 1.0 9600" &
373  }
374  '
375  
376  # options
377  if [ "$1" == "" ] ; then
378      echo "tea2adt: *** you must specify an option, run tea2adt -h for more information ***"
379      echo_help
380  elif [ "$1" == "-a" ] || [ "$1" == "--audio-settings" ] ; then  
381      echo_audio_interfaces
382  elif [ "$1" == "-c" ] || [ "$1" == "--chat" ] ; then
383      ask_password
384      # init_minimodem
385      set_volumes
386      create_fifos "$1"
387      ${TERMINAL} ./mmtx.sh "${PASSWORD}" &
388      # wait a little bit so the transmitter can initialize the state flags
389      sleep 0.5
390      echo_parameters
391      # don't call set_interfaces here,  have not yet a source-output, need yet to run rx.src
392      source rx.src | python3 mmrx.py "${PASSWORD}" $1
393  elif [ "$1" == "-f" ] || [ "$1" == "--file" ] ; then
394      ask_password
395      # init_minimodem
396      set_volumes
397      create_fifos "$1"
398      ${TERMINAL} ./mmtxfile.sh "${PASSWORD}" &
399      # wait a little bit so the transmitter can initialize the state flags
400      sleep 0.5
401      # track input files
402      ${TERMINAL} cat ${PIPE_FILE_IN} &
403      # don't call set_interfaces here,  have not yet a source-output, need yet to run rx.src
404      # we don't echo_parameters in -f mode
405      source rx.src | python3 mmrx.py "${PASSWORD}" $1
406  elif [ "$1" == "-l" ] || [ "$1" == "--llm" ] || [ "$1" == "--llm-chat" ] || [ "$1" == "--llm-prompt" ] ; then
407      ask_password
408      # init_minimodem
409      set_volumes
410      create_fifos "$1"
411      if [ "${LLM_CMD}" != "" ] ; then
412          # check if ollama is used and if it is running
413          if [[ ${LLM_CMD} =~ "ollama run" ]]; then
414              while ! curl -s http://localhost:11434/api/tags > /dev/null; do
415                  # sleep 1
416                  echo "Error: ollama is not running!"
417                  exit
418              done
419          fi
420          # create persistent session to run in background with output pipe
421          # (input is done with command 'tmux send-keys -t session_llm')
422          tmux new-session -d -s session_llm "${LLM_CMD} > ${PIPE_LLM_OUT}" &
423          # TODO: use instead 2> to not redirect errors?
424          # tmux new-session -d -s session_llm "${LLM_CMD} 2> ${PIPE_LLM_OUT}" &
425          echo "LLM model started..."
426      else
427          echo "Error: llm_cmd is not configured!"
428          exit
429      fi
430      echo_parameters
431      # don't call set_interfaces here,  have not yet a source-output, need yet to run rx.src
432      source rx.src | python3 mmrx.py "${PASSWORD}" $1
433  elif [ "$1" == "-s" ] || [ "$1" == "--rs" ] || [ "$1" == "--remote-shell" ] || [ "$1" == "--reverse-shell" ] ; then
434      ask_password
435      # init_minimodem
436      set_volumes
437      create_fifos "$1"
438      # create persistent shell to run in background with input and output pipes
439      while true; do /bin/sh &> ${PIPE_SHELL_OUT}; done 0< ${PIPE_SHELL_IN} &
440      echo_parameters
441      # don't call set_interfaces here,  have not yet a source-output, need yet to run rx.src
442      source rx.src | python3 mmrx.py "${PASSWORD}" $1
443  elif [ "$1" == "-p" ] || [ "$1" == "--probe" ] ; then
444      # init_minimodem
445      set_volumes
446      echo_parameters
447      ${TERMINAL} ./mmrxnopwd.sh &
448      echo "**********************************"
449      echo "*** tea2adt probe transmitter ****"
450      echo "**********************************"
451      echo "Test messages "${PROBE_MSG}" sent with a delay of "${PROBE_SLEEP}" seconds..."
452      echo "(a separate terminal is opened to receive unencrypted test messages from the communication partner)"
453      # SET_COMM_IFS
454      # set_interfaces
455      while :
456      do
457          echo "${PROBE_MSG}" | source tx.src
458          sleep ${PROBE_SLEEP}
459      done
460  elif [ "$1" == "-g" ] || [ "$1" == "--gui" ] ; then
461      python3 gui.py
462  elif [ "$1" == "-d" ] || [ "$1" == "--directory" ] ; then
463      echo $(pip show tea2adt | grep Location)"/tea2adt_source"
464  elif [ "$1" == "-V" ] || [ "$1" == "--version" ] ; then
465      echo_version
466  elif [ "$1" == "-h" ] || [ "$1" == "--help" ] ; then
467      echo_help
468  elif [ "$1" == "-i" ] || [ "$1" == "--image" ] ; then
469      echo_image  
470  else
471      echo "tea2adt: invalid option"
472  fi