Ambient behavior from bus events: react to listening state and time of day¶
In a nutshell
You build an OVOSSkill that reacts to bus events instead of utterances, using add_event on ovos.listener.record.* / ovos.audio.output.* events plus a repeating scheduled check.
When you'd want this: a skill needs to do something not triggered by an utterance at all. Examples include dimming a light while the device is actively listening, or changing its greeting depending on whether it's morning or night, driven purely by bus events other services already emit.
import datetime
from ovos_bus_client.message import Message
from ovos_workshop.skills import OVOSSkill
class AmbientMoodSkill(OVOSSkill):
def initialize(self):
# the listener (ovos-dinkum-listener) emits these around each
# recording, independent of any skill or intent
self.add_event("ovos.listener.record.started", self.handle_listening_start)
self.add_event("ovos.listener.record.ended", self.handle_listening_end)
# ovos-audio emits these around each spoken response
self.add_event("ovos.audio.output.started", self.handle_speaking_start)
self.add_event("ovos.audio.output.ended", self.handle_speaking_end)
# check the time of day once now, and again every 15 minutes
self._update_time_of_day()
self.schedule_repeating_event(self._update_time_of_day, None, 15 * 60,
name="mood_time_check")
def handle_listening_start(self, message):
self._set_mood("listening")
def handle_listening_end(self, message):
self._set_mood("idle")
def handle_speaking_start(self, message):
self._set_mood("speaking")
def handle_speaking_end(self, message):
self._set_mood("idle")
def _update_time_of_day(self, message=None):
hour = datetime.datetime.now().hour
self.is_daytime = 7 <= hour < 20
def _set_mood(self, mood):
self.bus.emit(Message("ovos.ambient_mood.changed",
{"mood": mood, "daytime": self.is_daytime}))
Moving parts¶
ovos.listener.record.started/ovos.listener.record.endedbracket an active recording (wake word already triggered).ovos.audio.output.started/.endedbracket the device speaking and are emitted byovos-audio. Each of these four also travels under a pre-spec name (recognizer_loop:record_begin,recognizer_loop:record_end,recognizer_loop:audio_output_start,recognizer_loop:audio_output_end), so older subscribers keep working, but new skills subscribe to the spec names shown here. Both pairs fire regardless of which skill (if any) is involved. There is alsorecognizer_loop:wakeword, emitted the instant the wake word itself is detected, slightly before recording begins.self.add_event(msg_type, handler)subscribes for the lifetime of the skill (auto-removed on shutdown). This is the general-purpose alternative to a decorator-based intent handler, for any bus event that isn't an utterance.schedule_repeating_event(handler, when, frequency, name=...)withwhen=Nonestarts the first run after onefrequencyinterval. Pass adatetimeforwheninstead if the first run needs to happen at a specific moment.- This skill emits its own
ovos.ambient_mood.changedevent rather than reaching into a light/hardware plugin directly, keeping it decoupled from whatever actually consumes the mood (a PHAL plugin, another skill, a GUI). See Bus Service for the emit/on API and PIPELINE-1 correlation for how bus events relate to a given utterance's session.
Read next: Skill Cookbook Related: Bus Service · Converse Pipeline · Reminder with restart persistence