Tag Archives

youve got mail

SmartThings You’ve Got Mail through Sonos Speakers when Mailbox Opened

February 7, 2018 0 comments

So I had a few Samsung SmartThings Multipurpose Sensor’s laying around and decided to put one of them to good use.

I wanted my mailbox when it was opened to alert me that the mail was delivered and what better way then to use the AOL Sound “You’ve Got Mail”.  At first I did this just screwing around but then I realized how useful this actually became.  I never have to keep checking the mailbox waiting for mail to arrive.  I can continue on with my day waiting for the “You’ve Got Mail” sound to carry out through about 6 Sonos speakers.

Step 1.  First things first you need to login to your SmartThings IDE (link).

Step 2.  Make sure you click on “My Locations” then the Location name mine is “Home“.

Step 3.  Next we need to add a Custom SmartApp.  So click on “My Smartapps” at the top of your screen and then choose the green button “New Smart App“.

Step 4.  Then choose “From Code“.

Step 5.  Copy and Paste the entire code snippet below.  Then choose “Save” and then “Publish”.

/**
 *  Copyright 2015 SmartThings
 *
 *  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.
 *
 *  Sonos Custom Message
 *
 *  Author: SmartThings
 *  Date: 2014-1-29
 */
definition(
    name: "Sonos Notify Mailbox",
    namespace: "smartthings",
    author: "SmartThings",
    description: "Play a sound or custom message through your Sonos when the mode changes or other events occur.",
    category: "SmartThings Labs",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
)

preferences {
    page(name: "mainPage", title: "Play a message on your Sonos when something happens", install: true, uninstall: true)
    page(name: "chooseTrack", title: "Select a song or station")
    page(name: "timeIntervalInput", title: "Only during a certain time") {
        section {
            input "starting", "time", title: "Starting", required: false
            input "ending", "time", title: "Ending", required: false
        }
    }
}

def mainPage() {
    dynamicPage(name: "mainPage") {
        def anythingSet = anythingSet()
        if (anythingSet) {
            section("Play message when"){
                ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
                ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
                ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
                ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
                ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
                ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
                ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
                ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
                ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
                ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
                ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
                ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
                ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
            }
        }
        def hideable = anythingSet || app.installationState == "COMPLETE"
        def sectionTitle = anythingSet ? "Select additional triggers" : "Play message when..."

        section(sectionTitle, hideable: hideable, hidden: true){
            ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
            ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
            ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
            ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
            ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
            ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
            ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
            ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
            ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
            ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
            ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
            ifUnset "triggerModes", "mode", title: "System Changes Mode", description: "Select mode(s)", required: false, multiple: true
            ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
        }
        section{
            input "actionType", "enum", title: "Action?", required: true, defaultValue: "Custom Message", options: [
                "Custom Message",
                "Bell 1",
                "Bell 2",
                "Dogs Barking",
                "Fire Alarm",
                "The mail has arrived",
                "A door opened",
                "There is motion",
                "Smartthings detected a flood",
                "Smartthings detected smoke",
                "Someone is arriving",
                "Piano",
                "Lightsaber",
                "National Lampoon",
                "Leave it on the door",
                "You Got Mail",
                "Plain Doorbell",
                "Let It Go"]
            input "message","text",title:"Play this message", required:false, multiple: false
        }
        section {
            input "sonos", "capability.musicPlayer", title: "On this Sonos player", required: true, multiple: true
        }
        section("More options", hideable: true, hidden: true) {
            input "resumePlaying", "bool", title: "Resume currently playing music after notification", required: false, defaultValue: true
            href "chooseTrack", title: "Or play this music or radio station", description: song ? state.selectedSong?.station : "Tap to set", state: song ? "complete" : "incomplete"

            input "volume", "number", title: "Temporarily change volume", description: "0-100%", required: false
            input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
            href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
            input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
                options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
            if (settings.modes) {
                input "modes", "mode", title: "Only when mode is", multiple: true, required: false
            }
            input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
        }
        section([mobileOnly:true]) {
            label title: "Assign a name", required: false
            mode title: "Set for specific mode(s)", required: false
        }
    }
}

def chooseTrack() {
    dynamicPage(name: "chooseTrack") {
        section{
            input "song","enum",title:"Play this track", required:true, multiple: false, options: songOptions()
        }
    }
}

