Lightweight Home Security with Indigo and Asterisk

I mentioned last week that I’ve been working on building sort of a lightweight home security system for my house so my 4-year-old daughter won’t be able to sneak out of the house again.

I spent about a day researching possible solutions, to see if there was something simple that I could buy that would make me happy, and I couldn’t find anything on the market that was cheap and would tell me which of my 6 exterior doors had been opened loudly enough to hear from across the house. There’s no point in putting a door buzzer on the basement door if you can’t hear it from the master bedroom. The only solutions that I found were from professional alarm companies, and they probably would have charged me a couple thousand for installation plus $30-$50 per month for monitoring. I’m just not willing to pay that much, and it’s not really what I was looking for–it’s gross overkill for my problem.

So, I decided to build it myself. After a few hours’ searching, I decided to use Perceptive Automation’s Indigo home-automation software for the Mac. It’s commercial software, but I like its user interface and capabilities better then any of the open-source solutions that I’ve seen. It’s under active development, supports just about everything that I need, it’s client-server so I can access it from any Mac I own, and it comes with a nice web interface that I can get to via my phone. I figured that it’d be cheaper to pay the money for Indigo then to spend most of a week hacking away at one of the open-source packages to get them to do what I want. Plus, er, pretty much every open source home automation program that I could find was written in Perl, and I’ve been successfully avoiding Perl for almost 5 years now. The last thing I really want to do is spend a week modifying someone else’s Perl. That kind of thing gives me nightmares.

So I fired Indigo up on an old Powerbook that I had laying around and bought a bunch of hardware from MacHomeStore and SmartHome.com. The important bits are a W800RF32A wireless reciever and a whole bunch of $12 DS10a door and window sensors. The sensors broadcast their state over the air, and the receiver feeds them into my Mac.

That took care of the input side of the equation, but I still needed some relatively cheap way to play audio around my house. I poked around for a while looking at random X10 hardware and radio solutions before I realized that most VoIP phones support audio paging. Since my house is full of VoIP phones and I run my own Asterisk server, all I’d really need to do was write a couple dozen lines of Asterisk dialplan code, and everything should just work. I ended up ordering two new VoIP phones (Grandstream GXP-2020s) because two of the phones that I have are too old to be usable for paging. I probably could have used $45 Budgetones, but I have future plans for the GXP-2020’s big displays, so I decided to spend a bit more for them.

It took me about 30 minutes to unpack everything, install Indigo, and have it receiving data from a test sensor. Each sensor assigns itself a random 8-bit ID when it’s powered up, so the first problem was mapping semi-random sensor IDs onto logical names. Indigo comes with a blob of sample AppleScript for doing this, and it only took me 5 minutes to modify it so that one of Indigo’s internal variables changed state to reflect the state of the door sensor–true when the door is closed and false when opened. Five more minutes and I had a nifty web page with a green blob that turned red when the door opened. A half-hour after that, I had a PNG floorplan of my house that I could use as a backdrop for a bunch of little red/green blobs:

After this, it’s all just a matter of plumbing. First, I modified Indigo’s sensor-handling AppleScript example into something that knows how to talk to Asterisk, using one of the Asterisk/AppleScript integration examples on the voip-info.org Wiki. Here’s I ended up with:

(*
  Door sensor tracking code for Indigo and Asterisk.
  By Scott Laird <scott@sigkill.org>
  http://scottstuff.net

  This code handles incoming security events, maps each numeric
  device ID onto a logical name, and then tells Asterisk to page all
  VoIP phones with a device-specific message.

  Net effect: opening the front door causes "Front door opened" to echo
  throughout the house.

  I'm calling doDialOut directly rather then using an Indigo trigger for 4
  reasons:
    1.  I have the extension name handy here, while I'd have to parse it back out of the variable name if I used triggers.
    2.  Creating an identical trigger for each of 10+ sensors is a pain in the neck.
    3.  We need to do *something* with unknown sensor events, but creating variables for them is pretty clearly wrong.
    4.  My AppleScript is lousy, and I can't get triggers to call doDialOut correctly.
*)

