Sendmail - Send email and text notifications, (notification device) no local server needed

I really wouldn't think that would almost ever happen. I really wish you were compatible with @ritchierich's format.

But thanks for adding the feature!


@kahn-hubitat happy to work with you to make something consistent. I considered including other attributes such as To but decided against it for my initial release. One thought is to require an “object/map” be passed via message string and our code could look for message starting with { and ending with }. For example:
{Subject:Email Subject,, Message: This is a test}

Then code would parse the string to JSON object and gather the values and substitute from default settings if entered. This wouldn’t require prefixes and keys could be entered in any order but it will require a Message key.


Interesting but no need to change the message as i want to still allow that at the end so that anything in place works without changes.

I was also thinking of leaving what have done
And just adding the subject: header and then it would work with what you have and that would by far be simplest.

I also could easily alleviate the order dependency with what i have by checking at top with a big compound if and wrapping the code in a loop but figured it wasn't necessary as the functionality is already there.

But if you implement something with {header: value, header: value }, Message

I would use your code

The cc case was a slightly more complicated for me as it adds a new header not just swap one.

Also what would happen with multiple subjects or multiple froms as the syntax would allow it.

Ok i will just two ver 4.91 and add the subject: header and then see what shakes out.

Rambling done.


2 sec. change

new version 4.91

  • 4.91 added option for the subject header to be rh-Subject: or just Subject: to be consistent with ritchierich code base so you can
  • have one notification send through both implementations.

@kahn-hubitat I just tested this in my app and it works. Message sent would be:
{Subject:Email Subject , Message:Email body,}

Only difference is my Gmail notification won't accept a From since that is defaulted by the API.

I tried to convert the string to a JSON object but ran into issues since it would require quotes around the key and value so came up with a different method. Hopefully this works in your app:

