Howto setup Alexa to announce your incoming Asterisk calls using openhab

Hi all,
This is not for the faint hearted and requires some reasonable level of Asterisk knowledge, with that said.

This is my version of how I get the Amazon Echo (Alexa) to announce my incoming calls.
There are loads of ways this can be done and I am merely providing one way.

It has a dependency that you have

  1. AmazonEchoControl binding setup and able to make announcements using TTS command.
  2. Also I recommend that you have the OH2 REST UI enabled it makes testing easier.

On Openhab I created the following:

items

Switch  asteriskCall       "Ring Group Call [%s]" { expire="3s,command=OFF" }
String  asteriskCallerID "Caller ID [%s]"
String  asteriskCallerIDCNAM "Caller ID Name [%s]"

Sitemap

		Text label="Asterisk" icon=phone {
			Default item=asteriskCallerID
			Default item=asteriskCallerIDCNAM
			Default item=asteriskCall
		}

Rules

var String caller
val String precallerid = "Incoming call, "

rule "incoming call"
when
    // asteriskCallerID is a string item which contains callerID populated in the asterisk dialplan
    Item asteriskCallerID received command 
then
    logInfo("Asterisk-Caller-ID","Caller ID " + asteriskCallerID.state.toString )
	logInfo("Asterisk-Caller-ID","  Incoming Call " + asteriskCall.state )
	// home contacts, add a case statement for each number to announce.
	if (asteriskCall.state==ON) {
	// map known names to CID	
	var t = transform("MAP", "Home_Contacts.map", asteriskCallerID.state.toString)
	caller = precallerid + " " + if(t == "") asteriskCallerID.state else t

	**_LR_Echo_TTS_**.sendCommand(caller)
	val CNAM = caller.toString.split(',').get(1)
	logInfo("Asterisk-Caller-ID","  Caller CNAM: " + CNAM.toString )	
	postUpdate(asteriskCallerIDCNAM,CNAM)
	}
end

Contacts are added in a map file after a great suggestion from @rikoshak, an example is shown below.

transform/Home_Contact.map

021XXXXXXX=Sue Mobile
027XXXXXXX=Paul Mobile

Make sure to add the contacts you want as mappings.
Make sure to change the Echo binding TTS command item to suit your environment.
Next, do some testing. ?Using the REST UI populate the asteriskCallerID parameter with a phone number.
Then using your Basic UI enable the switch ‘asteriskCall’ You should hear your echo device announce the incoming call.

Once the above is working its time to make asterisk populate the caller ID for incoming calls and also switch on the flag to say its an incoming call.

In my rather old v13 FreePBX asterisk install I went to the following conf files and added the following entries.

/etc/asterisk/extensions_override_freepbx.conf

[from-trunk]
exten => 0123456789,n,Set(IncCall=${SHELL(curl -X POST --header "Content-Type: text/plain" --header "Accept: application/json" -d "ON" "http://<OPENHAB>:8080/rest/items/asteriskCall" && echo -n 1 || echo -n 0)});
exten => 0123456789,n,NoOp(IncCall is ${IncCall});

Replace 0123456789 with the number people dial to reach you.
And replace < OPENHAB > with your OH2 IP address. also change the port if you need to
The first rule in the conf file does all the heavy lifting the second is just a result confirmation that will be visible in the asterisk console if you need it to test. a result of 1 is successful and a 0 is a failure.

Now you can do some more testing when you call your home number, asterisk should populate the Asterisk Caller ID information in your sitemap with the correct caller identity of the phone ringing you.

When that works fine we are on to the last step we want asterisk to switch on the incoming call switch so that the OH rule condition will match and the announcement will be spoken.
To do this, on your asterisk server you edit the same file and copy your ‘’ context from the extensions_additional.conf file. we then need to choose carefully where to add the following two lines,

exten => s,n,Set(InCallNUMResp=${SHELL(curl -X POST --header "Content-Type: text/plain" --header "Accept: application/json" --data-urlencode ${CALLERID(num)} "http://<OPENHAB>:8080/rest/items/asteriskCallerID" && echo -n 1 || echo -n 0)});
exten => s,n,NoOp(InCallNUMResp is ${InCallNUMResp});

In my version i decided to insert it in the macro-user-callerid context, I did this by copying the macro in its entirity into the override file and inserting the lines I wanted.
beelow is my example, yours may vary.