using terms from application "IndigoServer"
  on receive security event of eventType with code devID
    set extension to "unknown"

    -- Map sensor IDs onto logical names.  In a real language,
    -- I'd use some sort of hash and skip the if ... else if ... code,
    -- but I don't see anything suitable in AppleScript.  Sigh.  I'm
    -- stuck writing code in blub.
    if devID is 95 then
      set extension to "frontdoor"
    else if devID is 175 then
      set extension to "deckdoor"
    else if devID is 99 then
      set extension to "stairs"
    else if devID is 201 then
      set extension to "backyard"
    else if devID is 55 then
      set extension to "upgarage"
    else if devID is 155 then
      set extension to "downgarage"
    else if devID is 253 then
      set extension to "slider"
    end if

    -- If we get a request for an unknown devID, then send it on to Asterisk 
    -- so we get an audible indication that *something* happened.  Since batteries
    -- falling out of DS10 modules can cause the ID to change, I'd rather not ignore
    -- these.  YMMV, however.
    if extension is "unknown" then
      my doDialOut(devID)
    else
      -- Indigo variable names are derived from Asterisk extension names.

      -- In retrospect, doorClosed_ is a great name for doors, but not so hot
      -- for doorbells or motion sensors.  Feel free to change this.
      set var to ("doorClosed_" & extension)
      set seen_var to ("lastSeen_" & extension)
      set change_var to ("lastChanged_" & extension)
      set timestamp to (current date) as string

      -- Figure out if it opened or closed.  There are actually 4 different
      -- results that the sensors can return (normal/active X min/max),
      -- but we only care about normal/active.
      if eventType is sec_SensorNormal_min then
        set val to "true"
      else if eventType is sec_SensorNormal_max then
        set val to "true"
      else
        set val to "false"
      end if

      -- Create the Indigo variables if they don't already exist.
      if not (variable var exists) then
        make new variable with properties {name:var, value:val}
      end if

      if not (variable seen_var exists) then
        make new variable with properties {name:seen_var, value:timestamp}
      end if

      if not (variable change_var exists) then
        make new variable with properties {name:change_var, value:timestamp}
      end if

      -- Did the value of this variable just change?
      if not (value of variable var is val) then
        set value of variable change_var to timestamp

        -- If it just changed and it's now false, then send an alert.
        -- The DS10a sensors send a signal once per hour, even if nothing's changed.
        -- So we don't want to alert *unless* something's changed.
        if val is "false" then
          my doDialOut(extension)
        end if
      end if

      -- We need to keep track of the time that each DS10 was last seen.
      -- This way we can spot bad batteries.
      set value of variable seen_var to timestamp
      set value of variable var to val
    end if
  end receive security event

  -- doDialOut tells Asterisk to dial a specific extension using
  -- Asterisk's management interface.  This doesn't require that Asterisk
  -- runs on the same machine as Indigo.
  --
  -- To get this to work in your environment, you'll need to change the IP
  -- address, username, and password, and possibly the contexts and/or 
  -- extension names used at the bottom.
  on doDialOut(extension)
    log "Paging extension " & extension using type "Dialer"
    set expectscript to "set timeout 20;
spawn telnet 10.0.0.1 5038;
expect \"Asterisk Call Manager/1.0\";
send \"Action: login
username: my_username
secret: secretsecret

\";
expect \"Message: Authentication accepted\";

send \"Action: originate
Exten: " & extension & "
Context: security-paging
Channel: Local/all@paging
Priority: 1
Callerid: Security: " & extension & " <0>

\";

sleep 1;

send \"Action: logoff

\";
"
    set results to do shell script "/usr/bin/expect -c  '" & expectscript & "'"
  end doDialOut
end using terms from

In addition to simply monitoring the current state of the sensors, this also tracks their last change as well as the last time each sensor sent out a “nothing’s changed” report. Eventually I’ll add monitoring for this so I can spot failing batteries immediately, and not two months later when I discover that two doors aren’t monitored anymore. Indigo tracks each variable internally, and gives you a handy window for viewing and modifying them:

Once all of that was in place, it was time to add paging logic to Asterisk. I created two new contexts, security-paging to contain the messages that need to be played back and paging to handle the phones. The AppleScript above basically glues the two ends together, sending ‘deckdoor@security-paging’ to ‘all@paging’. Here’s the relevant bit of my Asterisk config:

[security-paging]
  exten => frontdoor,1,Playback(security/frontdoor)
  exten => deckdoor,1,Playback(security/deckdoor)
  exten => backyard,1,Playback(security/backyard)
  exten => stairs,1,Playback(security/stairs)
  exten => upgarage,1,Playback(security/upgarage)
  exten => downgarage,1,Playback(security/downgarage)
  exten => slider,1,Playback(security/slider)

  exten => _.,1,SayNumber(${EXTEN})

[paging]
  exten => all,1,Page(Local/203@paging&Local/204@paging&Local/205@paging&Local/206@paging)

  ; Cisco 7940; no Call-Info support, but you can create a new line and turn on auto-answer on the phone.
  exten => 203,1,Dial(SIP/203aa)

  ; Sipura 841.  It needs a semicolon before answer-after.
  exten => 204,1,SipAddHeader(Call-Info: \;answer-after=0)
  exten => 204,n,Dial(SIP/204)

  ; Grandstream GXP-2020s, although the same config will work for most modern phones.
  exten => 205,1,SipAddHeader(Call-Info: answer-after=0)
  exten => 205,n,Dial(SIP/205)

  exten => 206,1,SipAddHeader(Call-Info: answer-after=0)
  exten => 206,n,Dial(SIP/206)

Then I just had to record some audio files to use for annoucements. The easiest way to do this is just to call yourself up and leave voicemail, and then copy the VM files over into Asterisk’s sound file directory, usually /var/lib/asterisk/sounds. You could get better quality recordings with a good microphone and audio-processing app, but I’m not sure that there’s really a point, given the quality of most speakerphone speakers.

