Need help porting LANnouncer DTH

I found this thread while integrating LANnouncer into my HE. TTS worked OK but no siren, chime, etc. No option I could find to use those directly from HE. From this thread I understand LANnouncer should be able to activate those commands through the 'speak' command but I never had any luck with that, perhaps because of the URL encoding. So I made a rough kludge/hack to jparks speech command to do it. Edit your driver code speak command to add this before it calls sendcommand() for the speech:

if (toSay.matches( "ALARMON")) {
    log.debug "Executing 'siren'"
    sendEvent(name:"alarm", value:"siren")
    def command=(AlarmContinuous?"&ALARM=SIREN:CONTINUOUS&":"&ALARM=SIREN&")+getDoneString();
    return sendCommands(command);

} 

This code is from the siren() function. Using this method you can add more IF statements to make custom commands that will fire any of the other events that you want by using 'speak'.

Now when I tell LANnouncer to speak ALARMON it kicks on the siren. Hopefully this will be of help to someone.

Many, many thanks to NWTONY and JPARK - you guys rock!

Here is my code, it handles both spaces in the text to speech and have no issues with siren and chime:

/**
 *  LANnouncer Alerter-Hubitat (Originally LANdroid Alerter)
 *
 *  Requires the Android TTSService (aka LANnouncer  in the Play Store; https://play.google.com/store/apps/details?id=com.keybounce.lannouncer )
 *
 *  Copyright 2015 Tony McNamara
 *  8/1/2019 - Further modifyed by Ron Vargo to remove SmartThings references and support spaces in TTS strings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */

metadata {
    definition (name: "LANnouncer Alerter-Hubitat", namespace: "RonV42", author: "Ron Vargo") {
        capability "Alarm"
        capability "Speech Synthesis"
        capability "Notification"
        capability "Tone"
        attribute  "LANnouncerSMS","string"
    }
    preferences {
        input("DeviceLocalLan", "string", title:"Android IP Address", description:"Please enter your tablet's I.P. address", defaultValue:"" , required: false, displayDuringSetup: true)
        input("DevicePort", "string", title:"Android Port", description:"Port the Android device listens on", defaultValue:"1035", required: false, displayDuringSetup: true)
        input("ReplyOnEmpty", "bool", title:"Say Nothing", description:"When no speech is found, announce LANdroid?  (Needed for the speech and notify tiles to work)", defaultValue: true, displayDuringSetup: true)
    }

    simulator {
        
    }

    tiles {
        standardTile("alarm", "device.alarm", width: 2, height: 2) {
            state "off", label:'off', action:'alarm.both', icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff"
            state "strobe", label:'strobe!', action:'alarm.off', icon:"st.Lighting.light11", backgroundColor:"#e86d13"
            state "siren", label:'siren!', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13"
            state "both", label:'alarm!', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13"
        }

        standardTile("strobe", "device.alarm", inactiveLabel: false, decoration: "flat") {
            state "default", label:'', action:"alarm.strobe", icon:"st.secondary.strobe"
        }
        standardTile("siren", "device.alarm", inactiveLabel: false, decoration: "flat") {
            state "default", label:'', action:"alarm.siren", icon:"st.secondary.siren"
        }       

        standardTile("speak", "device.speech", inactiveLabel: false, decoration: "flat") {
            state "default", label:'Speak', action:"Speech Synthesis.speak", icon:"st.Electronics.electronics13"
        }
        standardTile("toast", "device.notification", inactiveLabel: false, decoration: "flat") {
            state "default", label:'Notify', action:"notification.deviceNotification", icon:"st.Kids.kids1"
        }
        standardTile("beep", "device.tone", inactiveLabel: false, decoration: "flat") {
            state "default", label:'Tone', action:"tone.beep", icon:"st.Entertainment.entertainment2"
        }       

        main "alarm"
        details(["alarm","strobe","siren","speak","toast","beep"])
    }
}

// parse events into attributes
def parse(String description) {
    log.debug "Parsing '${description}'"
    // TODO: handle 'alarm' attribute

}

// handle commands
def off() {
    log.debug "Executing 'off'"
    // TODO: handle 'off' command
}

def strobe() {
    log.debug "Executing 'strobe'"
    // TODO: handle 'strobe' command
    def command="&FLASH=STROBE&"+getDoneString()
    sendCommands(command)

}

def siren() {
    log.debug "Executing 'siren'"
    // TODO: handle 'siren' command
    def command="&ALARM=SIREN&"+getDoneString()
    sendCommands(command)
}

def beep() {
    log.debug "Executing 'beep'"
    // TODO: handle 'siren' command
    def command="&ALARM=CHIME&"+getDoneString()
    sendCommands(command)
}