private songOptions() {

    // Make sure current selection is in the set

    def options = new LinkedHashSet()
    if (state.selectedSong?.station) {
        options << state.selectedSong.station
    }
    else if (state.selectedSong?.description) {
        // TODO - Remove eventually? 'description' for backward compatibility
        options << state.selectedSong.description
    }

    // Query for recent tracks
    def states = sonos.statesSince("trackData", new Date(0), [max:30])
    def dataMaps = states.collect{it.jsonValue}
    options.addAll(dataMaps.collect{it.station})

    log.trace "${options.size()} songs in list"
    options.take(20) as List
}

private saveSelectedSong() {
    try {
        def thisSong = song
        log.info "Looking for $thisSong"
        def songs = sonos.statesSince("trackData", new Date(0), [max:30]).collect{it.jsonValue}
        log.info "Searching ${songs.size()} records"

        def data = songs.find {s -> s.station == thisSong}
        log.info "Found ${data?.station}"
        if (data) {
            state.selectedSong = data
            log.debug "Selected song = $state.selectedSong"
        }
        else if (song == state.selectedSong?.station) {
            log.debug "Selected existing entry '$song', which is no longer in the last 20 list"
        }
        else {
            log.warn "Selected song '$song' not found"
        }
    }
    catch (Throwable t) {
        log.error t
    }
}

private anythingSet() {
    for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes","timeOfDay"]) {
        if (settings[name]) {
            return true
        }
    }
    return false
}

private ifUnset(Map options, String name, String capability) {
    if (!settings[name]) {
        input(options, name, capability)
    }
}

private ifSet(Map options, String name, String capability) {
    if (settings[name]) {
        input(options, name, capability)
    }
}

def installed() {
    log.debug "Installed with settings: ${settings}"
    subscribeToEvents()
}

def updated() {
    log.debug "Updated with settings: ${settings}"
    unsubscribe()
    unschedule()
    subscribeToEvents()
}

def subscribeToEvents() {
    subscribe(app, appTouchHandler)
    subscribe(contact, "contact.open", eventHandler)
    subscribe(contactClosed, "contact.closed", eventHandler)
    subscribe(acceleration, "acceleration.active", eventHandler)
    subscribe(motion, "motion.active", eventHandler)
    subscribe(mySwitch, "switch.on", eventHandler)
    subscribe(mySwitchOff, "switch.off", eventHandler)
    subscribe(arrivalPresence, "presence.present", eventHandler)
    subscribe(departurePresence, "presence.not present", eventHandler)
    subscribe(smoke, "smoke.detected", eventHandler)
    subscribe(smoke, "smoke.tested", eventHandler)
    subscribe(smoke, "carbonMonoxide.detected", eventHandler)
    subscribe(water, "water.wet", eventHandler)
    subscribe(button1, "button.pushed", eventHandler)

    if (triggerModes) {
        subscribe(location, modeChangeHandler)
    }

    if (timeOfDay) {
        schedule(timeOfDay, scheduledTimeHandler)
    }

    if (song) {
        saveSelectedSong()
    }

    loadText()
}

def eventHandler(evt) {
    log.trace "eventHandler($evt?.name: $evt?.value)"
    if (allOk) {
        log.trace "allOk"
        def lastTime = state[frequencyKey(evt)]
        if (oncePerDayOk(lastTime)) {
            if (frequency) {
                if (lastTime == null || now() - lastTime >= frequency * 60000) {
                    takeAction(evt)
                }
                else {
                    log.debug "Not taking action because $frequency minutes have not elapsed since last action"
                }
            }
            else {
                takeAction(evt)
            }
        }
        else {
            log.debug "Not taking action because it was already taken today"
        }
    }
}
def modeChangeHandler(evt) {
    log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
    if (evt.value in triggerModes) {
        eventHandler(evt)
    }
}

def scheduledTimeHandler() {
    eventHandler(null)
}

def appTouchHandler(evt) {
    takeAction(evt)
}

private takeAction(evt) {

    log.trace "takeAction()"

    if (song) {
        sonos.playSoundAndTrack(state.sound.uri, state.sound.duration, state.selectedSong, volume)
    }
    else if (resumePlaying){
        sonos.playTrackAndResume(state.sound.uri, state.sound.duration, volume)
    }
    else {
        sonos.playTrackAndRestore(state.sound.uri, state.sound.duration, volume)
    }

    if (frequency || oncePerDay) {
        state[frequencyKey(evt)] = now()
    }
    log.trace "Exiting takeAction()"
}