All told, it took me about 8 hours of research to put this all together, and maybe 10 hours to implement it all, including learning a bit of AppleScript. Now I have a programmable system for monitoring my house, and all of the pieces in place for adding X10 or Insteon components as they make sense. It’s all under my control; if I can code it, then I can make it happen.

So, does it all work? Yep–Monday morning it caught my daughter trying to sneak into the garage. Mission accomplished.

Posted by Scott Laird Wed, 15 Aug 2007 16:16:00 GMT


Really, really dumb Nokia E61 SIP bug

One of the things I really like about my Nokia E61 is that it’s not just a cell phone, it’s also a VoIP phone. It includes a SIP client that works with Asterisk, mostly, so I’m able to get by with just one phone at home–incoming calls headed to either my cell number *or* my home number ring to the same phone. Which is great.

Except it all stopped working when I upgraded to the latest E61 firmware a couple months ago. I re-created my SIP settings, but the phone just wouldn’t register with Asterisk. Deleting and re-creating the settings didn’t make much of a difference, and eventually I decided to put off debugging it and wait until I had more time.

So, this morning I spent about an hour and a half trying to fix the phone. I re-configured it using several different configs that I found on line, and none of them worked. They all just generated “Registration Failed” messages on the phone. So I turned on SIP debugging in Asterisk, but it wasn’t very helpful–it didn’t show the phone trying to register at all. So I fired up tcpdump and discovered that the phone wasn’t actually sending any SIP requests at all–it was failing locally without ever talking to the network at all.

So I did a bit more digging, and found a Nokia forum comment that suggests that the upgrade from firmware 2.x to 3.x corrupts the SIP settings, and simply deleting SIP configurations won’t fix it. The poster recommended deleting SIP connections before upgrading, and then re-creating them after.

That isn’t an option for me anymore–I can’t downgrade the phone back to 2.x–but a simple backup/restore cycle (using the built-in backup to memory card option) fixed everything. It took about 10 minutes, but my E61 is now registering with Asterisk again.

Posted by Scott Laird Tue, 07 Aug 2007 16:45:00 GMT


Electronic Home Improvement

It’s been a big year for home improvements–I’ve painted two rooms, replaced the carpet in both, added a new light fixture, torn off a deck, and replaced a fence. Somehow, after all of that, I still feel behind–I need to re-do my back yard and add a new patio before The Dark and Cold sets in for the year.

Before that happens, though, I’m planning on spending some time updating the electronic side of the house. My home asterisk server is getting long in the tooth, and lately it’s been bouncing incoming phone calls. My config files are almost 3.5 years old, and the old thing’s getting kind of crufty, plus NuFone seems to be going through one of their annual crisises.

On top of that, my wife’s perpetually worried about our 4-year-old sneaking out of the house without us noticing–we’ve already caught her playing in the middle of the cul-de-sac once, and since she has no real fear of cars (or strangers, or getting lost), we’d like to add some sort of warning system to know when one of the exterior doors have been opened.

You know what they say–when all you have is a big pile of computer equipment and a credit card, then everything looks like an excuse to buy more hardware and write some code…

So, I have a small mountain of X10 and Insteon hardware due in later this week, and I’m going to set up door monitoring via a bunch of cheap RF door sensors. I’m planning on monitoring everything via Indigo running on an old Mac, and then announcing open doors via auto-answer on VoIP phones using Asterisk. Sounds easy enough, right? I can’t see any indication that anyone has ever done any Indigo-Asterisk integration work, but it doesn’t look all that hard. Along the way, I guess I’m going to have to completely re-write my Asterisk dial plan, because it’s really too crufty to live.

While I’m at it, I’m planning on adding a couple “smart” light switches, just to cut down on the number of basement and garage lights that get left on for days at a stretch. I figure they’ll pay for themselves, eventually…

Hopefully all of this’ll give me something to talk about at MindCamp this year.

Posted by Scott Laird Mon, 06 Aug 2007 17:11:00 GMT


Asterisk upgrades

I upgraded my home Asterisk server from 1.2.x to 1.4.0-beta3 today. Unfortunately, I’d ignored a bunch of deprecation warnings when I upgraded from Asterisk 1.0 to 1.2, and it looks like 1.4 removed almost everything that was deprecated in 1.2. So I spent most of an hour replacing DBget(var=some/key) with Set(var=DB(some/key)). I guess that’s an improvement, although neither form is exactly elegant.

The single biggest improvement that I’ve seen so far is Jabber support. Now, every time the phone rings, my Gmail account gets an instant message with the caller ID information.

My Asterisk config is now around 2 years old and getting kind of crufty. Sooner or later I’m going to need to rewrite it in a better language; either AEL or one of the various Ruby plugins for Asterisk. AEL looks like a big improvement over Asterisk’s traditional config language, but it’s still not a real programming language.

Posted by Scott Laird Mon, 06 Nov 2006 05:28:31 GMT


Asterisk 1.2

It looks like Asterisk 1.2 is out. From the release announcement:

This release of Asterisk contains over 3,000 improvements on version 1.0, including hundreds of new features and applications.

I’m planning on upgrading my home Asterisk server soon, possibly today, and then I’ll look at migrating pieces of my dial plan to RAGI and/or AEL.

