Skip to content

Commit

Permalink
nicer web interface
Browse files Browse the repository at this point in the history
  • Loading branch information
hbarnard committed Jan 9, 2023
1 parent 0641231 commit dd60004
Show file tree
Hide file tree
Showing 25 changed files with 709 additions and 85 deletions.
Binary file modified __pycache__/intent_server.cpython-39.pyc
Binary file not shown.
13 changes: 13 additions & 0 deletions bits-and-pieces/benchmark-whisper.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,16 @@ whisper_print_timings: encode time = 195740.19 ms / 32623.37 ms per layer
whisper_print_timings: decode time = 57331.04 ms / 9555.17 ms per layer
whisper_print_timings: total time = 259338.50 ms
pi@mema3:~/whisper.cpp $ ./main -h

Thinkpad

whisper_print_timings: load time = 422.48 ms
whisper_print_timings: mel time = 2901.66 ms
whisper_print_timings: sample time = 155.64 ms
whisper_print_timings: encode time = 26528.44 ms / 4421.41 ms per layer
whisper_print_timings: decode time = 11414.45 ms / 1902.41 ms per layer
whisper_print_timings: total time = 41441.81 ms




4 changes: 2 additions & 2 deletions etc/mema.ini
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ rhasspy_main = http://localhost:12101
mimic3 = http://localhost:59125
node_red = http://localhost:1880

# this only affects the template directory at the moment
# Fixme
#FIXME: this only affects the template directory at the moment, start of internationalisation
language = en

#FIXME: some small prompts within the intent server, should be [prompts][en] to give full multilingual addressability
Expand All @@ -88,6 +87,7 @@ nope = I'm_sorry_Dave_I'm_afraid_I_can't_do_that
ok_then = OK_then
done = Done_and_dusted
what_kind = What_kind_of_pie_would_you_like?
ok_going = OK_here_we_go
#FIXME: should be [literals][en] as above
Expand Down
39 changes: 9 additions & 30 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,29 @@ echo '*------------------------------------------------------------------------
echo 'making media directories'
echo '*-------------------------------------------------------------------------------*'

mkdir -p static/media/mos
mkdir -p static/media/tmp
mkdir -p static/media/rec
mkdir -p static/media/vid
mkdir -p static/media/pic
mkdir static/media/mos
mkdir static/media/tmp
mkdir static/media/rec
mkdir static/media/vid
mkdir static/media/pic

echo '*-------------------------------------------------------------------------------*'
echo 'installing packages'
echo '*-------------------------------------------------------------------------------*'
# may not need some of these
apt install python3-pip
apt install python3-numpy
apt install python3-pycurl
apt install python3-matplotlib python3-tk
apt install mosquitto mosquitto-dev
apt install sqlite3
apt install cmake
# makes life simpler for debugging, not in core
apt install ack
# this is to enable the xdg-open replacement jaro
apt-get install guile-2.2
# audiovisual section
apt install ffmpeg
apt install libcamera-apps
apt install pulseaudio
apt install vlc
apt install ntp
# docker
# no get_docker instead sudo apt install docker.io
# may or may not need ffmpeg for sample conversion
apt install ffmpeg portaudio portaudio19-dev fswebcam curl python3-pyaudio guile2.0
sudo apt install docker.io
echo '*-------------------------------------------------------------------------------*'
echo 'installing mema3'
echo 'install python3 packages, make take a while'
echo '*-------------------------------------------------------------------------------*'
sudo -H pip install -r requirements.txt
#FIXME: voice bonnet stuff: not happy about this, does tons of 'other stuff' as well!
# read the docs
#wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py
#sudo python3 raspi-blinka.py
# cant find board
#sudo pip3 install rpi_ws281x adafruit-circuitpython-neopixel
#sudo python3 -m pip install --force-reinstall adafruit-blinka



echo '*-------------------------------------------------------------------------------*'
echo 'installing intent server service'
cp etc/systemd/intent_server.service /etc/systemd/system/
Expand All @@ -67,7 +46,7 @@ echo '*------------------------------------------------------------------------
echo 'trying to start containers'
usermod -aG docker pi
docker run -d --network host --name mema_rhasspy --restart unless-stopped -v "$HOME/.config/rhasspy/profiles:/profiles" -v "/etc/localtime:/etc/localtime:ro" --device /dev/snd:/dev/snd rhasspy/rhasspy --user-profiles /profiles --profile en
docker run --network host -d --restart unless-stopped --name mema_mimic3 -v "${HOME}/.local/share/mycroft/mimic3:/home/mimic3/.local/share/mycroft/mimic3" 'mycroftai/mimic3'
sudo docker run --network host -d --restart unless-stopped --name mema_mimic3 -v "${HOME}/.local/share/mycroft/mimic3:/home/mimic3/.local/share/mycroft/mimic3" 'mycroftai/mimic3'
docker run -d --network host -v node_red_data:/data --restart unless-stopped --name mema_nodered nodered/node-red
echo '*-------------------------------------------------------------------------------*'
echo 'starting intent server'
Expand Down
51 changes: 50 additions & 1 deletion intent_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import sys
import os, pwd