[macro-user-callerid]
exten => lang-playback,1,GosubIf($[${DIALPLAN_EXISTS(macro-user-callerid,${CHANNEL(language)})}]?macro-user-callerid,${CHANNEL(language)},${ARG1}():macro-user-callerid,en,${ARG1}())
exten => lang-playback,n,Return()
exten => s,1,Noop(override - Running through macro-user-callerid)
exten => s,n,Set(TOUCH_MONITOR=${UNIQUEID})
exten => s,n,Set(AMPUSER=${IF($["${AMPUSER}" = ""]?${CALLERID(number)}:${AMPUSER})})

exten => s,n,Noop(BEGIN - trigger openhab integration)
exten => s,n,Set(InCallNUMResp=${SH
ELL(curl -X POST --header "Content-Type: text/plain" --header "Accept: application/json" --data-urlencode ${CALLERID(num)} "http://<OPENHAB>:8080/rest/items/asteriskCallerID" && echo -n 1 || echo -n 0)});
exten => s,n,NoOp(InCallNUMResp is ${InCallNUMResp});
exten => s,n,Noop(END - Openhab integration)

exten => s,n,GotoIf($["${CUT(CHANNEL,@,2):5:5}"="queue" | ${LEN(${AMPUSERCIDNAME})}]?report)
exten => s,n,ExecIf($["${REALCALLERIDNUM:1:2}" = ""]?Set(REALCALLERIDNUM=${CALLERID(number)}))
exten => s,n,Set(AMPUSER=${DB(DEVICE/${REALCALLERIDNUM}/user)})
exten => s,n,GotoIf($["${AMPUSER}" = "none"]?limit)
exten => s,n,Set(AMPUSERCIDNAME=${DB(AMPUSER/${AMPUSER}/cidname)})
exten => s,n,ExecIf($["${ARG2}" != "EXTERNAL" & ${DB_EXISTS(AMPUSER/${AMPUSER}/cidnum)} & "${AMPUSER}" != "${DB(AMPUSER/${AMPUSER}/cidnum)}"]?Set(__CIDMASQUERADING=TRUE))
exten => s,n,GotoIf($["${AMPUSERCIDNAME:1:2}" = ""]?report)
exten => s,n,Set(AMPUSERCID=${IF($["${ARG2}" != "EXTERNAL" & "${DB_EXISTS(AMPUSER/${AMPUSER}/cidnum)}" = "1"]?${DB_RESULT}:${AMPUSER})})
exten => s,n,Set(__DIAL_OPTIONS=${IF($["${DB_EXISTS(AMPUSER/${AMPUSER}/dialopts)}" = "1"]?${DB_RESULT}:${DIAL_OPTIONS})})
exten => s,n,Set(CALLERID(all)="${AMPUSERCIDNAME}" <${AMPUSERCID}>)