if (message.startsWith("{") && message.endsWith("}")) {
	message = message.replace("{", "")
	message = message.replace("}", "")
	def messageSplit = message.split(",")
	def messageObject = [:]
	for (int i = 0; i < messageSplit.size(); i++) {
		def messagePart = messageSplit[i]
		def messagePartSplit = messagePart.split(":")
		if (messagePartSplit.size() == 2) {
			messageObject[messagePartSplit[0]] = messagePartSplit[1].trim()

	toEmail = (messageObject.containsKey("To")) ? messageObject.To : toEmail
	subject = (messageObject.containsKey("Subject")) ? messageObject.Subject : subject
	message = (messageObject.containsKey("Message")) ? messageObject.Message : message
	fromEmail = (messageObject.containsKey("From")) ? messageObject.From : fromEmail
1 Like

That's not gonna work for me as i said it would break all existing stuff to have the closing bracket at the end and the message inside.

Also would break messages with commas in them which is why i.didnt use.split. i could modify it to isolate the begin and end bracket leaving the message after it and then call your code.

crap already had this code in and it breaks the brackets.. not sure what i was trying to do i think pull out the message if passed som xml

if(msgData.substring(0,1) == "{") {

	        def slurper = new groovy.json.JsonSlurper()
	        def result = slurper.parseText(msgData)	      
            emlBody = result.Body
	        emlSubject = (result.Subject != null ? result.Subject : "")
        } else {
           	emlBody = msgData
        	emlSubject = (Subject != null ? "${Subject}" : "")

ya already had this code to parse the subject out of straight xml.. weird. so brackets are not going to work or it will break anyone using this feature.. must have added it for a reason.

1 Like

contains key is not working correctly.. will look at alternate solution..


  • v 4.92
    the original header replacement above still works
  • but added an alternative message based on ritchierich pseudo xml at the start of the message
    {header: value, header: value}, this is the message
    {header: value, header: value. Message: this is the message}
    both work.
    Legal header values for replacement are: Subject: From: To: CC: Message:
    order is not important.
    {Subject: new subject,, Message:newmessage here,To:, From:}, this is a test
    {Subject: new subject,, Message:newmessage here,To:, From:}

caveat: just notice if you use the message: replacement option the message itself cannot have a colon : in it or it will truncate past the colon.


here is the bulk of the code change

// handle bracket case for ehader replacement
if (emlBody.startsWith("{"))

               def newFrom = ""
               def newTo = ""
               def newCC = ""
               def newSubject = ""
               def newMessage = ""
               def headers = ""
               def body = ""
               if (state.debug) log.debug "found header directive start {."     
               // now find end and separate
               def sMsg = emlBody.split("}")
               emlBody = emlBody.replace("{", "")
               if (state.debug) log.debug "size = ${sMsg.size()}"
               if (sMsg.size() == 2)
                   // now strip out comma on message
                   def cMsg = sMsg[1].split(",")
                   headers = sMsg[0]
                   body = cMsg[1]
                   if (state.debug) log.debug "left side (headers) = $headers, right side (msg} = ${cMsg[1]}, body = $body"
                   // message must be inside
                   headers = sMsg[0]
                   if (state.debug) log.debug "left side (headers) = $headers, right side blank (message must be in options)!"                     
                   //emlBody = cMSg[1]
                   headers = headers.replace("}", "")
                   headers = headers.replace("{", "")
                   def headerSplit = headers.split(",")

                   if (state.debug) log.debug "header split = ${headerSplit} size = ${headerSplit.size()}"
                   for (int i = 0; i < headerSplit.size(); i++) 
	                  def headerPart = headerSplit[i]
	                  def headerPartSplit = headerPart.split(":")
                       if (state.debug) log.debug "part = ${headerPart}"
                    if (headerPart.contains("To")) 
                         def dh = headerPart.split(":")
                         if (dh.size() == 2) newTo = dh[1].trim() 
                    if (headerPart.contains("Subject")) 
                         def dh = headerPart.split(":")
                         if (dh.size() == 2) newSubject = dh[1].trim()
                    if (headerPart.contains("From")) 
                       def dh = headerPart.split(":")
                       if (dh.size() == 2) newFrom = dh[1].trim()
                     if (headerPart.contains("CC")) 
                       def dh = headerPart.split(":")
                       if (dh.size() == 2) newCC = dh[1].trim()
                     if (headerPart.contains("Message")) 
                       def dh = headerPart.split(":")
                       if (dh.size() == 2) newMessage = dh[1].trim()
                   } // loop
                    if (state.debug) log.debug "newFrom = *${newFrom}*, newTo = *${newTo}*, newSubject = *${newSubject}* newCC = *${newCC}* newMessage = *${newMessage}*"
                   // now handle using the new fields if there
                   if (newFrom != "") From = newFrom
                   if (newTo != "") To = newTo
                   if (newSubject != "") emlSubject = newSubject
                   if (newMessage != "") emlBody = newMessage
                     else emlBody = body
                   if (newCC != "") 
                       cc = newCC
                       ccFound = true
           } // starts with }

Just tested changing the Subject of an email and it works great! Thanks.

Here is what I did using this in WebCore:


This project is a great idea. Couple of questions...

Is it attachment capable (local files) or is it just text?

Can you use global varialbes on the driver page instead of header directives?

no for attachments .. where would you get the file anyway from the file manager..

too complicated to pull a file in and start uuencoding it anyway.

second, there is no way to know even what global variable you would want to use nor access. so no to that as well.

1 Like

Not really sure I follow your use case but the stock Notifications app supports Global Variables to be included in the message as of v2.3.4 and Rule Machine does as well. So if the notification you are trying to send comes from one of those apps you can include the GV values within the notification as they get sent to the notification devices and from there to whatever device it is set to send it to Email vs Push vs SMS vs etc...

1 Like

Thanks.. I ended up creating a webcore template to take care of associating gv's and applying them to the directives. Now I can just set them and call it from all my other instances.

1 Like

Hi, I am looking to do SMTP notifications to an internal email server. There will be no need for authentication.

This is a very long thread going back several years so I was wondering if someone could point me to the correct spot to just install the latest version of the app and then the process for just sending over to a local email server. Thanks!

Easiest methos is to use Hubitat Package Manager and search for "sendmail" and install it from there. Otherwise list most community solutions the code links are in the very first post.


As noted, install it from Hubitat Package Manager.

The process/setup is documented above - you create a new device on the Device tab in HE, and then complete the setup of the parent,which then creates the child devices.

Example setup:

Once you save preferences on the parent device it will create the necessary number of children matching the "concurrent messages" setting:
2023-02-01 08_42_38-Chrome Main
2023-02-01 08_43_04-Chrome Main


Thank you!

Thank you for the response!

1 Like