import webbrowser as wb

# for system health, but pycurl should be used elsewhere
# get rid of one of these, pycurl_request is probably problematic

Expand Down Expand Up @@ -52,6 +54,7 @@

#FIXME: Problems with xdg-open
os.environ["DISPLAY"] = ":0.0"
#xset -display :0 -dpms

pi = False
# no test on system name in os now, unreliable, changed from arm to aaarch
Expand Down Expand Up @@ -262,12 +265,58 @@ def run_pie(number,please):
#FIXME: Oh boy, what a problem for something v. simple!
# make general!

'''[
def run_front_page(number,please):
Call_URL = "http://localhost:8000/memories.html"
mycmd = r'handlr open {}'.format(Call_URL)
subprocess.Popen(mycmd,shell = True)

'''

def run_front_page(number,please):


mu.curl_speak(config['en_prompts']['ok_going'])
#wb.open('http://localhost:8000/memories.html', new=2)
wb.get('/usr/bin/chromium').open('http://localhost:8000/memories.html', new=0)
'''
try:
Call_URL = "http://localhost:8000/memories.html"
mycmd = r'jaro {}'.format(Call_URL)
logging.debug('in jaro command ' + mycmd)
mycmd = 'chromium-browser --display=:0 --kiosk --incognito --window-position=0,0 https://reelyactive.github.io/diy/pi-kiosk/'
#subprocess.run([mycmd, number[0] ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout
subprocess.run(mycmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
#subprocess.Popen(mycmd,shell = True)
except subprocess.CalledProcessError as e:
logging.debug('in jaro command error' + mycmd)
raise RuntimeError("command '{}' return here with error (code {}): {}".format(e.cmd, e.returncode, e.output))
'''

#FIXME: Probably merge this with memories fetch? Don't let these one page calls multiply?
@app.get("/privacy.html")
def fetch_privacy(request: Request):
mema_health = system_health()
if mema_health['wifi'] == 'dotgreen':
mu.curl_speak('the_wifi_network_is_connected')
else:
mu.curl_speak('the_wifi_network_is_off')

if config['main']['use_external_ai'] == 'yes':
mu.curl_speak('external_services_for_labelling_and_transcription_are_on')
else:
mu.curl_speak('external_services_are_off')

#FIXME: wordcloud() not here, in a cron
return TEMPLATES.TemplateResponse(
"privacy.html",
{"request": {}, "results" :{}, "mema_health": system_health()}
)






def run_search_memories(number,please):
print('in search memories')
Expand Down
7 changes: 7 additions & 0 deletions kiosk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
xset -dpms # disable DPMS (Energy Star) features.
xset s off # disable screen saver
xset s noblank # don't blank the video device
#matchbox-window-manager -use_titlebar no &
#unclutter & # hide X mouse cursor unless mouse activated
#/usr/bin/chromium --display=:0 --kiosk --incognito --window-position=0,0 http://localhost:8000/memories.html
Binary file modified memalib/__pycache__/mema_utility.cpython-39.pyc
Binary file not shown.
28 changes: 24 additions & 4 deletions memalib/mema_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import re
import datetime
from configobj import ConfigObj
from fastapi import WebSocket

config = ConfigObj('etc/mema.ini')
logging.basicConfig(filename=config['main']['logfile_name'], format='%(asctime)s %(message)s', encoding='utf-8', level=logging.DEBUG)
Expand Down Expand Up @@ -73,7 +74,7 @@ def docker_control(command,instance_name):
return int(datetime.datetime.now().timestamp())


#FIXME: not used current, but useful
#FIXME: not used currently, but useful

def system_health():

Expand Down Expand Up @@ -102,14 +103,33 @@ def system_health():
mema_health[name] = 'dotred'
c.close()

# test whether wifi is up
text_string = Path('/proc/net/wireless').read_text()
m = re.match(r"wlp2s0", text_string)
# test whether wifi is up, make a long line rather than use complex regex!
t = Path('/proc/net/wireless').read_text()
t = ''.join(t.splitlines())
m = re.search(r'wlp', t)
print('m is ', m, t)
if m:
mema_health['wifi'] = 'dotgreen'
else:
mema_health['wifi'] = 'dotred'
return mema_health


def timer_function():
print("timed out \n")


# websocket manager, inspiration is https://gealber.com/simple-chat-app-websockets-fastapi

class ConnectionManager:
def __init__(self):
self.connections: List[WebSocket] = []

async def connect(self, websocket: WebSocket):
await websocket.accept()
self.connections.append(websocket)

async def broadcast(self, data: str):
for connection in self.connections:
await connection.send_text(data)

115 changes: 73 additions & 42 deletions record_story.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,104 @@
import datetime
from pathlib import Path
import os
import re
import memalib.mema_utility as mu
#from plumbum import local, FG, BG

