Rhasspy with openHAB, part 3: questions and answers

Posted by

In addition to voice announcements and voice commands, I also implemented a “dialog” feature, where I can ask for information, and get an answer from the system. Currently, this very limited, I can only ask for the time of day, for temperature and humidity.

In /etc/openhab/things/rhasspy.things we have

Thing mqtt:topic:rh-mq:VoiceQuestion (mqtt:broker:rh-mq) {
  Channels:
    Type string: Message [ stateTopic="hermes/intent/VoiceQuestion" ]
}

In /etc/openhab/items/rhasspy.items we have

String Rhasspy_Question  "Rhasspy Question"  { channel="mqtt:topic:rh-mq:VoiceQuestion:Message" }

String vqTime "time" (gVQ)  // dummy items picked up by Rhasspy slot program

in other .items files, I have (the details are not relevant for what I am trying to explain here)

Number localCurrentTemperature "temperature [%.0f°C]" <temperature>  (gVQ)  {...some binding...}
Number localCurrentHumidity    "humidity [%.0f%%rH]"  <humidity>     (gVQ)  {...some binding...}

In /etc/openhab/rules/rhasspy.rules we have

rule "Rhasspy VoiceQuestion message"
when 
    Item Rhasspy_Question received update 
then 
    if (IsLive.state!=ON) { return; }

    val String json = newState.toString
    val String rawInput = transform("JSONPATH","$.rawInput", json)
    val String topic = transform("JSONPATH","$.slots[?(@.entity=='oh_items')].rawValue", json).toLowerCase
    val String siteId = transform("JSONPATH","$.siteId", json)
    logInfo("voice","Site {} heard '{}', ask about '{}'", siteId, rawInput, topic )

    var String answer = " I don't know"

    if (topic=="temperature") {
        answer = "the temperature is " 
        + String::format("%.0f", (localCurrentTemperature.state as DecimalType).floatValue )
        + " degrees outside and "
        + String::format("%.0f", (AZ_Temp.state as DecimalType).floatValue )
        + " degrees inside"
    } else if (topic=="humidity") {
        answer = "the " + topic + " is " 
        + String::format("%.0f", (localCurrentHumidity.state as DecimalType).floatValue )
        + " percent"
    } else if (topic == "time") {
        answer = "it is " + String.format("%1$tH:%1$tM", now)
    }

    // where should the answer be heard?
    val sinkName = transform("MAP","source_to_sink.map",siteId)
    val sinkItem = gSay.members.findFirst[ t | t.name=="say_"+sinkName]
    if (sinkItem !== null) {
        sinkItem.sendCommand(answer)
    }

    logInfo("voice","QUESTION '{}' to '{}', ANSWER '{}' to '{}'", topic, siteId, answer, sinkName)
end 

This part is a bit ugly, because I have a long if .. else if chain to identify each question topic. There is room for improvement …

Leave a Reply