Posted by Scott Laird Thu, 17 Nov 2005 14:08:49 GMT


Asterisk extension language

While I really like all of the things that Asterisk allows me to do with my phone system, I’m really not very fond of its configuration language. The language provided in Asterisk 1.0 is slightly better then sendmail.cf, but it’s still a lousy language. They made a few small improvements early in the Asterisk 1.2 development process that helped a bit, but I assumed that we’d have to wait for another year or two before someone broke down and wrote a decent language for Asterisk.

It looks like I may have been overly pessimestic. I discovered the Asterisk extension language on a list of new features for Asterisk 1.2 today. Somehow I’d missed this when it first went into Asterisk.

Here’s a chunk out of my old config file. It’s not perfect, but it’s what you get when you’re stuck dealing with line numbers:

[macro-diallocal]
  exten => s,1,AbsoluteTimeout(7200)
  exten => s,n,SetAMAFlags(default)
  exten => s,n(analog),Dial(${TRUNK}/${ARG1})
  exten => s,n,Congestion
  exten => s,analog+101,Macro(condsetcid)
  exten => s,n,SetCIDName(LAIRD SCOTT)
  exten => s,n,SetAMAFlags(billing)
  exten => s,n(nufone),Dial(${NUFONE}/1${ARG1})
  exten => s,n,Congestion
  exten => s,nufone+101,Busy

And here’s the equivalent using the new configuration language:

macro diallocal( number ) {
  AbsoluteTimeout(7200);
  SetAMAFlags(default);
  Dial(${TRUNK}/${number});
  if(${DIALSTATUS} = "CONGESTION" || ${DIALSTATUS} = "CHANUNAVAILABLE") {
    &condsetcid();
    SetCIDName("LAIRD SCOTT");
    SetAMAFlags(billing);
    Dial(${NUFONE}/1${number});
    switch(${DIALSTATUS}) {
      case BUSY:
        Busy;
      default:
        Congestion;
    }
  }
}

It’s still not ideal–${VAR} is ugly–but it’s vastly better then the old syntax. This plus a bit of Rails integration should give us a really nice phone environment. I’m rapidly approaching the “tear it down and rebuild it” point with my Asterisk system–there’s a lot of stuff that I’d like to add that just doesn’t fit in with my current configuration files–so I’ll have a set of articles on integrating Asterisk, AEL, and Rails in a few weeks.

Posted by Scott Laird Mon, 10 Oct 2005 19:28:54 GMT


Rails and Asterisk

Oooh. Just when it looks like I’m going to have to stoop to programming in Perl to do a bit of Asterisk integration, someone comes out with a way to use Rails with Asterisk’s AGI scripting interface.

This has the potential to be really interesting, because Asterisk+Rails should be about a decade more advanced then anything that the IVR people are used to seeing.

I have a couple small Asterisk scripting jobs that I’ve promised to various family memebers; if my schedule allows, I’ll give RAGI a spin and share how it goes later this week.

Posted by Scott Laird Mon, 12 Sep 2005 14:11:20 GMT


Asterisk 1.2 is approaching

I’ve fallen a bit behind on Asterisk with all of the time that I’ve spent working on Typo lately, but I found this announcement sitting in my inbox today:

As previously mentioned on the lists by Olle Johannson, we are actively trying to get Asterisk in shape for a 1.2 release within the next 60 days.

I will produce the first release candidate on August 20th, with followup versions produced every week until we deem the release ready for public consumption. I expect it will require at least three -RC releases for us to get things in shape, so that means that 1.2 itself may be ready by September 15th.

Asterisk has desperately needed a new stable release for months now, and it looks like version 1.2 will be out within the next two months.

Posted by Scott Laird Tue, 26 Jul 2005 18:41:11 GMT


SIP/GSM MNVO/VoIP double-play?

I want to see a combination VoIP/MVNO double-play. That’s one company that sells cellular service (using someone else’s network), sells VoIP service, and integrates the two services.

There are two specific scenarios that seem obvious:

  1. They sell the customer mobile phone service and VoIP service via an ATA or SIP hardphone. This would be great for people who have turned off their home phone service while continuing to pay for some form of Internet access. One number would ring both devices, the first one to pick up wins. Outgoing calls would have the same caller ID from either device. Alternate products would be family plans with multiple phones, each with their own number, then a shared number that will ring all devices; and centrex plans for small businesses, where the company provides both VoIP desk phones and mobile phones.

  2. They sell the customer mobile phone service and act like a SIP client. This way the customer can integrate their mobile phones directly into their existing phone system. Ideally, the MVNO’s SIP gateway will register and unregister with the SIP PBX as the phone gains and loses service; this will let the PBX do the Right Thing with voicemail, and also enable a number of other services.

Personally, I’d love to buy services like this. I’d prefer GSM phones, simply because the most interesting phones are almost always GSM-only. Accoring to Cellular News, there are at least three GSM MVNOs in the US right now, using both Cingular and T-Mobile’s networks, so this is certainly possible.

