Deepcopy story data and config changes
This commit is contained in:
parent
484542de8d
commit
43e3e52f2a
6 changed files with 100 additions and 102 deletions
|
@ -93,7 +93,7 @@ chown=pi:pi
|
||||||
## Deploy / usefull commands
|
## Deploy / usefull commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
for i in {1..6}; do rsync -av ~/hugvey/ pi@hugvey$i.local:/home/pi/hugvey/ --exclude=www --exclude=venv --exclude=local --exclude=*.pyc --exclude=.git; done
|
for i in {1..26}; do echo $i; rsync -av ~/hugvey/ pi@hugvey$i.local:/home/pi/hugvey/ --exclude=www --exclude=venv --exclude=local --exclude=*.pyc --exclude=.git --exclude=recordings --exclude=/voice* --exclude=/pd; done
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -204,4 +204,4 @@ times occured/only on n-th instance: determines the order of diversions of the s
|
||||||
## 4G Modem
|
## 4G Modem
|
||||||
|
|
||||||
Visit 192.168.5.1
|
Visit 192.168.5.1
|
||||||
The password is at the bottom of the device.
|
The password is at the bottom of the device.
|
||||||
|
|
|
@ -5,7 +5,7 @@ voice:
|
||||||
input_rate: 44100
|
input_rate: 44100
|
||||||
target_rate: 16000
|
target_rate: 16000
|
||||||
port: 4444
|
port: 4444
|
||||||
input_name: 'AK5371'
|
input_name: 'USB Audio Device'
|
||||||
output_name: 'USB Audio Device'
|
output_name: 'USB Audio Device'
|
||||||
input_mixer: 'Mic'
|
input_mixer: 'Mic'
|
||||||
output_mixer: 'PCM'
|
output_mixer: 'PCM'
|
||||||
|
@ -13,6 +13,3 @@ voice:
|
||||||
output_volume: 30
|
output_volume: 30
|
||||||
file_address: "http://hugveycmd.local:8888"
|
file_address: "http://hugveycmd.local:8888"
|
||||||
output_driver: pulseaudio
|
output_driver: pulseaudio
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ from hugvey.voice import VoiceStorage
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
from hugvey.speech.recorder import Recorder
|
from hugvey.speech.recorder import Recorder
|
||||||
from pythonosc import udp_client
|
from pythonosc import udp_client
|
||||||
|
import copy
|
||||||
|
|
||||||
mainLogger = logging.getLogger("hugvey")
|
mainLogger = logging.getLogger("hugvey")
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ class CentralCommand(object):
|
||||||
self.languageFiles = {}
|
self.languageFiles = {}
|
||||||
self.languageConfig = {}
|
self.languageConfig = {}
|
||||||
self.args = args # cli args
|
self.args = args # cli args
|
||||||
|
|
||||||
eventLogger.addHandler(logging.handlers.QueueHandler(self.logQueue))
|
eventLogger.addHandler(logging.handlers.QueueHandler(self.logQueue))
|
||||||
|
|
||||||
def loadConfig(self, filename):
|
def loadConfig(self, filename):
|
||||||
|
@ -85,14 +86,14 @@ class CentralCommand(object):
|
||||||
self.hugvey_ids = [i + 1 for i in range(self.config['hugveys'])]
|
self.hugvey_ids = [i + 1 for i in range(self.config['hugveys'])]
|
||||||
|
|
||||||
self.loadLanguages()
|
self.loadLanguages()
|
||||||
|
|
||||||
|
|
||||||
voice_dir = os.path.join(self.config['web']['files_dir'], 'voices')
|
voice_dir = os.path.join(self.config['web']['files_dir'], 'voices')
|
||||||
self.voiceStorage = VoiceStorage(voice_dir, self.languageConfig)
|
self.voiceStorage = VoiceStorage(voice_dir, self.languageConfig)
|
||||||
|
|
||||||
self.panopticon = Panopticon(self, self.config, self.voiceStorage)
|
self.panopticon = Panopticon(self, self.config, self.voiceStorage)
|
||||||
|
|
||||||
|
|
||||||
def loadLanguages(self):
|
def loadLanguages(self):
|
||||||
logger.debug('load language files')
|
logger.debug('load language files')
|
||||||
self.languages = {}
|
self.languages = {}
|
||||||
|
@ -116,7 +117,7 @@ class CentralCommand(object):
|
||||||
# if not hv.story:
|
# if not hv.story:
|
||||||
# status['status'] = 'off'
|
# status['status'] = 'off'
|
||||||
# return status
|
# return status
|
||||||
|
|
||||||
status['status'] = hv.getStatus()
|
status['status'] = hv.getStatus()
|
||||||
status['language'] = hv.language_code
|
status['language'] = hv.language_code
|
||||||
status['light_id'] = hv.lightId
|
status['light_id'] = hv.lightId
|
||||||
|
@ -126,7 +127,7 @@ class CentralCommand(object):
|
||||||
# status['history'] = hv.story.getLogSummary() # disabled as it is a bit slow. We now have eventLog
|
# status['history'] = hv.story.getLogSummary() # disabled as it is a bit slow. We now have eventLog
|
||||||
# status['counts'] = {t: len(a) for t, a in status['history'].items() if t != 'directions' }
|
# status['counts'] = {t: len(a) for t, a in status['history'].items() if t != 'directions' }
|
||||||
status['counts'] = {} if not hv.story else hv.story.getLogCounts()
|
status['counts'] = {} if not hv.story else hv.story.getLogCounts()
|
||||||
status['duration'] = 0 if not hv.story else hv.story.timer.getElapsed()
|
status['duration'] = 0 if not hv.story else hv.story.timer.getElapsed()
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
@ -139,10 +140,10 @@ class CentralCommand(object):
|
||||||
'logbookId': None,
|
'logbookId': None,
|
||||||
'logbook': [],
|
'logbook': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
#use this to test if any threads stay open
|
#use this to test if any threads stay open
|
||||||
# eg. after killing/dying of a hugvey
|
# eg. after killing/dying of a hugvey
|
||||||
# print(threading.enumerate())
|
# print(threading.enumerate())
|
||||||
|
|
||||||
for hv_id in self.hugvey_ids:
|
for hv_id in self.hugvey_ids:
|
||||||
status['hugveys'].append(self.getHugveyStatus(hv_id, selected_id == hv_id))
|
status['hugveys'].append(self.getHugveyStatus(hv_id, selected_id == hv_id))
|
||||||
|
@ -151,7 +152,7 @@ class CentralCommand(object):
|
||||||
if self.hugveys[selected_id].recorder:
|
if self.hugveys[selected_id].recorder:
|
||||||
status['logbook'] = self.hugveys[selected_id].recorder.currentLog
|
status['logbook'] = self.hugveys[selected_id].recorder.currentLog
|
||||||
status['logbookId'] = selected_id
|
status['logbookId'] = selected_id
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def commandHugvey(self, hv_id, msg):
|
def commandHugvey(self, hv_id, msg):
|
||||||
|
@ -169,8 +170,8 @@ class CentralCommand(object):
|
||||||
|
|
||||||
def _queueCommand(self, hv_id, msg):
|
def _queueCommand(self, hv_id, msg):
|
||||||
self.commandQueue.put_nowait((hv_id, msg))
|
self.commandQueue.put_nowait((hv_id, msg))
|
||||||
|
|
||||||
|
|
||||||
def commandLight(self, route, data):
|
def commandLight(self, route, data):
|
||||||
"""
|
"""
|
||||||
Buffer light commands
|
Buffer light commands
|
||||||
|
@ -215,13 +216,13 @@ class CentralCommand(object):
|
||||||
|
|
||||||
logger.warn('Stopping command sender')
|
logger.warn('Stopping command sender')
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
|
||||||
async def lightSender(self):
|
async def lightSender(self):
|
||||||
lightConn = udp_client.SimpleUDPClient(
|
lightConn = udp_client.SimpleUDPClient(
|
||||||
self.config['light']['ip'],
|
self.config['light']['ip'],
|
||||||
self.config['light']['port'])
|
self.config['light']['port'])
|
||||||
|
|
||||||
logger.info(f"Ready to send light commands to: {self.config['light']['ip']}:{self.config['light']['port']}")
|
logger.info(f"Ready to send light commands to: {self.config['light']['ip']}:{self.config['light']['port']}")
|
||||||
|
|
||||||
while self.isRunning.is_set():
|
while self.isRunning.is_set():
|
||||||
|
@ -232,7 +233,7 @@ class CentralCommand(object):
|
||||||
|
|
||||||
logger.warn('Stopping light sender')
|
logger.warn('Stopping light sender')
|
||||||
lightConn._sock.close()
|
lightConn._sock.close()
|
||||||
|
|
||||||
async def redLightController(self):
|
async def redLightController(self):
|
||||||
"""
|
"""
|
||||||
Every second, check if no hugveys are available. If so, the red light should be
|
Every second, check if no hugveys are available. If so, the red light should be
|
||||||
|
@ -264,7 +265,7 @@ class CentralCommand(object):
|
||||||
thread = threading.Thread(
|
thread = threading.Thread(
|
||||||
target=self.hugveyStateRunner, args=(hugvey_id,), name=f"hugvey#{hugvey_id}")
|
target=self.hugveyStateRunner, args=(hugvey_id,), name=f"hugvey#{hugvey_id}")
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def hugveyStateRunner(self, hugvey_id):
|
def hugveyStateRunner(self, hugvey_id):
|
||||||
while self.isRunning.is_set():
|
while self.isRunning.is_set():
|
||||||
logger.info(f'Instantiate hugvey #{hugvey_id}')
|
logger.info(f'Instantiate hugvey #{hugvey_id}')
|
||||||
|
@ -278,7 +279,7 @@ class CentralCommand(object):
|
||||||
return
|
return
|
||||||
logger.critical(f'Hugvey stopped (crashed?). Reinstantiate after 5 sec')
|
logger.critical(f'Hugvey stopped (crashed?). Reinstantiate after 5 sec')
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
async def timerEmitter(self):
|
async def timerEmitter(self):
|
||||||
"""
|
"""
|
||||||
This is fixed: a one hour loop with a collective moment 10-15 minutes,
|
This is fixed: a one hour loop with a collective moment 10-15 minutes,
|
||||||
|
@ -288,25 +289,25 @@ class CentralCommand(object):
|
||||||
intervals = [
|
intervals = [
|
||||||
{
|
{
|
||||||
'start_time': 10*60,
|
'start_time': 10*60,
|
||||||
'duration': 5 * 60,
|
'duration': 5 * 60,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'start_time': 30*60,
|
'start_time': 30*60,
|
||||||
'duration': 5 * 60,
|
'duration': 5 * 60,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'start_time': 50*60,
|
'start_time': 50*60,
|
||||||
'duration': 5 * 60,
|
'duration': 5 * 60,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
# TODO: emit start event
|
# TODO: emit start event
|
||||||
|
|
||||||
while self.isRunning.is_set():
|
while self.isRunning.is_set():
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def eventListener(self):
|
async def eventListener(self):
|
||||||
s = self.ctx.socket(zmq.SUB)
|
s = self.ctx.socket(zmq.SUB)
|
||||||
s.bind(self.config['events']['listen_address'])
|
s.bind(self.config['events']['listen_address'])
|
||||||
|
@ -319,7 +320,7 @@ class CentralCommand(object):
|
||||||
while self.isRunning.is_set():
|
while self.isRunning.is_set():
|
||||||
try:
|
try:
|
||||||
hugvey_id, msg = await zmqReceive(s)
|
hugvey_id, msg = await zmqReceive(s)
|
||||||
|
|
||||||
if hugvey_id not in self.hugvey_ids:
|
if hugvey_id not in self.hugvey_ids:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
"Message from alien Hugvey: {}".format(hugvey_id))
|
"Message from alien Hugvey: {}".format(hugvey_id))
|
||||||
|
@ -355,7 +356,7 @@ class CentralCommand(object):
|
||||||
fn = await self.voiceStorage.requestFile(hv.language_code, text, isVariable)
|
fn = await self.voiceStorage.requestFile(hv.language_code, text, isVariable)
|
||||||
if fn is None:
|
if fn is None:
|
||||||
eventLogger.getChild(f"{hugvey_id}").critical("error: No voice file fetched, check logs.")
|
eventLogger.getChild(f"{hugvey_id}").critical("error: No voice file fetched, check logs.")
|
||||||
fn = 'local/crash.wav'
|
fn = 'local/crash.wav'
|
||||||
# TODO: trigger a repeat/crash event.
|
# TODO: trigger a repeat/crash event.
|
||||||
await s.send_string(fn)
|
await s.send_string(fn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -376,7 +377,7 @@ class CentralCommand(object):
|
||||||
self.catchException(self.lightSender()))
|
self.catchException(self.lightSender()))
|
||||||
self.tasks['redLightController'] = self.loop.create_task(
|
self.tasks['redLightController'] = self.loop.create_task(
|
||||||
self.catchException(self.redLightController()))
|
self.catchException(self.redLightController()))
|
||||||
|
|
||||||
for hid in self.hugvey_ids:
|
for hid in self.hugvey_ids:
|
||||||
self.tasks['voiceListener'] = self.loop.create_task(
|
self.tasks['voiceListener'] = self.loop.create_task(
|
||||||
self.catchException(self.voiceListener(hid)))
|
self.catchException(self.voiceListener(hid)))
|
||||||
|
@ -386,12 +387,12 @@ class CentralCommand(object):
|
||||||
self.panopticon_thread = threading.Thread(
|
self.panopticon_thread = threading.Thread(
|
||||||
target=self.panopticon.start, name="Panopticon")
|
target=self.panopticon.start, name="Panopticon")
|
||||||
self.panopticon_thread.start()
|
self.panopticon_thread.start()
|
||||||
|
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.isRunning.clear()
|
self.isRunning.clear()
|
||||||
|
|
||||||
async def catchException(self, awaitable):
|
async def catchException(self, awaitable):
|
||||||
try:
|
try:
|
||||||
# print(awaitable)
|
# print(awaitable)
|
||||||
|
@ -405,7 +406,7 @@ class HugveyState(object):
|
||||||
"""Represents the state of a Hugvey client on the server.
|
"""Represents the state of a Hugvey client on the server.
|
||||||
Manages server connections & voice parsing etc.
|
Manages server connections & voice parsing etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# all statusses can only go up or down, except for gone, which is an error state:
|
# all statusses can only go up or down, except for gone, which is an error state:
|
||||||
# off <-> blocked <-> available <-> running <-> paused
|
# off <-> blocked <-> available <-> running <-> paused
|
||||||
STATE_OFF = "off"
|
STATE_OFF = "off"
|
||||||
|
@ -424,7 +425,7 @@ class HugveyState(object):
|
||||||
self.isConfigured = None
|
self.isConfigured = None
|
||||||
self.isRunning = asyncio.Event(loop=self.loop)
|
self.isRunning = asyncio.Event(loop=self.loop)
|
||||||
self.isRunning.clear()
|
self.isRunning.clear()
|
||||||
|
|
||||||
self.eventQueue = None
|
self.eventQueue = None
|
||||||
self.language_code = 'en-GB'
|
self.language_code = 'en-GB'
|
||||||
self.story = None
|
self.story = None
|
||||||
|
@ -435,24 +436,24 @@ class HugveyState(object):
|
||||||
self.notShuttingDown = True # TODO: allow shutdown of object
|
self.notShuttingDown = True # TODO: allow shutdown of object
|
||||||
self.startMsgId = None
|
self.startMsgId = None
|
||||||
self.eventLogger = eventLogger.getChild(f"{self.id}")
|
self.eventLogger = eventLogger.getChild(f"{self.id}")
|
||||||
|
|
||||||
self.setStatus(self.STATE_GONE)
|
self.setStatus(self.STATE_GONE)
|
||||||
|
|
||||||
self.requireRestartAfterStop = None
|
self.requireRestartAfterStop = None
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.logger.warn("Destroying hugvey object")
|
self.logger.warn("Destroying hugvey object")
|
||||||
|
|
||||||
def getStatus(self):
|
def getStatus(self):
|
||||||
return self.status
|
return self.status
|
||||||
|
|
||||||
def setStatus(self, status):
|
def setStatus(self, status):
|
||||||
self.status = status
|
self.status = status
|
||||||
lightOn = status in [self.STATE_AVAILABLE, self.STATE_PAUSE]
|
lightOn = status in [self.STATE_AVAILABLE, self.STATE_PAUSE]
|
||||||
self.setLightStatus(lightOn)
|
self.setLightStatus(lightOn)
|
||||||
self.eventLogger.info(f"status: {self.status}")
|
self.eventLogger.info(f"status: {self.status}")
|
||||||
|
|
||||||
|
|
||||||
def config(self, hostname, ip):
|
def config(self, hostname, ip):
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
|
@ -463,7 +464,7 @@ class HugveyState(object):
|
||||||
else:
|
else:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Hugvey {self.id} at {self.ip}, host: {self.hostname}")
|
f"Hugvey {self.id} at {self.ip}, host: {self.hostname}")
|
||||||
|
|
||||||
if self.status == self.STATE_GONE:
|
if self.status == self.STATE_GONE:
|
||||||
# turn on :-)
|
# turn on :-)
|
||||||
self.setStatus(self.STATE_BLOCKED)
|
self.setStatus(self.STATE_BLOCKED)
|
||||||
|
@ -496,7 +497,7 @@ class HugveyState(object):
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
self.logger.critical(f"Hugvey crash")
|
self.logger.critical(f"Hugvey crash")
|
||||||
self.eventLogger.critical(f"error: {e}")
|
self.eventLogger.critical(f"error: {e}")
|
||||||
|
|
||||||
# restart
|
# restart
|
||||||
# TODO: test proper functioning
|
# TODO: test proper functioning
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
@ -510,18 +511,18 @@ class HugveyState(object):
|
||||||
else:
|
else:
|
||||||
# Allow for both the Hugvey Command, or the Story handle the event.
|
# Allow for both the Hugvey Command, or the Story handle the event.
|
||||||
self.loop.call_soon_threadsafe(self._queueEvent, msg)
|
self.loop.call_soon_threadsafe(self._queueEvent, msg)
|
||||||
|
|
||||||
def _queueEvent(self, msg):
|
def _queueEvent(self, msg):
|
||||||
"""
|
"""
|
||||||
Put event in both the event loop for the story as well as the Hugvey State handler
|
Put event in both the event loop for the story as well as the Hugvey State handler
|
||||||
"""
|
"""
|
||||||
self.logger.debug(f"Queue event in hugvey loop: {msg}")
|
self.logger.debug(f"Queue event in hugvey loop: {msg}")
|
||||||
self.eventQueue.put_nowait(msg)
|
self.eventQueue.put_nowait(msg)
|
||||||
|
|
||||||
# connection events don't need to go to the story
|
# connection events don't need to go to the story
|
||||||
if msg['event'] == 'connection':
|
if msg['event'] == 'connection':
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.story:
|
if self.story:
|
||||||
self.story.events.append(msg)
|
self.story.events.append(msg)
|
||||||
else:
|
else:
|
||||||
|
@ -538,7 +539,7 @@ class HugveyState(object):
|
||||||
self.logger.error("Hugvey did not send heartbeat.")
|
self.logger.error("Hugvey did not send heartbeat.")
|
||||||
self.gone()
|
self.gone()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.logger.debug("Received: {}".format(event))
|
self.logger.debug("Received: {}".format(event))
|
||||||
if event['event'] == 'connection':
|
if event['event'] == 'connection':
|
||||||
# 'event': 'connection',
|
# 'event': 'connection',
|
||||||
|
@ -546,11 +547,11 @@ class HugveyState(object):
|
||||||
# 'host': socket.gethostname(),
|
# 'host': socket.gethostname(),
|
||||||
# 'ip': self.getIp(),
|
# 'ip': self.getIp(),
|
||||||
self.config(event['host'], event['ip'])
|
self.config(event['host'], event['ip'])
|
||||||
|
|
||||||
|
|
||||||
if event['event'] == 'language':
|
if event['event'] == 'language':
|
||||||
self.setLanguage(event['code'])
|
self.setLanguage(event['code'])
|
||||||
|
|
||||||
if event['event'] == 'pause':
|
if event['event'] == 'pause':
|
||||||
self.pause()
|
self.pause()
|
||||||
if event['event'] == 'block':
|
if event['event'] == 'block':
|
||||||
|
@ -563,7 +564,7 @@ class HugveyState(object):
|
||||||
self.story._finish() # finish story AND hugvey state
|
self.story._finish() # finish story AND hugvey state
|
||||||
if event['event'] == 'resume':
|
if event['event'] == 'resume':
|
||||||
self.resume()
|
self.resume()
|
||||||
|
|
||||||
if event['event'] == 'change_language':
|
if event['event'] == 'change_language':
|
||||||
self.setLanguage(event['lang_code'])
|
self.setLanguage(event['lang_code'])
|
||||||
if event['event'] == 'change_light':
|
if event['event'] == 'change_light':
|
||||||
|
@ -577,7 +578,7 @@ class HugveyState(object):
|
||||||
# self.restart()
|
# self.restart()
|
||||||
if self.story is None:
|
if self.story is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.startMsgId = event['msg_id']
|
self.startMsgId = event['msg_id']
|
||||||
self.logger.debug(f"Restart from {self.startMsgId}")
|
self.logger.debug(f"Restart from {self.startMsgId}")
|
||||||
self.restart()
|
self.restart()
|
||||||
|
@ -587,18 +588,18 @@ class HugveyState(object):
|
||||||
def setLanguage(self, language_code):
|
def setLanguage(self, language_code):
|
||||||
if language_code not in self.command.languages:
|
if language_code not in self.command.languages:
|
||||||
raise Exception("Invalid language {}".format(language_code))
|
raise Exception("Invalid language {}".format(language_code))
|
||||||
|
|
||||||
self.logger.info(f"set language: {language_code}")
|
self.logger.info(f"set language: {language_code}")
|
||||||
self.language_code = language_code
|
self.language_code = language_code
|
||||||
|
|
||||||
if self.google:
|
if self.google:
|
||||||
self.google.setLanguage(language_code)
|
self.google.setLanguage(language_code)
|
||||||
|
|
||||||
if self.isRunning.is_set():
|
if self.isRunning.is_set():
|
||||||
self.restart()
|
self.restart()
|
||||||
# self.story.reset()
|
# self.story.reset()
|
||||||
# self.story.setStoryData(self.command.languages[language_code])
|
# self.story.setStoryData(self.command.languages[language_code])
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
self.logger.info('Pause')
|
self.logger.info('Pause')
|
||||||
if self.google:
|
if self.google:
|
||||||
|
@ -607,7 +608,7 @@ class HugveyState(object):
|
||||||
self.story.pause()
|
self.story.pause()
|
||||||
self.isRunning.clear()
|
self.isRunning.clear()
|
||||||
self.setStatus(self.STATE_PAUSE)
|
self.setStatus(self.STATE_PAUSE)
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
""" Start playing without reset"""
|
""" Start playing without reset"""
|
||||||
self.logger.info('Resume')
|
self.logger.info('Resume')
|
||||||
|
@ -617,14 +618,14 @@ class HugveyState(object):
|
||||||
self.story.resume()
|
self.story.resume()
|
||||||
self.isRunning.set()
|
self.isRunning.set()
|
||||||
self.setStatus(self.STATE_RUNNING)
|
self.setStatus(self.STATE_RUNNING)
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
"""Start playing with reset"""
|
"""Start playing with reset"""
|
||||||
self.logger.info('Restart')
|
self.logger.info('Restart')
|
||||||
if self.story:
|
if self.story:
|
||||||
self.story.stop()
|
self.story.stop()
|
||||||
self.resume()
|
self.resume()
|
||||||
|
|
||||||
def block(self):
|
def block(self):
|
||||||
"""Block a hugvey"""
|
"""Block a hugvey"""
|
||||||
self.logger.info('block')
|
self.logger.info('block')
|
||||||
|
@ -634,37 +635,37 @@ class HugveyState(object):
|
||||||
self.story.finish()
|
self.story.finish()
|
||||||
self.isRunning.clear()
|
self.isRunning.clear()
|
||||||
self.setStatus(self.STATE_BLOCKED)
|
self.setStatus(self.STATE_BLOCKED)
|
||||||
|
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Put in available mode"""
|
"""Put in available mode"""
|
||||||
self.logger.info('Finish/Await')
|
self.logger.info('Finish/Await')
|
||||||
self.pause()
|
self.pause()
|
||||||
self.setStatus(self.STATE_AVAILABLE)
|
self.setStatus(self.STATE_AVAILABLE)
|
||||||
|
|
||||||
def setLightStatus(self, on):
|
def setLightStatus(self, on):
|
||||||
status = 1 if on else 0
|
status = 1 if on else 0
|
||||||
self.logger.log(LOG_BS, f"Send /hugvey {status}")
|
self.logger.log(LOG_BS, f"Send /hugvey {status}")
|
||||||
|
|
||||||
self.command.commandLight('/hugvey', [self.lightId, status])
|
self.command.commandLight('/hugvey', [self.lightId, status])
|
||||||
|
|
||||||
def setLightId(self, id):
|
def setLightId(self, id):
|
||||||
"""
|
"""
|
||||||
Connect hugvey to another light
|
Connect hugvey to another light
|
||||||
"""
|
"""
|
||||||
self.lightId = id
|
self.lightId = id
|
||||||
|
|
||||||
def gone(self):
|
def gone(self):
|
||||||
'''Status to 'gone' as in, shutdown/crashed/whatever
|
'''Status to 'gone' as in, shutdown/crashed/whatever
|
||||||
'''
|
'''
|
||||||
self.pause()
|
self.pause()
|
||||||
if self.story:
|
if self.story:
|
||||||
self.story.stop()
|
self.story.stop()
|
||||||
|
|
||||||
self.logger.warn('Gone')
|
self.logger.warn('Gone')
|
||||||
self.eventLogger.warn("Gone")
|
self.eventLogger.warn("Gone")
|
||||||
self.isConfigured = None
|
self.isConfigured = None
|
||||||
self.setStatus(self.STATE_GONE)
|
self.setStatus(self.STATE_GONE)
|
||||||
|
|
||||||
def shutdown(self, definitive = False):
|
def shutdown(self, definitive = False):
|
||||||
self.logger.info(f"Start shutdown sequence {definitive}")
|
self.logger.info(f"Start shutdown sequence {definitive}")
|
||||||
self.eventLogger.critical(f"error: shutting down")
|
self.eventLogger.critical(f"error: shutting down")
|
||||||
|
@ -673,7 +674,7 @@ class HugveyState(object):
|
||||||
if self.story:
|
if self.story:
|
||||||
self.story.shutdown()
|
self.story.shutdown()
|
||||||
self.story = None
|
self.story = None
|
||||||
|
|
||||||
# shutdown for stream consumers already ran. Only clear references
|
# shutdown for stream consumers already ran. Only clear references
|
||||||
if self.google:
|
if self.google:
|
||||||
self.google = None
|
self.google = None
|
||||||
|
@ -681,14 +682,14 @@ class HugveyState(object):
|
||||||
self.player = None
|
self.player = None
|
||||||
if self.recorder:
|
if self.recorder:
|
||||||
self.recorder = None
|
self.recorder = None
|
||||||
|
|
||||||
if self.requireRestartAfterStop is None:
|
if self.requireRestartAfterStop is None:
|
||||||
# prevent double setting of the same variable
|
# prevent double setting of the same variable
|
||||||
# first call sometimes triggers second
|
# first call sometimes triggers second
|
||||||
self.requireRestartAfterStop = not definitive
|
self.requireRestartAfterStop = not definitive
|
||||||
|
|
||||||
self.notShuttingDown = False
|
self.notShuttingDown = False
|
||||||
|
|
||||||
|
|
||||||
async def playStory(self):
|
async def playStory(self):
|
||||||
while self.notShuttingDown:
|
while self.notShuttingDown:
|
||||||
|
@ -706,13 +707,13 @@ class HugveyState(object):
|
||||||
self.logger.warn(f"Starting from {startMsgId}")
|
self.logger.warn(f"Starting from {startMsgId}")
|
||||||
if not self.streamer:
|
if not self.streamer:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
self.streamer.triggerStart()
|
self.streamer.triggerStart()
|
||||||
self.story.setStoryData(self.command.languages[self.language_code])
|
self.story.setStoryData(copy.deepcopy(self.command.languages[self.language_code]))
|
||||||
self.setLightStatus(False)
|
self.setLightStatus(False)
|
||||||
await self.story.run(startMsgId)
|
await self.story.run(startMsgId)
|
||||||
# self.story = None
|
# self.story = None
|
||||||
|
|
||||||
def getStreamer(self):
|
def getStreamer(self):
|
||||||
if not self.streamer:
|
if not self.streamer:
|
||||||
self.streamer = AudioStreamer(
|
self.streamer = AudioStreamer(
|
||||||
|
@ -720,19 +721,19 @@ class HugveyState(object):
|
||||||
self.ip,
|
self.ip,
|
||||||
int(self.command.config['voice']['port']) + self.id,
|
int(self.command.config['voice']['port']) + self.id,
|
||||||
self.id)
|
self.id)
|
||||||
|
|
||||||
if self.command.config['voyeur']:
|
if self.command.config['voyeur']:
|
||||||
self.logger.warn("Debug on: Connecting Audio player")
|
self.logger.warn("Debug on: Connecting Audio player")
|
||||||
self.player = Player(
|
self.player = Player(
|
||||||
self.command.config['voice']['src_rate'], self.command.config['voice']['out_rate'])
|
self.command.config['voice']['src_rate'], self.command.config['voice']['out_rate'])
|
||||||
self.streamer.addConsumer(self.player)
|
self.streamer.addConsumer(self.player)
|
||||||
|
|
||||||
if self.command.config['voice']['record_dir']:
|
if self.command.config['voice']['record_dir']:
|
||||||
self.logger.warn("Record Audio of conversation")
|
self.logger.warn("Record Audio of conversation")
|
||||||
self.recorder = Recorder( self.id,
|
self.recorder = Recorder( self.id,
|
||||||
self.command.config['voice']['src_rate'], self.command.config['voice']['record_dir'])
|
self.command.config['voice']['src_rate'], self.command.config['voice']['record_dir'])
|
||||||
self.streamer.addConsumer(self.recorder)
|
self.streamer.addConsumer(self.recorder)
|
||||||
|
|
||||||
self.logger.debug("Start Speech")
|
self.logger.debug("Start Speech")
|
||||||
self.google = GoogleVoiceClient(
|
self.google = GoogleVoiceClient(
|
||||||
hugvey=self,
|
hugvey=self,
|
||||||
|
@ -747,7 +748,7 @@ class HugveyState(object):
|
||||||
'''
|
'''
|
||||||
Start the audio streamer service
|
Start the audio streamer service
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.logger.debug("Start audio loop")
|
self.logger.debug("Start audio loop")
|
||||||
|
|
||||||
while self.notShuttingDown:
|
while self.notShuttingDown:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
apt-get update
|
apt-get update
|
||||||
|
|
||||||
apt-get install -y munin-node bc supervisor libsox-fmt-pulse
|
apt-get install -y munin-node bc supervisor
|
||||||
cp installation/rpi-internal-temp /usr/share/munin/plugins
|
cp installation/rpi-internal-temp /usr/share/munin/plugins
|
||||||
ln -sf /usr/share/munin/plugins/rpi-internal-temp /etc/munin/plugins/rpi-internal-temp
|
ln -sf /usr/share/munin/plugins/rpi-internal-temp /etc/munin/plugins/rpi-internal-temp
|
||||||
rm /etc/munin/plugins/irqstats
|
rm /etc/munin/plugins/irqstats
|
||||||
|
|
|
@ -7,7 +7,7 @@ voice:
|
||||||
port: 4444
|
port: 4444
|
||||||
chunk: 2972
|
chunk: 2972
|
||||||
google_credentials: "../test_googlespeech/My First Project-0c7833e0d5fa.json"
|
google_credentials: "../test_googlespeech/My First Project-0c7833e0d5fa.json"
|
||||||
hugveys: 25
|
hugveys: 26
|
||||||
languages:
|
languages:
|
||||||
- code: en-GB
|
- code: en-GB
|
||||||
file: story_en.json
|
file: story_en.json
|
||||||
|
@ -28,7 +28,7 @@ languages:
|
||||||
ms_lang: "fr-FR"
|
ms_lang: "fr-FR"
|
||||||
web:
|
web:
|
||||||
port: 8888
|
port: 8888
|
||||||
files_dir: "local/"
|
files_dir: "local/"
|
||||||
light:
|
light:
|
||||||
ip: "192.168.178.15"
|
ip: "192.168.178.15"
|
||||||
port: 7400
|
port: 7400
|
||||||
|
|
|
@ -18,60 +18,60 @@ class Timeline{
|
||||||
{content: '.', start: new Date(), type: 'point', group: 1}
|
{content: '.', start: new Date(), type: 'point', group: 1}
|
||||||
]);
|
]);
|
||||||
console.log('init timeline');
|
console.log('init timeline');
|
||||||
|
|
||||||
let groups = [];
|
let groups = [];
|
||||||
for(let hid = 1; hid<=this.count; hid++) {
|
for(let hid = 1; hid<=this.count; hid++) {
|
||||||
groups.push({id: parseInt(hid), content: 'Hugvey #'+hid});
|
groups.push({id: parseInt(hid), content: 'Hugvey #'+hid});
|
||||||
this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)})
|
this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)})
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataGroups = new vis.DataSet(groups);
|
let dataGroups = new vis.DataSet(groups);
|
||||||
let options = {
|
let options = {
|
||||||
// 'rollingMode': {'follow': true, 'offset': .8 }
|
// 'rollingMode': {'follow': true, 'offset': .8 }
|
||||||
};
|
};
|
||||||
console.log('groups', dataGroups, groups, options);
|
console.log('groups', dataGroups, groups, options);
|
||||||
|
|
||||||
this.timeline = new vis.Timeline(this.el, this.eventDataSet, dataGroups, options);
|
this.timeline = new vis.Timeline(this.el, this.eventDataSet, dataGroups, options);
|
||||||
|
|
||||||
let tl = this.timeline;
|
let tl = this.timeline;
|
||||||
let startDate = new Date();
|
let startDate = new Date();
|
||||||
startDate.setMinutes(startDate.getMinutes()-1);
|
startDate.setMinutes(startDate.getMinutes()-1);
|
||||||
let endDate = new Date();
|
let endDate = new Date();
|
||||||
endDate.setMinutes(endDate.getMinutes()+20);
|
endDate.setMinutes(endDate.getMinutes()+20);
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
tl.setWindow(startDate, endDate);
|
tl.setWindow(startDate, endDate);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
this.moveInterval = setInterval(function(){
|
this.moveInterval = setInterval(function(){
|
||||||
// skip movement if not visible
|
// skip movement if not visible
|
||||||
tl.moveTo(new Date());
|
tl.moveTo(new Date());
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
ws.addEventListener( 'message', this);
|
ws.addEventListener( 'message', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(e) {
|
handleEvent(e) {
|
||||||
console.log('handle', e, this);
|
console.log('handle', e, this);
|
||||||
if(e.type == 'message') {
|
if(e.type == 'message') {
|
||||||
this.wsOnMessage(e)
|
this.wsOnMessage(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wsOnMessage(e) {
|
wsOnMessage(e) {
|
||||||
let msg = JSON.parse( e.data );
|
let msg = JSON.parse( e.data );
|
||||||
|
|
||||||
if ( typeof msg['action'] === 'undefined' ) {
|
if ( typeof msg['action'] === 'undefined' ) {
|
||||||
console.error( "not a valid message: " + e.data );
|
console.error( "not a valid message: " + e.data );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(msg['action'] != 'log') {
|
if(msg['action'] != 'log') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug(msg, this);
|
console.debug(msg, this);
|
||||||
|
|
||||||
let hv_id = parseInt(msg['id']);
|
let hv_id = parseInt(msg['id']);
|
||||||
// {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'}
|
// {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'}
|
||||||
let d, parts;
|
let d, parts;
|
||||||
|
@ -91,7 +91,7 @@ class Timeline{
|
||||||
this.eventDataSet.update(d);
|
this.eventDataSet.update(d);
|
||||||
console.log('update', d);
|
console.log('update', d);
|
||||||
} else {
|
} else {
|
||||||
this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'});
|
this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'speaking':
|
case 'speaking':
|
||||||
|
@ -101,7 +101,7 @@ class Timeline{
|
||||||
let id = parts.shift();
|
let id = parts.shift();
|
||||||
let content = parts.join(' ');
|
let content = parts.join(' ');
|
||||||
let scId = 'sc-'+id+'-'+hv_id;
|
let scId = 'sc-'+id+'-'+hv_id;
|
||||||
|
|
||||||
if(info.startsWith('start')){
|
if(info.startsWith('start')){
|
||||||
this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'});
|
this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'});
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ class Timeline{
|
||||||
this.eventDataSet.update(d);
|
this.eventDataSet.update(d);
|
||||||
} else {
|
} else {
|
||||||
console.log('add');
|
console.log('add');
|
||||||
this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'});
|
this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(info.startsWith('end')){
|
if(info.startsWith('end')){
|
||||||
|
@ -125,7 +125,7 @@ class Timeline{
|
||||||
this.eventDataSet.update(d);
|
this.eventDataSet.update(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'story':
|
case 'story':
|
||||||
// 'info': 'start'/'finished'
|
// 'info': 'start'/'finished'
|
||||||
|
@ -147,4 +147,4 @@ class Timeline{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tl = new Timeline(ws, document.getElementById('line'), 25);
|
var tl = new Timeline(ws, document.getElementById('line'), 26);
|
||||||
|
|
Loading…
Reference in a new issue