private frequencyKey(evt) {
    "lastActionTimeStamp"
}

private dayString(Date date) {
    def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
    if (location.timeZone) {
        df.setTimeZone(location.timeZone)
    }
    else {
        df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
    }
    df.format(date)
}

private oncePerDayOk(Long lastTime) {
    def result = true
    if (oncePerDay) {
        result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
        log.trace "oncePerDayOk = $result"
    }
    result
}

// TODO - centralize somehow
private getAllOk() {
    modeOk && daysOk && timeOk
}

private getModeOk() {
    def result = !modes || modes.contains(location.mode)
    log.trace "modeOk = $result"
    result
}

private getDaysOk() {
    def result = true
    if (days) {
        def df = new java.text.SimpleDateFormat("EEEE")
        if (location.timeZone) {
            df.setTimeZone(location.timeZone)
        }
        else {
            df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
        }
        def day = df.format(new Date())
        result = days.contains(day)
    }
    log.trace "daysOk = $result"
    result
}

private getTimeOk() {
    def result = true
    if (starting && ending) {
        def currTime = now()
        def start = timeToday(starting, location?.timeZone).time
        def stop = timeToday(ending, location?.timeZone).time
        result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
    }
    log.trace "timeOk = $result"
    result
}

private hhmm(time, fmt = "h:mm a")
{
    def t = timeToday(time, location.timeZone)
    def f = new java.text.SimpleDateFormat(fmt)
    f.setTimeZone(location.timeZone ?: timeZone(time))
    f.format(t)
}

private getTimeLabel()
{
    (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}
// TODO - End Centralize

private loadText() {
    switch ( actionType) {
        case "Bell 1":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"]
            break;
        case "Bell 2":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell2.mp3", duration: "10"]
            break;
        case "Dogs Barking":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/dogs.mp3", duration: "10"]
            break;
        case "Fire Alarm":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/alarm.mp3", duration: "17"]
            break;
        case "The mail has arrived":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/the+mail+has+arrived.mp3", duration: "1"]
            break;
        case "A door opened":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/a+door+opened.mp3", duration: "1"]
            break;
        case "There is motion":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/there+is+motion.mp3", duration: "1"]
            break;
        case "Smartthings detected a flood":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/smartthings+detected+a+flood.mp3", duration: "2"]
            break;
        case "Smartthings detected smoke":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/smartthings+detected+smoke.mp3", duration: "1"]
            break;
        case "Someone is arriving":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/someone+is+arriving.mp3", duration: "1"]
            break;
        case "Piano":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/piano2.mp3", duration: "10"]
            break;
        case "Lightsaber":
            state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/lightsaber.mp3", duration: "10"]
            break;
            case "National Lampoon":
            state.sound = [uri: "http://l5j.net/sounds/nationallampoon.mp3", duration: "27"]
            break;
            case "Leave it on the door":
            state.sound = [uri: "http://l5j.net/sounds/yougotmail.mp3", duration: "2"]
            break;
             case "You Got Mail":
            state.sound = [uri: "http://l5j.net/sounds/mail.mp3", duration: "1"]
            break;
            case "Let It Go":
            state.sound = [uri: "http://l5j.net/sounds/letitgo.mp3", duration: "28"]
            break;
        default:
            if (message) {
                state.sound = textToSpeech(message instanceof List ? message[0] : message) // not sure why this is (sometimes) needed)
            }
            else {
                state.sound = textToSpeech("You selected the custom message option but did not enter a message in the $app.label Smart App")
            }
            break;
    }
}

Step 6.  Now launch the SmartThings app on your Android device.  And choose “Marketplace“.

Step 7.  Then choose “SmartApps” at the top.

Step 8.  Now choose “My Apps” at the bottom.

Step 9.  Now choose the Custom Smart App we added in the IDE earlier.  “Sonos_Mailbox_Notify”.

Step 10.  Here’s where you can configure everything.  So I have mine setup so when the “Contact” opens of Mailbox it will sound the “You’ve got Mail” sound throughout my selected Sonos Speakers.  You can also switch this so it only Plays once per day, or during selected time frame.

{S Gotmail