On the hardware front, several companies seem to be providing GSM/SIP gateway equipment:

It’s possible that Earthlink will be rolling something like this out soon–they’re building a MVNO, and they share at least one board member with Bridgeport Networks. They seem to be concentrating more on EV-DO then anything GSM-related, though.

There seems to be a huge push in the cellular industry to integrate SIP into their networks, so something like this will be possible sooner or later. My current contract with Cingular is up in 6 months; it’d be nice if someone has something on the market that I can buy by then.

Posted by Scott Laird Fri, 08 Jul 2005 18:35:26 GMT


Updated Asterisk config files

I’ve had several people ask me for a more recent version of my Asterisk configuration files, and it’s been about a year since I last posted them, so here they are.

extensions.conf

Here’s extensions.conf. I’ve lightly redacted a few phone numbers and deleted a few really obvious bits of cruft, but this is essentially what my Asterisk server is running. There’s at least a year’s worth of cruft lurking in here, but it’s a real, live example:

[general]
  static=yes
  writeprotect=no

[globals]
  TRUNK=Zap/1                         ; FXO interface
  ANALOG=Zap/2                        ; FXS interface

  KITSIP=Sip/203
  BEDSIP=Sip/204
  BASESIP=Sip/210

  NUFONE=IAX2/username@NuFone
  EBC=IAX2/username@EBC
  PHONES=${ANALOG}&${KITSIP}&${BEDSIP}

; Fax receiving macro.  Handles rxfax and directing the fax to the 
; correct email address.
[macro-faxreceive]
  exten => s,1,SetVar(FAXFILE=/var/spool/asterisk-fax/${UNIQUEID}.tif)
  exten => s,2,DBGet(EMAILADDR=extensionemail/${MACRO_EXTEN})
  exten => s,3,rxfax(${FAXFILE})
  exten => s,103,SetVar(EMAILADDR=scott@sigkill.org)
  exten => s,104,Goto(3)

; This fixes up incoming CallerID.  NuFone doesn't transmit CID name
; info on incoming toll-free calls.  So, we cache the name when we see
; it and use the cached copy when we can.
; 
; This is kind of close to LookupCIDName, except we also *store* the
; CID Name when we know it.
[macro-cidfix]
  exten => s,1,GotoIf($["${CALLERIDNUM:0:2}" = "+1"]?600)
  exten => s,n(dbget),DBGet(cidfixname=cidname/${CALLERIDNUM})
  exten => s,n,SetCIDName(${cidfixname})
  exten => s,dbget+101,GotoIf($[${CALLERIDNAME} = "Toll-Free Call"]?500:104)
  exten => s,n,GotoIf($["X${CALLERIDNAME}" = "X"]?500:104)
  exten => s,n,DBput(cidname/${CALLERIDNUM}=${CALLERIDNAME})
  exten => s,500,SetCallerID("No Caller ID <Unknown>")
  exten => s,600,SetCIDNum($[${CALLERIDNUM:2:100}])
  exten => s,n,Goto(dbget)