def both() {
    log.debug "Executing 'both'"
    // TODO: handle 'both' command
    def command="&ALARM=ON&FLASH=ON&"+getDoneString()
    sendCommands(command)
}


def speak(toSay) {
    log.debug "Executing 'speak'"
    if (!toSay?.trim()) {
        if (ReplyOnEmpty) {
            toSay = "Landroid Speech Synthesizer"
        }
    }
    
    toSay = toSay.replaceAll("\\s","%20")

    if (toSay?.trim()) {
        def command="&SPEAK="+toSay+"&"+getDoneString()
        sendCommands(command)
    }
}

def deviceNotification(toToast) {
    log.debug "Executing notification with "+toToast
    if (!toToast?.trim()) {
        if (ReplyOnEmpty) {
            toToast = "Landroid Speech Synthesizer"
        }
    }
    if (toToast?.trim()) {
        def command="&TOAST="+toToast+"&"+getDoneString()
        sendCommands(command)
    }
}    

/* Send to IP and to SMS as appropriate */
private sendCommands(command) {
    log.debug "Command request: "+command
//    sendSMSCommand(command)
    sendIPCommand(command)
}

private sendIPCommand(commandString ) {
    log.debug "Sending command to "+DeviceLocalLan
    sendEvent(name: "LANnouncerIP", value: commandString, isStateChange: true)
    if (DeviceLocalLan?.trim()) {
        def host = DeviceLocalLan 
        def hosthex = convertIPtoHex(host)
        def porthex = convertPortToHex(DevicePort)
        device.deviceNetworkId = "$hosthex:$porthex" 

        def headers = [:] 
        headers.put("HOST", "$host:$DevicePort")

        def method = "GET"

        def hubAction = new hubitat.device.HubAction([
            method: method,
            path: "/"+commandString,
            headers: headers],
            device.deviceNetworkId
            )
        hubAction
    }
}

private sendSMSCommand(commandString) {
    def preface = "+@TTSSMS@+"
    def smsValue = preface+"&"+commandString
    state.lastsmscommand = smsValue
    sendEvent(name: "LANnouncerSMS", value: smsValue, isStateChange: true)
    /*
    if (SMSPhone?.trim()) {
        sendSmsMessage(SMSPhone, preface+"&"+commandString)
    }
    */
}

private String getDoneString() {
    return "@DONE@"
}


private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
    log.debug "IP address entered is $ipAddress and the converted hex code is $hex"
    return hex

}

private String convertPortToHex(port) {
    String hexport = port.toString().format( '%04x', port.toInteger() )
    log.debug hexport
    return hexport
}

Having some issues trying to implement this code. I have a zigbee motion sensor sending notifications to my android phone with reasonable dependability, but adding to that HSM a speech alert via Lannouncer is hit or miss. It usually works once and then never again until I reset everything or tinker. I have been able to receive the notification through the Hubitat app along with the speech alert and even the siren. It just never repeats consistently.

My hope is to eventually add this android app to a dedicated tablet running one of the dashboard apps, but I can’t even get it running on my phone.

I wish the native Hubitat android app could receive speech alerts without having to rely on an additional app or codebase.

Thanks to ronv42 for the snippet. I think I installed it right, but there was some trial and error involved.

One of the things I have found that the LANnouncer app works best when in the foreground on most phones. And older versions of android are better than newer. Android will kill background services on newer versions of their OS if you don't directly interact with the app. I've been lucky with older Moto G6 phone's but every once an a while I get one that doesn't leave the app running.

Thanks for the prompt reply ronv42. I'll move on to trying to get an older tablet up and running as advised. Perhaps the older OS will behave. Hopefully running a dashboard app in the foreground doesn't interfere.

I've read other posts about workarounds to avoid web-connected solutions, or expensive solutions like Sonos. Is cobbling together something like I'm attempting still the only solution for local speech prompts or alerts?

Well, good grief! I seem to be drifting further away the more I tinker. Can someone check my process here?

I pasted the code that Ron provided into the Drivers Code section as is with no changes

I added a new Virtual Device selecting “LANnouncer Alerter-Hubitat” under “type” and named the device “LANnouncer”

I entered the reported IP address taken from the running Android app and left the port as 1035

In HSM config, I added %device% %value% %time% as “Message to speak” and chose the new device. The volume level shows as 10 without an option to change.

Save, done back out of the menus and trigger a motion sensor.

I hear the text from the tablet running LANnouncer at low volume and also receive a text notification from my phone running the Hubitat app.

After the motion sensor goes back to inactive state, I trigger the sensor a second time and nothing happens.

If I go into the HSM config and “Cancel Alerts” it works again.

Before, when I was merely sending text notifications to my phone, subsequent motion sensors did not require a cancellation before re-triggering the alert. Is there something more I need to do allow for consistent and repeated voice alerts?