exten => s,n,GotoIf($["${ARG1}"="LIMIT" & ${LEN(${AMPUSER})} & ${DB_EXISTS(AMPUSER/${AMPUSER}/concurrency_limit)} & ${DB(AMPUSER/${AMPUSER}/concurrency_limit)}>0 & ${GROUP_COUNT(${AMPUSER}@concurrency_limit)}>=${DB(AMPUSER/${AMPUSER}/concurrency_limit)}]?limit)
exten => s,n,ExecIf($["${ARG1}"="LIMIT" & ${LEN(${AMPUSER})}]?Set(GROUP(concurrency_limit)=${AMPUSER}))
exten => s,n,ExecIf($["${DB(AMPUSER/${AMPUSER}/language)}" != ""]?Set(CHANNEL(language)=${DB(AMPUSER/${AMPUSER}/language)}))
exten => s,n(report),Noop(Macro Depth is ${MACRO_DEPTH})
exten => s,n,GotoIf($["${MACRO_DEPTH}" = "" | ${MACRO_DEPTH} < 6 ]?report2:macroerror)
exten => s,n(report2),GotoIf($[ "${ARG1}" = "SKIPTTL" | "${ARG1}" = "LIMIT" ]?continue)
exten => s,n,ExecIf($["${CALLEE_ACCOUNCODE}" = ""]?Set(__CALLEE_ACCOUNCODE=${DB(AMPUSER/${IF($["${MACRO_CONTEXT}"="macro-exten-vm"]?${ARG2}:${MACRO_EXTEN})}/accountcode)}))
exten => s,n(report3),Set(__TTL=${IF($["foo${TTL}" = "foo"]?64:$[ ${TTL} - 1 ])})
exten => s,n,GotoIf($[ ${TTL} > 0 ]?continue)
exten => s,n,Wait(${RINGTIMER})
exten => s,n,Answer
exten => s,n,Wait(1)
exten => s,n,Gosub(macro-user-callerid,lang-playback,1(hook_0))
exten => s,n,Macro(hangupcall,)
exten => s,n(macroerror),Noop(Macro Limit Reached. Aborting Call)
exten => s,n,Answer
exten => s,n,Wait(1)
exten => s,n,Gosub(macro-user-callerid,lang-playback,1(hook_2))
exten => s,n,Macro(hangupcall,)
exten => s,n(limit),Answer
exten => s,n,Wait(1)
exten => s,n,Gosub(macro-user-callerid,lang-playback,1(hook_1))
exten => s,n,Macro(hangupcall,)
exten => s,n,Congestion(20)
exten => s,n(continue),Set(CALLERID(number)=${CALLERID(number):0:40})
exten => s,n,Set(CALLERID(name)=${CALLERID(name):0:40})
exten => s,n,GotoIf($["${CALLERID(name)}" = ""]?cnum)
exten => s,n,Set(CDR(cnam)=${CALLERID(name)})
exten => s,n(cnum),Set(CDR(cnum)=${CALLERID(num)})
exten => s,n,Set(CHANNEL(language)=${MASTER_CHANNEL(CHANNEL(language))})

exten => h,1,Macro(hangupcall,)

exten => en,1(hook_0),Playback(im-sorry&an-error-has-occurred&with&call-forwarding)
exten => en,n,Return()
exten => en,n(hook_1),Playback(beep&im-sorry&your&simul-call-limit-reached&goodbye)
exten => en,n,Return()
exten => en,n(hook_2),Playback(im-sorry&an-error-has-occurred)
exten => en,n,Return()

exten => ja,1(hook_0),Playback(im-sorry&call-forwarding&jp-no&an-error-has-occured)
exten => ja,n,Return()
exten => ja,n(hook_1),Playback(beep&im-sorry&simul-call-limit-reached)
exten => ja,n,Return()
exten => ja,n(hook_2),Playback(im-sorry&an-error-has-occured)
exten => ja,n,Return()
;End of macro-user-callerid-custom

you will see the two Openhab integration lines in the second block of code.

Anytime you edit the asterisk conf files make sure you run ‘dialplan reload’ at the console CLI so that the changes take effect.
if you have any issues simply delete the stuff out of the override file that you put in and reload the dialplan and it all should return to the point prior to you modifying it.

[EDIT] I have now updated to include the mapping suggestion from Rich.

Good luck

Paul

1 Like

Fantastic tutorial! Thanks for posting!

I do have a question and a comment.

What are the | in your Item definitions? A copy and paste artifact or do these actually work?

There can be a way to make this work generically I think using a map file. I just tested it and if the String passed into the map file doesn’t exist there is a warning in the logs and it returns blank.

So, if you add the numbers to a map with Sue Mobile and Paul Mobile mapped to the appropriate numbers then you can replace the switch statement with

    var t = transform("MAP", "incoming_call.map", asteriskCallerId.state.toString)
    caller = precallerid + " " + if(t == "") asteriskCallerID.state else t

Then you can update, add, and remove mappings by just editing the map file instead of the Rule and running the risk of breaking something in the rule in the process.

Thanks Rich great suggestion for using the mapping ability for contacts, I will make the change on my installationand when I have tested I will modify the post to reflect your idea.

As for the items, I have no idea why the pipe symbols appeared but yeah I am guessing its some copy n paste anomaly. I have corrected.

Regards

Paul

1 Like

Not completely… :wink:

Could you perhaps assist me in locating the item piece(s) that still require correcting?

Thanks

The item definitions in the first post still have some | in them

I tried repasting the item definitions again and the same thing happened, I have not had that happen before. I use notepad++ as my local editor and I guess the erroneous characters are tabs and CR/LF’s and the like.

Anyway, I enlisted the assistance of a sighted friend so hopefully the unwanted characters are finally removed manually. That is not to say that there are not invisible characters still present so for that reason, it may pay for anyone trying this out to type it in if they meet problems.

1 Like