; Conditionally set the CallerID.  If we already have a valid CallerID
; (basically, it's more then 4 digits long), then we leave it in place.
; Otherwise, replace it with our home phone number
[macro-condsetcid]
  exten => s,1,NoOp
  exten => s,2,GotoIf($[${CALLERIDNUM:0:4} = ${CALLERIDNUM}]?3:4)
  exten => s,3,SetCallerID(4254889014)
  exten => s,4,NoOp

; Do call forwarding out of a database.  Look up the caller ID number, 
; and if it exists in the forward DB, then forward the call to the 
; specified number.
[macro-dbcallforward]
  exten => s,1,DBGet(FORWARDTO=forward/${CALLERIDNUM})
  exten => s,2,Macro(condsetcid)
  exten => s,3,Wait(1)
  exten => s,4,Dial(Local/${FORWARDTO}@inside/n)
  exten => s,102,Noop

; Outside line macro.  This handles incoming phone calls from outside
; of the house, including caller-ID rewriting, blacklisting, and
; distinctive ring.
[macro-outsideline]
  exten => s,1,AbsoluteTimeout(7200)
  exten => s,2,SetCDRUserField(Outside (${CHANNEL}))
  exten => s,3,Macro(cidfix)
  exten => s,4,Macro(dbcallforward)
  exten => s,5,Macro(setalertinfo)
  exten => s,6,LookupBlacklist
  exten => s,7,Dial(${PHONES},30,Ttr)
  exten => s,8,Answer
  exten => s,9,Goto(outside-ivr,s,1)
  exten => s,107,Goto(208)
  exten => s,108,Goto(8)
  exten => s,207,Wait(3600)
  exten => s,208,Hangup

; Extension dialing logic.  Call an internal extension and bounce to VM
; if no answer.
[macro-callextension]
  exten => s,1,Dial(${ARG1},30,t)
  exten => s,2,Voicemail(${ARG2})
  exten => s,3,Hangup
  exten => s,102,Voicemail(${ARG2})
  exten => s,103,Hangup

; The next block of macros handle outgoing calls.  This is more complex
; then you'd expect for a couple reasons.  First, I'm using two different
; outgoing mechanisms--NuFone and my POTS line, but I'd like either one
; to be able to work as a fallback if the other fails.
;
; Next, I want to be able to handle 7- and 10-digit dialing without
; having to remember which area codes and prefixes require a '1' first.
; I'm in the Seattle area, and part of 425 is free, along with 95% of
; 206.  With POTS, I must dial a '1' iff it is a toll call.  With
; NuFone I must always dial a 1.
;
; See http://scottstuff.net/scott/archives/000163.html
[macro-diallocal]
  exten => s,1,AbsoluteTimeout(7200)
  exten => s,n,SetAMAFlags(default)
  exten => s,n(analog),Dial(${TRUNK}/${ARG1})
  exten => s,n,Congestion
  exten => s,analog+101,Macro(condsetcid)
  exten => s,n,SetCIDName(LAIRD SCOTT)
  exten => s,n,SetAMAFlags(billing)
  exten => s,n(nufone),Dial(${NUFONE}/1${ARG1})
  exten => s,n,Congestion
  exten => s,nufone+101,Busy

; This handles 10-digit local numbers
[macro-dialanalogwithfallback]
  exten => s,1,Macro(diallocal,${MACRO_EXTEN})

; This handles 7-digit local numbers
[macro-dialanalogwithfallback425]
  exten => s,1,Macro(diallocal,425${MACRO_EXTEN})

; This handles 10-digit non-local numbers
[macro-dialanalogwithfallback1]
  exten => s,1,Dial(${TRUNK}/1${MACRO_EXTEN})
  exten => s,2,Congestion
  exten => s,102,Macro(condsetcid)
  exten => s,103,SetCIDName(LAIRD SCOTT)
  exten => s,104,Dial(${NUFONE}/1${MACRO_EXTEN})
  exten => s,105,Congestion
  exten => s,205,Busy

; This just dials via POTS without any special handling
[macro-dialanalog]
  exten => s,1,Dial(${TRUNK}/${MACRO_EXTEN})
  exten => s,2,Congestion
  exten => s,102,Busy

; ditto, but for NuFone.
[macro-dialnufone]
  exten => s,1,Macro(condsetcid)
  exten => s,n,SetCIDName(LAIRD SCOTT)
  exten => s,n,SetAMAFlags(billing)
  exten => s,n,Dial(${NUFONE}/${MACRO_EXTEN})
  exten => s,n,Congestion

[macro-dialnufonewithfallback]
  exten => s,1,Macro(condsetcid)
  exten => s,n,SetCIDName(LAIRD SCOTT)
  exten => s,n,SetAMAFlags(billing)
  exten => s,n(nufone),Dial(${NUFONE}/${MACRO_EXTEN})
  exten => s,n,Congestion
  exten => s,nufone+101,SetAMAFlags(default)
  exten => s,n(analog),Dial(${TRUNK}/${MACRO_EXTEN})
  exten => s,n,Macro(fastbusy)
  exten => s,analog+101,Busy

; for 10-digit dialing in non-local areas
[macro-dialnufonewithfallback1]
  exten => s,1,Macro(condsetcid)
  exten => s,n,SetCIDName(LAIRD SCOTT)
  exten => s,n,SetAMAFlags(billing)
  exten => s,n(nufone),Dial(${NUFONE}/1${MACRO_EXTEN})
  exten => s,n,Congestion
  exten => s,nufone+101,SetAMAFlags(default)
  exten => s,n(analog),Dial(${TRUNK}/1${MACRO_EXTEN})
  exten => s,n,Macro(fastbusy)
  exten => s,analog+101,Busy

; This sets the ALERT_INFO variable based on the CALLERIDNUM
; setting via a lookup in the 'distinctivering' database.  See
; http://scottstuff.net/scott/archives/000163.html
[macro-setalertinfo]
  exten => s,1,DBGet(ALERT_INFO=distinctivering/${CALLERIDNUM})

; This is the 'inside' context, where calls made from inside of the
; house start out.  The 'inside' context (or 'inside-sip'/'inside-analog')
; is referred to in sip.conf and zapata.conf.  That's what ties this all
; together.
[inside]
  include => parkedcalls
  include => operator
  include => e911
  include => forced-analog
  include => information
  include => local
  include => pstn-local10
  include => tollfree
  include => pstn-nonlocal10
  include => international

; There's no real reason for these to exist right now; I could
; just replace the references to them in sip.conf/zapata.conf
; with references to the 'inside' context.  This is kind of 
; historical.
[inside-sip]
  include => inside

[inside-analog]
  include => inside

[operator]
  exten => 0,1,Macro(dialanalog)

[e911]
  exten => 911,1,Macro(dialanalog)
  exten => 9911,1,Macro(dialanalog)

[forced-analog]
  ; Only allow 2, 3, 7, and 10-digit strings.  Without this, calls to area
  ; codes that start with a '9' (like my sister's cell phone in NYC) don't
  ; work.
  exten => _9XX,1,Macro(dialanalog)
  exten => _9XXX,1,Macro(dialanalog)
  exten => _9XXXXXXX,1,Macro(dialanalog)
  exten => _9XXXXXXXXXX,1,Macro(dialanalog)

[information]
  exten => 411,1,Macro(dialanalog)

; The 'local-extensions.conf' file is autogenerated.  See
; http://scottstuff.net/scott/archives/000163.html and
; http://svn.scottstuff.net/project/asterisk-lca-map/
[pstn-local10]
#include "local-extensions.conf"

[pstn-nonlocal10]
  exten => _NXXNXXXXXX,1,Macro(dialnufonewithfallback1)
  exten => _1NXXNXXXXXX,1,Macro(dialnufonewithfallback)

[tollfree]
  exten => _1888NXXXXXX,1,Macro(dialanalogwithfallback)
  exten => _1877NXXXXXX,1,Macro(dialanalogwithfallback)
  exten => _1866NXXXXXX,1,Macro(dialanalogwithfallback)
  exten => _1855NXXXXXX,1,Macro(dialanalogwithfallback)
  exten => _1800NXXXXXX,1,Macro(dialanalogwithfallback)

  exten => _888NXXXXXX,1,Macro(dialanalogwithfallback1)
  exten => _877NXXXXXX,1,Macro(dialanalogwithfallback1)
  exten => _866NXXXXXX,1,Macro(dialanalogwithfallback1)
  exten => _855NXXXXXX,1,Macro(dialanalogwithfallback1)
  exten => _800NXXXXXX,1,Macro(dialanalogwithfallback1)

[international]
  exten => _011.,1,Macro(dialnufone)

[local]
  exten => 200,1,Macro(callextension,${PHONES},200)
  exten => 201,1,Macro(callextension,${ANALOG},200)
  exten => 202,1,Macro(callextension,${BEDSIP},200)
  exten => 203,1,Macro(callextension,${KITSIP},200)
  exten => 204,1,Macro(callextension,${BEDSIP},200)

  exten => 210,1,Macro(callextension,${BASESIP},210)

  exten => 230,1,Answer
  exten => 230,2,Wait(5)
  exten => 230,3,Goto(fax,2201,1)

  exten => 221,1,Answer
  exten => 221,2,Goto(outside-ivr,s,1)
  exten => 221,3,Beep
  exten => 221,4,Hangup

  exten => 222,1,Wait,2
  exten => 222,2,VoicemailMain(s200)
  exten => 222,3,Hangup

  exten => 211,1,Wait(2)
  exten => 211,n,VoicemailMain(s210)

  exten => 223,1,Wait,2
  exten => 223,2,VoicemailMain
  exten => 223,3,Hangup

[outside-ivr]
  ; This is the outside IVR
  ; Playback a "We're not home message"
  ; To leave a message for Scott, press 1
  ; To leave a message for C, press 2
  ; Otherwise stay on the line.
  ;
  ; Also, 3 => main voicemail
  ;  4 => check voicemail (main)
  ;  5 => check voicemail
  ;
  ; Check for fax, too

  exten => s,1,AppendCDRUserField( -> IVR)
  exten => s,2,DigitTimeout(5)
  exten => s,3,ResponseTimeout(2)
  exten => s,4,Wait(1)
  exten => s,5,Background(laird/ivr-greeting)

  exten => 1,1,AppendCDRUserField( -> 1)
  exten => 1,2,Wait(1)
  exten => 1,3,VoiceMail(us201)
  exten => 1,4,Hangup

  exten => 2,1,AppendCDRUserField( -> 2)
  exten => 2,2,Wait(1)
  exten => 2,3,VoiceMail(us202)
  exten => 2,4,Hangup

  exten => 3,1,AppendCDRUserField( -> 3)
  exten => 3,2,Wait(1)
  exten => 3,3,VoiceMail(us200)
  exten => 3,4,Hangup

  exten => 4,1,AppendCDRUserField( -> 4)
  exten => 4,2,Wait(1)
  exten => 4,3,VoiceMailMain(200)
  exten => 4,4,Hangup

  exten => 5,1,AppendCDRUserField( -> 5)
  exten => 5,2,Wait(1)
  exten => 5,3,VoiceMailMain
  exten => 5,4,Hangup

  exten => fax,1,AppendCDRUserField( -> fax)
  exten => fax,2,Answer
  exten => fax,3,Goto(fax,2201,1)

  exten => t,1,AppendCDRUserField( -> timeout)
  exten => t,2,VoiceMail(s200)
  exten => t,3,Hangup

  exten => i,1,AppendCDRUserField( -> invalid)
  exten => i,2,Playback(tt-allbusy)
  exten => i,3,Goto(s,3)

[fax]
  exten => fax,1,Macro(faxreceive)
  exten => 2201,1,Macro(faxreceive)
  exten => 2202,1,Macro(faxreceive)
  exten => 2203,1,Macro(faxreceive)

  exten => h,1,system(/usr/local/sbin/mailfax ${FAXFILE} ${EMAILADDR} "${CALLERIDNUM} ${CALLERIDNAME}")

; This is where outside calls enter the system, thanks to 
; zapata.conf, sip.conf, and iax2.conf.  By and large, calls are
; directed to the 'outsideline' macro, *except* for the second 
; 866 number; that's just for checking voicemail.
[outside]
  exten => s,1,GotoIf($[X${CALLERIDNAME} = X]?200:2)
  exten => s,2,Macro(outsideline)
  exten => s,3,Hangup
  exten => s,200,Wait(30)
  exten => s,201,Hangup
  exten => 4254889999,Macro(outsideline)
  exten => 4254889999,2,Hangup
  exten => 8662299999,1,Macro(outsideline)
  exten => 8662299999,2,Hangup
  exten => 2068389999,1,Macro(outsideline)
  exten => 2068389999,n,Hangup
  exten => 8003949999,1,Macro(cidfix)
  exten => 8003949999,n,Goto(outside-ivr,s,1)
  exten => 8003949999,n,Hangup
  exten => 8888299999,1,SetAccount(pannell)
  exten => 8888299999,n,Macro(cidfix)
  exten => 8888299999,n,Dial(${BASESIP},30,t)
  exten => 8888299999,n,Voicemail(u210)
  exten => 4253549999,1,Macro(outsideline)
  exten => 4253549999,2,Hangup

  exten => 8662559999,1,VoicemailMain(200)
  exten => 8662559999,2,Hangup
  exten => 8662559999/2064849999,1,VoicemailMain(s200)
  exten => 8662559999/2064849999,2,Hangup
  exten => 8662559999/2069799999,1,VoicemailMain(s200)
  exten => 8662559999/2069799999,2,Hangup

  exten => scott,1,VoiceMail(us201)
  exten => scott,2,Hangup
  exten => cynthia,1,VoiceMail(us202)
  exten => cynthia,2,Hangup

  exten => fax,1,Goto(fax,2201,1)
  exten => t,1,Hangup
  exten => h,1,Hangup

[outside-analog]
  exten => s,1,GotoIf($[X${CALLERIDNAME} = X]?200:2)
  exten => s,2,Goto(outside,4254889999,1)
  exten => s,3,Hangup
  exten => s,200,Wait(30)
  exten => s,201,Hangup

[outside-sip]
  include => outside

[outside-nufone]
  include => outside

[outside-ebc]
  include => outside

[default]
  exten => s,n,Goto(outside,2068389999,1)
  exten => _x.,n,Goto(outside,2068389999,1)

sip.conf

Here’s sip.conf, again slightly redacted:

;
; SIP Configuration for Asterisk
;
[general]
port = 5060                   ; Port to bind to
bindaddr = 0.0.0.0    ; Address to bind to
context = outside-sip         ; Default for incoming calls
srvlookup = yes               ; Enable SRV lookups on outbound calls
;pedantic = yes               ; Enable slow, pedantic checking for Pingtel
tos=lowdelay
disallow=all                  ; Disallow all codecs
allow=gsm
allow=ilbc
allow=ulaw            ; Allow codecs in order of preference
insecure=very

[201]
type=friend
secret=zzzz
auth=md5
nat=yes
host=dynamic
reinvite=no
canreinvite=no
qualify=1000
dtmfmode=rfc2833
mailbox=200,201
callerid="Scott Laird" <201>
disallow=all
allow=gsm
allow=ulaw
allow=ilbc
context=inside-sip

[202]
type=friend
secret=zzzz
auth=md5
nat=yes
host=dynamic
reinvite=no
canreinvite=no
qualify=1000
dtmfmode=rfc2833
mailbox=200,202
callerid="Cynthia Laird" <202>
disallow=all
allow=gsm
allow=ulaw
allow=ilbc
context=inside-sip

[203]
type=friend
secret=zzzz
auth=md5
nat=yes
host=dynamic
reinvite=no
canreinvite=no
qualify=1000
dtmfmode=rfc2833
mailbox=200
callerid="Kitchen" <203>
disallow=all
allow=gsm
allow=ulaw
allow=ilbc
context=inside-sip

iax.conf

Finally, iax.conf:

; Inter-Asterisk eXchange driver definition
;
;
[general]
bandwidth=low
allow=gsm
disallow=ilbc
disallow=speex
disallow=ulaw
disallow=alaw
disallow=lpc10                ; Icky sound quality...  Mr. Roboto.
jitterbuffer=yes

register => laird:zzzz@switch-2.nufone.net
register => laird:zzzz@voip.teliax.com
authdebug=yes
tos=lowdelay

[guest]
type=user
context=outside
callerid="Guest IAX User"

[NuFone]
type=user
host=switch-2.nufone.net
secret=zzzz
context=outside-nufone
qualify=400
amaflags=billing

[NuFone]
type=peer
host=switch-1.nufone.net
secret=zzzz
qualify=400
amaflags=billing

[teliax]
context=outside
type=friend
host=voip.teliax.com
auth=md5
secret=zzzz
disallow=all
;allow=ilbc
allow=gsm
qualify=400

Posted by Scott Laird Thu, 07 Jul 2005 16:12:00 GMT