import replicate
from configobj import ConfigObj
import logging

import configparser

# convenience for separating Pi and a random laptop
pi = False
if os.uname()[4].startswith("arm"):
pi = True

if pi:
import board
from picamera import PiCamera
# coloured LEDS on front of voice bonnet, for primitive feedback
from digitalio import DigitalInOut, Direction, Pull
import adafruit_dotstar
DOTSTAR_DATA = board.D5
DOTSTAR_CLOCK = board.D6
dots = adafruit_dotstar.DotStar(DOTSTAR_CLOCK, DOTSTAR_DATA, 3, brightness=0.2)


# spoken prompts without going back into node red

def curl_speak(phrase):
cl = '''curl -s --header "Content-Type: text/utf-8" --request POST --data '{speech}' http://localhost:12101/api/text-to-speech > /dev/null'''.format(speech = phrase)
cl_array = cl.split()
subprocess.call(cl_array,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
return


# main script
def main():
dots = {}
config = configparser.ConfigParser()
config.read('etc/mema.ini')

config = ConfigObj('etc/mema.ini')
logging.basicConfig(filename=config['main']['logfile_name'], format='%(asctime)s %(message)s', encoding='utf-8', level=logging.DEBUG)

pi = False
#FIXME: mema.ini produces strings! also no test on system name now, unreliable
if config['main']['pi'] == 'yes' :
pi = True
import board
# coloured LEDS on front of voice bonnet, for primitive feedback
from digitalio import DigitalInOut, Direction, Pull
import adafruit_dotstar
DOTSTAR_DATA = board.D5
DOTSTAR_CLOCK = board.D6
dots = adafruit_dotstar.DotStar(DOTSTAR_CLOCK, DOTSTAR_DATA, 3, brightness=0.2)
dots.deinit()
phrase = config['en_prompts']['start_record'].replace(' ','_')
curl_speak(phrase)
sleep(1)

mu.curl_speak(config['en_prompts']['start_record'])
unix_time = mu.docker_control('stop', 'mema_rhasspy')
try:
subprocess.run(["docker", "stop", "mema_rhasspy"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))

# make a file name from the current unix timestamp
unix_time = int(datetime.datetime.now().timestamp())
file_name = str(unix_time) + ".wav"

file_path = config['main']['media_directory'] + "rec/" + file_name
media_path = config['main']['media_directory_url'] + "rec/" + file_name

dots[0] = (255,0,0) if pi else None

sleep(1)
if pi:
dots[0] = (255,0,0) # green

try:
#FIXME: this all goes wrong because of the comma -D Hw3,0 for example: see: https://github.com/shivammathur/setup-php/issues/392
record_command = config['main']['record_command'] + ' ' + config['main']['audio_maximum'] + ' ' + file_path
#record_array = record_command.split()
logging.debug('record command is: ' + record_command)
subprocess.call(record_command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
record_command = config['main']['record_command'] + ' ' + file_path
record_array = record_command.split()
subprocess.call(record_array, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
raise RuntimeError("command '{}' return here with error (code {}): {}".format(e.cmd, e.returncode, e.output))

dots[0] = (0,0,255) if pi else None # red
if pi:
dots[0] = (0,0,255) # red

try:
subprocess.run(["docker", "start", "mema_rhasspy"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))

sleep(5) # give mema_rhasspy time to reload!

phrase = config['en_prompts']['end_record'].replace(' ','_')
curl_speak(phrase)


unix_time = mu.docker_control('start', 'mema_rhasspy')
sleep(int(config['main']['rhasspy_reload'])) # give mema_rhasspy time to reload!

mu.curl_speak(config['en_prompts']['end_record'])

# give a little feedback
dots[0] = (0,255,0) if pi else None

text = config['en_literals']['unlabelled_audio']
if pi:
dots[0] = (0,255,0) # blue

# probably in configuration later
text = 'no label'

#FIXME speech to text on remote server, replace with whisper.cpp cron
if config['main']['use_external_ai'] == 'yes' :
logging.debug('in external AI transcribe')
# speech to text on remote server
if config['main']['use_external_ai']:
# select speech to text model
model = replicate.models.get("openai/whisper")
# format file as path object (openai needs this)
audio_file = Path(file_path)
result = model.predict(audio=audio_file)
text = result['transcription']
mu.curl_speak(config['en_prompts']['end_transcription'])

mu.curl_speak(config['en_prompts']['done'])
phrase = config['en_prompts']['end_transcription'].replace(' ','_')
else:
phrase = config['en_prompts']['done']
curl_speak(phrase)

# done, feedback, stop blinking lights
if pi:
Expand Down
2 changes: 1 addition & 1 deletion record_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# main script
def main():

dots = {}
config = ConfigObj('etc/mema.ini')
logging.basicConfig(filename=config['main']['logfile_name'], format='%(asctime)s %(message)s', encoding='utf-8', level=logging.DEBUG)

Expand Down
Loading

0 comments on commit dd60004

Please sign in to comment.