/**
 *  Talking Alarm Clock
 *
 *  Version - 1.0.0 5/23/15
 *  Version - 1.1.0 5/24/15 - A song can now be selected to play after the voice greeting and bug fixes
 *  Version - 1.2.0 5/27/15 - Added About screen and misc code clean up and GUI revisions
 *  Version - 1.3.0 5/29/15 - Further code optimizations and addition of alarm summary action
 *  Version - 1.3.1 5/30/15 - Fixed one small code syntax issue in Scenario D
 *  Version - 1.4.0 6/7/15 -  Revised About screen, enhanced the weather forecast voice summary, added a mode change option with alarm, and added secondary alarm options
 *  Version - 1.4.1 6/9/15 - Changed the mode change speech to make it clear when the mode change is taking place
 *  Version - 1.4.2 6/10/15 - To prevent accidental triggering of summary, put in a mode switch restriction
 *  Version - 1.4.3 6/12/15 - Syntax issues and minor GUI fixes
 *  Version - 1.4.4 6/15/15 - Fixed a bug with Phrase change at alarm time
 *  Version - 1.4.5 6/17/15 - Code optimization, implemented the new submitOnChange option and a new license agreement change
 *
 *  Copyright 2015 Michael Struck - Uses code from Lighting Director by Tim Slagle & Michael Struck
 *
 *  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.
 *
 */

definition(
    name: "Talking Alarm Clock",
    namespace: "MichaelStruck",
    author: "Michael Struck",
    description: "Control up to 4 waking schedules using a Sonos speaker as an alarm.",
    category: "Convenience",
    iconUrl: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/Talkingclock.png",
    iconX2Url: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/Talkingclock@2x.png",
    iconX3Url: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/Talkingclock@2x.png",
    pausable: true
)

preferences {
    page name:"pageMain"
    page name:"pageSetupScenarioA"
    page name:"pageSetupScenarioB"
    page name:"pageSetupScenarioC"
    page name:"pageSetupScenarioD"
    page name:"pageWeatherSettingsA" //technically, these 4 pages should not be dynamic, but are here to work around a crash on the Andriod app
    page name:"pageWeatherSettingsB"
    page name:"pageWeatherSettingsC"
    page name:"pageWeatherSettingsD"
}

// Show setup page
def pageMain() {
    dynamicPage(name: "pageMain", install: true, uninstall: true) {
        section ("Alarms") {
            href "pageSetupScenarioA", title: getTitle(ScenarioNameA, 1), description: getDesc(A_timeStart, A_sonos, A_day, A_mode), state: greyOut(ScenarioNameA, A_sonos, A_timeStart, A_alarmOn, A_alarmType)
            if (ScenarioNameA && A_sonos && A_timeStart && A_alarmType){
                input "A_alarmOn", "bool", title: "Enable this alarm?", defaultValue: "true", submitOnChange:true
            }
        }
        section {
            href "pageSetupScenarioB", title: getTitle(ScenarioNameB, 2), description: getDesc(B_timeStart, B_sonos, B_day, B_mode), state: greyOut(ScenarioNameB, B_sonos, B_timeStart, B_alarmOn, B_alarmType)
            if (ScenarioNameB && B_sonos && B_timeStart  && B_alarmType){
                input "B_alarmOn", "bool", title: "Enable this alarm?", defaultValue: "false", submitOnChange:true
            }
        }
        section {
            href "pageSetupScenarioC", title: getTitle(ScenarioNameC, 3), description: getDesc(C_timeStart, C_sonos, C_day, C_mode), state: greyOut(ScenarioNameC, C_sonos, C_timeStart, C_alarmOn, C_alarmType)
            if (ScenarioNameC && C_sonos && C_timeStart && C_alarmType){
                input "C_alarmOn", "bool", title: "Enable this alarm?", defaultValue: "false", submitOnChange:true
            }
        }
        section {
            href "pageSetupScenarioD", title: getTitle(ScenarioNameD, 4), description: getDesc(D_timeStart, D_sonos, D_day, D_mode), state: greyOut(ScenarioNameD, D_sonos, D_timeStart, D_alarmOn, D_alarmType)
            if (ScenarioNameD && D_sonos && D_timeStart && D_alarmType){
                input "D_alarmOn", "bool", title: "Enable this alarm?", defaultValue: "false", submitOnChange:true
            }
        }
        section([title:"Options", mobileOnly:true]) {
            input "alarmSummary", "bool", title: "Enable Alarm Summary", defaultValue: "false", submitOnChange:true
            if (alarmSummary) {
                href "pageAlarmSummary", title: "Alarm Summary Settings", description: "Tap to configure alarm summary settings", state: "complete"
            }
            input "zipCode", "text", title: "Zip Code", required: false
            label title:"Assign a name", required: false
            href "pageAbout", title: "About ${textAppName()}", description: "Tap to get application version, license and instructions"
        }
    }
}

page(name: "pageAlarmSummary", title: "Alarm Summary Settings") {
    section {
           input "summarySonos", "capability.musicPlayer", title: "Choose a Sonos speaker", required: false
        input "summaryVolume", "number", title: "Set the summary volume", description: "0-100%", required: false
        input "summaryDisabled", "bool", title: "Include disabled or unconfigured alarms in summary", defaultValue: "false"
        input "summaryMode", "mode", title: "Speak summary only during the following modes...", multiple: true, required: false
    }
}
//Show "pageSetupScenarioA" page
def pageSetupScenarioA() {
    dynamicPage(name: "pageSetupScenarioA") {
        section("Alarm settings") {
            input "ScenarioNameA", "text", title: "Scenario Name", multiple: false, required: true
            input "A_sonos", "capability.musicPlayer", title: "Choose a Sonos speaker", required: true, submitOnChange:true
            input "A_volume", "number", title: "Alarm volume", description: "0-100%", required: false
            input "A_timeStart", "time", title: "Time to trigger alarm", required: true
            input "A_day", "enum", options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], title: "Alarm on certain days of the week...", multiple: true, required: false
            input "A_mode", "mode", title: "Alarm only during the following modes...", multiple: true, required: false
            input "A_alarmType", "enum", title: "Select a primary alarm type...", multiple: false, required: true, options: [[1:"Alarm sound (up to 20 seconds)"],[2:"Voice Greeting"],[3:"Music track/Internet Radio"]], submitOnChange:true

            if (A_alarmType != "3") {
                if (A_alarmType == "1"){
                    input "A_secondAlarm", "enum", title: "Select a second alarm after the first is completed", multiple: false, required: false, options: [[1:"Voice Greeting"],[2:"Music track/Internet Radio"]], submitOnChange:true
                }
                if (A_alarmType == "2"){
                    input "A_secondAlarmMusic", "bool", title: "Play a track after voice greeting", defaultValue: "false", required: false, submitOnChange:true
                }
            }
        }
        if (A_alarmType == "1"){
            section ("Alarm sound options"){
                input "A_soundAlarm", "enum", title: "Play this sound...", required:false, multiple: false, options: [[1:"Alien-8 seconds"],[2:"Bell-12 seconds"], [3:"Buzzer-20 seconds"], [4:"Fire-20 seconds"], [5:"Rooster-2 seconds"], [6:"Siren-20 seconds"]]
                input "A_soundLength", "number", title: "Maximum time to play sound (empty=use sound default)", description: "1-20", required: false
               }
        }
        if (A_alarmType == "2" || (A_alarmType == "1" && A_secondAlarm =="1")) {
            section ("Voice greeting options") {
                input "A_wakeMsg", "text", title: "Wake voice message", defaultValue: "Good morning! It is %time% on %day%, %date%.", required: false
                href "pageWeatherSettingsA", title: "Weather Reporting Settings", description: getWeatherDesc(A_weatherReport, A_includeSunrise, A_includeSunset, A_includeTemp, A_humidity, A_localTemp), state: greyOut1(A_weatherReport, A_includeSunrise, A_includeSunset, A_includeTemp, A_humidity, A_localTemp)
            }
        }
           if (A_alarmType == "3" || (A_alarmType == "1" && A_secondAlarm =="2") || (A_alarmType == "2" && A_secondAlarmMusic)){
            section ("Music track/internet radio options"){
                input "A_musicTrack", "enum", title: "Play this track/internet radio station", required:false, multiple: false, options: songOptions(A_sonos, 1)
            }
          }
        section("Devices to control in this alarm scenario") {
            input "A_switches", "capability.switch",title: "Control the following switches...", multiple: true, required: false, submitOnChange:true
            href "pageDimmersA", title: "Dimmer Settings", description: dimmerDesc(A_dimmers), state: greyOutOption(A_dimmers), submitOnChange:true
            href "pageThermostatsA", title: "Thermostat Settings", description: thermostatDesc(A_thermostats, A_temperatureH, A_temperatureC), state: greyOutOption(A_thermostats), submitOnChange:true
            if ((A_switches || A_dimmers || A_thermostats) && (A_alarmType == "2" || (A_alarmType == "1" && A_secondAlarm =="1"))){
                input "A_confirmSwitches", "bool", title: "Confirm switches/thermostats status in voice message", defaultValue: "false"
            }
        }
        section ("Other actions at alarm time"){
            def phrases = location.helloHome?.getPhrases()*.label
            if (phrases) {
                phrases.sort()
                input "A_phrase", "enum", title: "Alarm triggers the following phrase", required: false, options: phrases, multiple: false, submitOnChange:true
                if (A_phrase  && (A_alarmType == "2" || (A_alarmType == "1" && A_secondAlarm =="1"))){
                    input "A_confirmPhrase", "bool", title: "Confirm Hello, Home phrase in voice message", defaultValue: "false"
                }
            }
            input "A_triggerMode", "mode", title: "Alarm triggers the following mode", required: false, submitOnChange:true
               if (A_triggerMode  && (A_alarmType == "2" || (A_alarmType == "1" && A_secondAlarm =="1"))){
                input "A_confirmMode", "bool", title: "Confirm mode in voice message", defaultValue: "false"
            }
        }
    }
}

page(name: "pageDimmersA", title: "Dimmer Settings") {
    section {
           input "A_dimmers", "capability.switchLevel", title: "Dim the following...", multiple: true, required: false
        input "A_level", "enum", options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],title: "Set dimmers to this level", multiple: false, required: false
    }
}

page(name: "pageThermostatsA", title: "Thermostat Settings") {
    section {
           input "A_thermostats", "capability.thermostat", title: "Thermostat to control...", multiple: false, required: false
    }
    section {
        input "A_temperatureH", "number", title: "Heating setpoint", required: false, description: "Temperature when in heat mode"
        input "A_temperatureC", "number", title: "Cooling setpoint", required: false, description: "Temperature when in cool mode"
    }
}

def pageWeatherSettingsA() {
    dynamicPage(name: "pageWeatherSettingsA", title: "Weather Reporting Settings") {
        section {
            input "A_includeTemp", "bool", title: "Speak current temperature (from local forecast)", defaultValue: "false"
            input "A_localTemp", "capability.temperatureMeasurement", title: "Speak local temperature (from device)", required: false, multiple: false
            input "A_humidity", "capability.relativeHumidityMeasurement", title: "Speak local humidity (from device)", required: false, multiple: false
            input "A_weatherReport", "bool", title: "Speak today's weather forecast", defaultValue: "false"
            input "A_includeSunrise", "bool", title: "Speak today's sunrise", defaultValue: "false"
            input "A_includeSunset", "bool", title: "Speak today's sunset", defaultValue: "false"
        }
    }
}

//Show "pageSetupScenarioB" page
def pageSetupScenarioB() {
    dynamicPage(name: "pageSetupScenarioB") {
        section("Alarm settings") {
            input "ScenarioNameB", "text", title: "Scenario Name", multiple: false, required: true
            input "B_sonos", "capability.musicPlayer", title: "Choose a Sonos speaker", required: true, submitOnChange:true
            input "B_volume", "number", title: "Alarm volume", description: "0-100%", required: false
            input "B_timeStart", "time", title: "Time to trigger alarm", required: true
            input "B_day", "enum", options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], title: "Alarm on certain days of the week...", multiple: true, required: false
            input "B_mode", "mode", title: "Alarm only during the following modes...", multiple: true, required: false
            input "B_alarmType", "enum", title: "Select a primary alarm type...", multiple: false, required: true, options: [[1:"Alarm sound (up to 20 seconds)"],[2:"Voice Greeting"],[3:"Music track/Internet Radio"]], submitOnChange:true

            if (B_alarmType != "3") {
                if (B_alarmType == "1"){
                    input "B_secondAlarm", "enum", title: "Select a second alarm after the first is completed", multiple: false, required: false, options: [[1:"Voice Greeting"],[2:"Music track/Internet Radio"]], submitOnChange:true
                }
                if (B_alarmType == "2"){
                    input "B_secondAlarmMusic", "bool", title: "Play a track after voice greeting", defaultValue: "false", required: false, submitOnChange:true
                }
            }
        }
        if (B_alarmType == "1"){
            section ("Alarm sound options"){
                input "B_soundAlarm", "enum", title: "Play this sound...", required:false, multiple: false, options: [[1:"Alien-8 seconds"],[2:"Bell-12 seconds"], [3:"Buzzer-20 seconds"], [4:"Fire-20 seconds"], [5:"Rooster-2 seconds"], [6:"Siren-20 seconds"]]
                input "B_soundLength", "number", title: "Maximum time to play sound (empty=use sound default)", description: "1-20", required: false
               }
        }
        if (B_alarmType == "2" || (B_alarmType == "1" && B_secondAlarm =="1")){
            section ("Voice greeting options") {
                input "B_wakeMsg", "text", title: "Wake voice message", defaultValue: "Good morning! It is %time% on %day%, %date%.", required: false
                href "pageWeatherSettingsB", title: "Weather Reporting Settings", description: getWeatherDesc(B_weatherReport, B_includeSunrise, B_includeSunset, B_includeTemp, B_humidity, B_localTemp), state: greyOut1(B_weatherReport, B_includeSunrise, B_includeSunset, B_includeTemp, B_humidity, B_localTemp)
            }
        }
           if (B_alarmType == "3" || (B_alarmType == "1" && B_secondAlarm =="2") || (B_alarmType == "2" && B_secondAlarmMusic)){
            section ("Music track/internet radio options"){
                input "B_musicTrack", "enum", title: "Play this track/internet radio station", required:false, multiple: false, options: songOptions(B_sonos, 1)
            }
          }
        section("Devices to control in this alarm scenario") {
            input "B_switches", "capability.switch",title: "Control the following switches...", multiple: true, required: false, submitOnChange:true
            href "pageDimmersB", title: "Dimmer Settings", description: dimmerDesc(B_dimmers), state: greyOutOption(B_dimmers), submitOnChange:true
            href "pageThermostatsB", title: "Thermostat Settings", description: thermostatDesc(B_thermostats, B_temperatureH, B_temperatureC), state: greyOutOption(B_thermostats), submitOnChange:true
            if ((B_switches || B_dimmers || B_thermostats) && (B_alarmType == "2" || (B_alarmType == "1" && B_secondAlarm =="1"))){
                input "B_confirmSwitches", "bool", title: "Confirm switches/thermostats status in voice message", defaultValue: "false"
            }
        }
        section ("Other actions at alarm time"){
            def phrases = location.helloHome?.getPhrases()*.label
            if (phrases) {
                phrases.sort()
                input "B_phrase", "enum", title: "Alarm triggers the following phrase", required: false, options: phrases, multiple: false, submitOnChange:true
                if (B_phrase  && (B_alarmType == "2" || (B_alarmType == "1" && B_secondAlarm =="1"))){
                    input "B_confirmPhrase", "bool", title: "Confirm Hello, Home phrase in voice message", defaultValue: "false"
                }
            }
            input "B_triggerMode", "mode", title: "Alarm triggers the following mode", required: false, submitOnChange:true
               if (B_triggerMode  && (B_alarmType == "2" || (B_alarmType == "1" && B_secondAlarm =="1"))){
                input "B_confirmMode", "bool", title: "Confirm mode in voice message", defaultValue: "false"
            }
        }
    }
}

page(name: "pageDimmersB", title: "Dimmer Settings") {
    section {
           input "B_dimmers", "capability.switchLevel", title: "Dim the following...", multiple: true, required: false
        input "B_level", "enum", options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],title: "Set dimmers to this level", multiple: false, required: false
    }
}

page(name: "pageThermostatsB", title: "Thermostat Settings") {
    section {
           input "B_thermostats", "capability.thermostat", title: "Thermostat to control...", multiple: false, required: false
    }
    section {
        input "B_temperatureH", "number", title: "Heating setpoint", required: false, description: "Temperature when in heat mode"
        input "B_temperatureC", "number", title: "Cooling setpoint", required: false, description: "Temperature when in cool mode"
    }
}

def pageWeatherSettingsB() {
    dynamicPage(name: "pageWeatherSettingsB", title: "Weather Reporting Settings") {
        section {
            input "B_includeTemp", "bool", title: "Speak current temperature (from local forecast)", defaultValue: "false"
            input "B_localTemp", "capability.temperatureMeasurement", title: "Speak local temperature (from device)", required: false, multiple: false
            input "B_humidity", "capability.relativeHumidityMeasurement", title: "Speak local humidity (from device)", required: false, multiple: false
            input "B_weatherReport", "bool", title: "Speak today's weather forecast", defaultValue: "false"
            input "B_includeSunrise", "bool", title: "Speak today's sunrise", defaultValue: "false"
            input "B_includeSunset", "bool", title: "Speak today's sunset", defaultValue: "false"
        }
    }
}

//Show "pageSetupScenarioC" page
def pageSetupScenarioC() {
    dynamicPage(name: "pageSetupScenarioC") {
        section("Alarm settings") {
            input "ScenarioNameC", "text", title: "Scenario Name", multiple: false, required: true
            input "C_sonos", "capability.musicPlayer", title: "Choose a Sonos speaker", required: true, submitOnChange:true
            input "C_volume", "number", title: "Alarm volume", description: "0-100%", required: false
            input "C_timeStart", "time", title: "Time to trigger alarm", required: true
            input "C_day", "enum", options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], title: "Alarm on certain days of the week...", multiple: true, required: false
            input "C_mode", "mode", title: "Alarm only during the following modes...", multiple: true, required: false
            input "C_alarmType", "enum", title: "Select a primary alarm type...", multiple: false, required: true, options: [[1:"Alarm sound (up to 20 seconds)"],[2:"Voice Greeting"],[3:"Music track/Internet Radio"]], submitOnChange:true

            if (C_alarmType != "3") {
                if (C_alarmType == "1"){
                    input "C_secondAlarm", "enum", title: "Select a second alarm after the first is completed", multiple: false, required: false, options: [[1:"Voice Greeting"],[2:"Music track/Internet Radio"]], submitOnChange:true
                }
                if (C_alarmType == "2"){
                    input "C_secondAlarmMusic", "bool", title: "Play a track after voice greeting", defaultValue: "false", required: false, submitOnChange:true
                }
            }
        }
        if (C_alarmType == "1"){
            section ("Alarm sound options"){
                input "C_soundAlarm", "enum", title: "Play this sound...", required:false, multiple: false, options: [[1:"Alien-8 seconds"],[2:"Bell-12 seconds"], [3:"Buzzer-20 seconds"], [4:"Fire-20 seconds"], [5:"Rooster-2 seconds"], [6:"Siren-20 seconds"]]
                input "C_soundLength", "number", title: "Maximum time to play sound (empty=use sound default)", description: "1-20", required: false
               }
        }

        if (C_alarmType == "2" || (C_alarmType == "1" && C_secondAlarm =="1")) {
            section ("Voice greeting options") {
                input "C_wakeMsg", "text", title: "Wake voice message", defaultValue: "Good morning! It is %time% on %day%, %date%.", required: false
                href "pageWeatherSettingsC", title: "Weather Reporting Settings", description: getWeatherDesc(C_weatherReport, C_includeSunrise, C_includeSunset, C_includeTemp, A_humidity, C_localTemp), state: greyOut1(C_weatherReport, C_includeSunrise, C_includeSunset, C_includeTemp, C_humidity, C_localTemp)            }
        }

           if (C_alarmType == "3" || (C_alarmType == "1" && C_secondAlarm =="2") || (C_alarmType == "2" && C_secondAlarmMusic)){
            section ("Music track/internet radio options"){
                input "C_musicTrack", "enum", title: "Play this track/internet radio station", required:false, multiple: false, options: songOptions(C_sonos, 1)
            }
          }
        section("Devices to control in this alarm scenario") {
            input "C_switches", "capability.switch",title: "Control the following switches...", multiple: true, required: false, submitOnChange:true
            href "pageDimmersC", title: "Dimmer Settings", description: dimmerDesc(C_dimmers), state: greyOutOption(C_dimmers), submitOnChange:true
            href "pageThermostatsC", title: "Thermostat Settings", description: thermostatDesc(C_thermostats, C_temperatureH, C_temperatureC), state: greyOutOption(C_thermostats), submitOnChange:true
            if ((C_switches || C_dimmers || C_thermostats) && (C_alarmType == "2" || (C_alarmType == "1" && C_secondAlarm =="1"))){
                input "C_confirmSwitches", "bool", title: "Confirm switches/thermostats status in voice message", defaultValue: "false"
            }
        }
        section ("Other actions at alarm time"){
            def phrases = location.helloHome?.getPhrases()*.label
            if (phrases) {
                phrases.sort()
                input "C_phrase", "enum", title: "Alarm triggers the following phrase", required: false, options: phrases, multiple: false, submitOnChange:true
                if (C_phrase  && (C_alarmType == "2" || (C_alarmType == "1" && C_secondAlarm =="1"))){
                    input "C_confirmPhrase", "bool", title: "Confirm Hello, Home phrase in voice message", defaultValue: "false"
                }
            }
            input "C_triggerMode", "mode", title: "Alarm triggers the following mode", required: false, submitOnChange:true
               if (C_triggerMode  && (C_alarmType == "2" || (C_alarmType == "1" && C_secondAlarm =="1"))){
                input "C_confirmMode", "bool", title: "Confirm mode in voice message", defaultValue: "false"
            }
        }
    }
}

page(name: "pageDimmersC", title: "Dimmer Settings") {
    section {
           input "C_dimmers", "capability.switchLevel", title: "Dim the following...", multiple: true, required: false
        input "C_level", "enum", options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],title: "Set dimmers to this level", multiple: false, required: false
    }
}

page(name: "pageThermostatsC", title: "Thermostat Settings") {
    section {
           input "C_thermostats", "capability.thermostat", title: "Thermostat to control...", multiple: false, required: false
    }
    section {
        input "C_temperatureH", "number", title: "Heating setpoint", required: false, description: "Temperature when in heat mode"
        input "C_temperatureC", "number", title: "Cooling setpoint", required: false, description: "Temperature when in cool mode"
    }
}

def pageWeatherSettingsC() {
    dynamicPage(name: "pageWeatherSettingsC", title: "Weather Reporting Settings") {
        section {
            input "C_includeTemp", "bool", title: "Speak current temperature (from local forecast)", defaultValue: "false"
            input "C_localTemp", "capability.temperatureMeasurement", title: "Speak local temperature (from device)", required: false, multiple: false
            input "C_humidity", "capability.relativeHumidityMeasurement", title: "Speak local humidity (from device)", required: false, multiple: false
            input "C_weatherReport", "bool", title: "Speak today's weather forecast", defaultValue: "false"
            input "C_includeSunrise", "bool", title: "Speak today's sunrise", defaultValue: "false"
            input "C_includeSunset", "bool", title: "Speak today's sunset", defaultValue: "false"
        }
    }
}

//Show "pageSetupScenarioD" page
def pageSetupScenarioD() {
    dynamicPage(name: "pageSetupScenarioD") {
        section("Alarm settings") {
            input "ScenarioNameD", "text", title: "Scenario Name", multiple: false, required: true
            input "D_sonos", "capability.musicPlayer", title: "Choose a Sonos speaker", required: true, submitOnChange:true
            input "D_volume", "number", title: "Alarm volume", description: "0-100%", required: false
            input "D_timeStart", "time", title: "Time to trigger alarm", required: true
            input "D_day", "enum", options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], title: "Alarm on certain days of the week...", multiple: true, required: false
            input "D_mode", "mode", title: "Alarm only during the following modes...", multiple: true, required: false
            input "D_alarmType", "enum", title: "Select a primary alarm type...", multiple: false, required: true, options: [[1:"Alarm sound (up to 20 seconds)"],[2:"Voice Greeting"],[3:"Music track/Internet Radio"]], submitOnChange:true

            if (D_alarmType != "3") {
                if (D_alarmType == "1"){
                    input "D_secondAlarm", "enum", title: "Select a second alarm after the first is completed", multiple: false, required: false, options: [[1:"Voice Greeting"],[2:"Music track/Internet Radio"]], submitOnChange:true
                }
                if (D_alarmType == "2"){
                    input "D_secondAlarmMusic", "bool", title: "Play a track after voice greeting", defaultValue: "false", required: false, submitOnChange:true
                }
            }
        }
        if (D_alarmType == "1"){
            section ("Alarm sound options"){
                input "D_soundAlarm", "enum", title: "Play this sound...", required:false, multiple: false, options: [[1:"Alien-8 seconds"],[2:"Bell-12 seconds"], [3:"Buzzer-20 seconds"], [4:"Fire-20 seconds"], [5:"Rooster-2 seconds"], [6:"Siren-20 seconds"]]
                input "D_soundLength", "number", title: "Maximum time to play sound (empty=use sound default)", description: "1-20", required: false
               }
        }

        if (D_alarmType == "2" || (D_alarmType == "1" && D_secondAlarm =="1")) {
            section ("Voice greeting options") {
                input "D_wakeMsg", "text", title: "Wake voice message", defaultValue: "Good morning! It is %time% on %day%, %date%.", required: false
                href "pageWeatherSettingsD", title: "Weather Reporting Settings", description: getWeatherDesc(D_weatherReport, D_includeSunrise, D_includeSunset, D_includeTemp, D_humidity, D_localTemp), state: greyOut1(D_weatherReport, D_includeSunrise, D_includeSunset, D_includeTemp, D_humidity, D_localTemp)            }
        }

           if (D_alarmType == "3" || (D_alarmType == "1" && D_secondAlarm =="2") || (D_alarmType == "2" && D_secondAlarmMusic)){
            section ("Music track/internet radio options"){
                input "D_musicTrack", "enum", title: "Play this track/internet radio station", required:false, multiple: false, options: songOptions(D_sonos, 1)
            }
          }
        section("Devices to control in this alarm scenario") {
            input "D_switches", "capability.switch",title: "Control the following switches...", multiple: true, required: false, submitOnChange:true
            href "pageDimmersD", title: "Dimmer Settings", description: dimmerDesc(D_dimmers), state: greyOutOption(D_dimmers), submitOnChange:true
            href "pageThermostatsD", title: "Thermostat Settings", description: thermostatDesc(D_thermostats, D_temperatureH, D_temperatureC), state: greyOutOption(D_thermostats), submitOnChange:true
            if ((D_switches || D_dimmers || D_thermostats) && (D_alarmType == "2" || (D_alarmType == "1" && D_secondAlarm =="1"))){
                input "D_confirmSwitches", "bool", title: "Confirm switches/thermostats status in voice message", defaultValue: "false"
            }
        }
        section ("Other actions at alarm time"){
            def phrases = location.helloHome?.getPhrases()*.label
            if (phrases) {
                phrases.sort()
                input "D_phrase", "enum", title: "Alarm triggers the following phrase", required: false, options: phrases, multiple: false, submitOnChange:true
                if (D_phrase  && (D_alarmType == "2" || (D_alarmType == "1" && D_secondAlarm =="1"))){
                    input "D_confirmPhrase", "bool", title: "Confirm Hello, Home phrase in voice message", defaultValue: "false"
                }
            }
            input "D_triggerMode", "mode", title: "Alarm triggers the following mode", required: false, submitOnChange:true
               if (D_triggerMode  && (D_alarmType == "2" || (D_alarmType == "1" && D_secondAlarm =="1"))){
                input "D_confirmMode", "bool", title: "Confirm mode in voice message", defaultValue: "false"
            }
        }
    }
}

page(name: "pageDimmersD", title: "Dimmer Settings") {
    section {
           input "D_dimmers", "capability.switchLevel", title: "Dim the following...", multiple: true, required: false
        input "D_level", "enum", options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]],title: "Set dimmers to this level", multiple: false, required: false
    }
}

page(name: "pageThermostatsD", title: "Thermostat Settings") {
    section {
           input "D_thermostats", "capability.thermostat", title: "Thermostat to control...", multiple: false, required: false
    }
    section {
        input "D_temperatureH", "number", title: "Heating setpoint", required: false, description: "Temperature when in heat mode"
        input "D_temperatureC", "number", title: "Cooling setpoint", required: false, description: "Temperature when in cool mode"
    }
}

def pageWeatherSettingsD() {
    dynamicPage(name: "pageWeatherSettingsD", title: "Weather Reporting Settings") {
        section {
            input "D_includeTemp", "bool", title: "Speak current temperature (from local forecast)", defaultValue: "false"
            input "D_localTemp", "capability.temperatureMeasurement", title: "Speak local temperature (from device)", required: false, multiple: false
            input "D_humidity", "capability.relativeHumidityMeasurement", title: "Speak local humidity (from device)", required: false, multiple: false
            input "D_weatherReport", "bool", title: "Speak today's weather forecast", defaultValue: "false"
            input "D_includeSunrise", "bool", title: "Speak today's sunrise", defaultValue: "false"
            input "D_includeSunset", "bool", title: "Speak today's sunset", defaultValue: "false"
        }
    }
}

page(name: "pageAbout", title: "About ${textAppName()}") {
        section {
            paragraph "${textVersion()}\n${textCopyright()}\n\n${textLicense()}\n"
        }
        section("Instructions") {
            paragraph textHelp()
        }
}

//--------------------------------------

def installed() {
    initialize()
}

def updated() {
    unschedule()
    unsubscribe()
    initialize()
}

def initialize() {
    if (A_alarmType =="1"){
        alarmSoundUri(A_soundAlarm, A_soundLength, 1)
    }
    if (B_alarmType =="1"){
        alarmSoundUri(B_soundAlarm, B_soundLength, 2)
    }
    if (C_alarmType =="1"){
        alarmSoundUri(C_soundAlarm, C_soundLength, 3)
    }
    if (D_alarmType =="1"){
        alarmSoundUri(D_soundAlarm, D_soundLength, 4)
    }

    if (alarmSummary && summarySonos) {
        subscribe(app, appTouchHandler)
    }
    if (ScenarioNameA && A_timeStart && A_sonos && A_alarmOn && A_alarmType){
        schedule (A_timeStart, alarm_A)
        if (A_musicTrack){
            saveSelectedSong(A_sonos, A_musicTrack, 1)
        }
    }
    if (ScenarioNameB && B_timeStart && B_sonos &&B_alarmOn && B_alarmType){
        schedule (B_timeStart, alarm_B)
        if (B_musicTrack){
            saveSelectedSong(B_sonos, B_musicTrack, 2)
        }
    }
    if (ScenarioNameC && C_timeStart && C_sonos && C_alarmOn && C_alarmType){
        schedule (C_timeStart, alarm_C)
        if (C_musicTrack){
            saveSelectedSong(C_sonos, C_musicTrack, 3)
        }
    }
    if (ScenarioNameD && D_timeStart && D_sonos && D_alarmOn && D_alarmType){
        schedule (D_timeStart, alarm_D)
        if (D_musicTrack){
            saveSelectedSong(D_sonos, D_musicTrack, 4)
        }
    }
}

//--------------------------------------

def alarm_A() {
    if ((!A_mode || A_mode.contains(location.mode)) && getDayOk(A_day)) {
        if (A_switches || A_dimmers || A_thermostats) {
            def dimLevel = A_level as Integer
            A_switches?.on()
            A_dimmers?.setLevel(dimLevel)
            if (A_thermostats) {
                def thermostatState = A_thermostats.currentThermostatMode
                if (thermostatState == "auto") {
                    A_thermostats.setHeatingSetpoint(A_temperatureH)
                    A_thermostats.setCoolingSetpoint(A_temperatureC)
                }
                else if (thermostatState == "heat") {
                    A_thermostats.setHeatingSetpoint(A_temperatureH)
                    log.info "Set $A_thermostats Heat $A_temperatureH°"
                }
                else {
                    A_thermostats.setCoolingSetpoint(A_temperatureC)
                    log.info "Set $A_thermostats Cool $A_temperatureC°"
                }
               }
        }
        if (A_phrase) {
            location.helloHome.execute(A_phrase)
        }

        if (A_triggerMode && location.mode != A_triggerMode) {
            if (location.modes?.find{it.name == A_triggerMode}) {
                setLocationMode(A_triggerMode)
            }
            else {
                log.debug "Unable to change to undefined mode '${A_triggerMode}'"
            }
        }

        if (A_volume) {
                A_sonos.setLevel(A_volume)
        }

        if (A_alarmType == "2" || (A_alarmType == "1" && A_secondAlarm =="1")) {
            state.fullMsgA = ""
            if (A_wakeMsg) {
                   getGreeting(A_wakeMsg, 1)
            }

            if (A_weatherReport || A_humidity || A_includeTemp || A_localTemp) {
                getWeatherReport(1, A_weatherReport, A_humidity, A_includeTemp, A_localTemp)
            }

            if (A_includeSunrise || A_includeSunset) {
                getSunriseSunset(1, A_includeSunrise, A_includeSunset)
            }

            if ((A_switches || A_dimmers || A_thermostats) && A_confirmSwitches) {
                getOnConfimation(A_switches, A_dimmers, A_thermostats, 1)
               }

            if (A_phrase && A_confirmPhrase) {
                getPhraseConfirmation(1, A_phrase)
            }

            if (A_triggerMode && A_confirmMode){
                getModeConfirmation(A_triggerMode, 1)
            }

            state.soundA = textToSpeech(state.fullMsgA, true)
        }

        if (A_alarmType == "1"){
            if (A_secondAlarm == "1" && state.soundAlarmA){
                A_sonos.playSoundAndTrack (state.soundAlarmA.uri, state.soundAlarmA.duration, state.soundA.uri)
            }
            if (A_secondAlarm == "2" && state.selectedSongA && state.soundAlarmA){
                A_sonos.playSoundAndTrack (state.soundAlarmA.uri, state.soundAlarmA.duration, state.selectedSongA)
            }
            if (!A_secondAlarm){
                A_sonos.playTrack(state.soundAlarmA.uri)
            }
        }

        if (A_alarmType == "2") {
            if (A_secondAlarmMusic && state.selectedSongA){
                A_sonos.playSoundAndTrack (state.soundA.uri, state.soundA.duration, state.selectedSongA)
            }
            else {
                A_sonos.playTrack(state.soundA.uri)
            }
        }

        if (A_alarmType == "3") {
               A_sonos.playTrack(state.selectedSongA)
        }
    }
}

def alarm_B() {
    if ((!B_mode || B_mode.contains(location.mode)) && getDayOk(B_day)) {
        if (B_switches || B_dimmers || B_thermostats) {
            def dimLevel = B_level as Integer
            B_switches?.on()
            B_dimmers?.setLevel(dimLevel)
            if (B_thermostats) {
                def thermostatState = B_thermostats.currentThermostatMode
                if (thermostatState == "auto") {
                    B_thermostats.setHeatingSetpoint(B_temperatureH)
                    B_thermostats.setCoolingSetpoint(B_temperatureC)
                }
                else if (thermostatState == "heat") {
                    B_thermostats.setHeatingSetpoint(B_temperatureH)
                    log.info "Set $B_thermostats Heat $B_temperatureH°"
                }
                else {
                    B_thermostats.setCoolingSetpoint(B_temperatureC)
                    log.info "Set $B_thermostats Cool $B_temperatureC°"
                }
               }
        }
        if (B_phrase) {
            location.helloHome.execute(B_phrase)
        }

        if (B_triggerMode && location.mode != B_triggerMode) {
            if (location.modes?.find{it.name == B_triggerMode}) {
                setLocationMode(B_triggerMode)
            }
            else {
                log.debug "Unable to change to undefined mode '${B_triggerMode}'"
            }
        }

        if (B_volume) {
                B_sonos.setLevel(B_volume)
        }

        if (B_alarmType == "2" || (B_alarmType == "1" && B_secondAlarm =="1")) {
            state.fullMsgB = ""
            if (B_wakeMsg) {
                   getGreeting(B_wakeMsg, 2)
            }

            if (B_weatherReport || B_humidity || B_includeTemp || B_localTemp) {
                getWeatherReport(2, B_weatherReport, B_humidity, B_includeTemp, B_localTemp)
            }

            if (B_includeSunrise || B_includeSunset) {
                getSunriseSunset(2, B_includeSunrise, B_includeSunset)
            }

            if ((B_switches || B_dimmers || B_thermostats) && B_confirmSwitches) {
                getOnConfimation(B_switches, B_dimmers, B_thermostats, 2)
               }

            if (B_phrase && B_confirmPhrase) {
                getPhraseConfirmation(2, B_phrase)
            }

            if (B_triggerMode && B_confirmMode){
                getModeConfirmation(B_triggerMode, 2)
            }

            state.soundB = textToSpeech(state.fullMsgB, true)
        }

        if (B_alarmType == "1"){
            if (B_secondAlarm == "1" && state.soundAlarmB) {
                B_sonos.playSoundAndTrack (state.soundAlarmB.uri, state.soundAlarmB.duration, state.soundB.uri)
            }
            if (B_secondAlarm == "2" && state.selectedSongB && state.soundAlarmB){
                B_sonos.playSoundAndTrack (state.soundAlarmB.uri, state.soundAlarmB.duration, state.selectedSongB)
            }
            if (!B_secondAlarm){
                B_sonos.playTrack(state.soundAlarmB.uri)
            }
        }

        if (B_alarmType == "2") {
            if (B_secondAlarmMusic && state.selectedSongB){
                B_sonos.playSoundAndTrack (state.soundB.uri, state.soundB.duration, state.selectedSongB)
            }
            else {
                B_sonos.playTrack(state.soundB.uri)
            }
        }

        if (B_alarmType == "3") {
               B_sonos.playTrack(state.selectedSongB)
        }
    }
}

def alarm_C() {
    if ((!C_mode || C_mode.contains(location.mode)) && getDayOk(C_day)) {
        if (C_switches || C_dimmers || C_thermostats) {
            def dimLevel = C_level as Integer
            C_switches?.on()
            C_dimmers?.setLevel(dimLevel)
            if (C_thermostats) {
                def thermostatState = C_thermostats.currentThermostatMode
                if (thermostatState == "auto") {
                    C_thermostats.setHeatingSetpoint(C_temperatureH)
                    C_thermostats.setCoolingSetpoint(C_temperatureC)
                }
                else if (thermostatState == "heat") {
                    C_thermostats.setHeatingSetpoint(C_temperatureH)
                    log.info "Set $C_thermostats Heat $C_temperatureH°"
                }
                else {
                    C_thermostats.setCoolingSetpoint(C_temperatureC)
                    log.info "Set $C_thermostats Cool $C_temperatureC°"
                }
               }
        }
        if (C_phrase) {
            location.helloHome.execute(C_phrase)
        }

        if (C_triggerMode && location.mode != C_triggerMode) {
            if (location.modes?.find{it.name == C_triggerMode}) {
                setLocationMode(C_triggerMode)
            }
            else {
                log.debug "Unable to change to undefined mode '${C_triggerMode}'"
            }
        }

        if (C_volume) {
                C_sonos.setLevel(C_volume)
        }

        if (C_alarmType == "2" || (C_alarmType == "1" && C_secondAlarm =="1")) {
            state.fullMsgC = ""
            if (C_wakeMsg) {
                   getGreeting(C_wakeMsg, 3)
            }

            if (C_weatherReport || C_humidity || C_includeTemp || C_localTemp) {
                getWeatherReport(3, C_weatherReport, C_humidity, C_includeTemp, C_localTemp)
            }

            if (C_includeSunrise || C_includeSunset) {
                getSunriseSunset(3, C_includeSunrise, C_includeSunset)
            }

            if ((C_switches || C_dimmers || C_thermostats) && C_confirmSwitches) {
                getOnConfimation(C_switches, C_dimmers, C_thermostats, 3)
               }

            if (C_phrase && C_confirmPhrase) {
                getPhraseConfirmation(3, C_phrase)
            }

            if (C_triggerMode && C_confirmMode){
                getModeConfirmation(C_triggerMode, 3)
            }

            state.soundC = textToSpeech(state.fullMsgC, true)
        }

        if (C_alarmType == "1"){
            if (C_secondAlarm == "1" && state.soundAlarmC){
                C_sonos.playSoundAndTrack (state.soundAlarmC.uri, state.soundAlarmC.duration, state.soundC.uri)
            }
            if (C_secondAlarm == "2" && state.selectedSongC && state.soundAlarmC){
                C_sonos.playSoundAndTrack (state.soundAlarmC.uri, state.soundAlarmC.duration, state.selectedSongC)
            }
            if (!C_secondAlarm){
                C_sonos.playTrack(state.soundAlarmC.uri)
            }
        }

        if (C_alarmType == "2") {
            if (C_secondAlarmMusic && state.selectedSongC){
                C_sonos.playSoundAndTrack (state.soundC.uri, state.soundC.duration, state.selectedSongC)
            }
            else {
                C_sonos.playTrack(state.soundC.uri)
            }
        }

        if (C_alarmType == "3") {
               C_sonos.playTrack(state.selectedSongC)
        }
    }
}

def alarm_D() {
    if ((!D_mode || D_mode.contains(location.mode)) && getDayOk(D_day)) {
        if (D_switches || D_dimmers || D_thermostats) {
            def dimLevel = D_level as Integer
            D_switches?.on()
            D_dimmers?.setLevel(dimLevel)
            if (D_thermostats) {
                def thermostatState = D_thermostats.currentThermostatMode
                if (thermostatState == "auto") {
                    D_thermostats.setHeatingSetpoint(D_temperatureH)
                    D_thermostats.setCoolingSetpoint(D_temperatureC)
                }
                else if (thermostatState == "heat") {
                    D_thermostats.setHeatingSetpoint(D_temperatureH)
                    log.info "Set $D_thermostats Heat $D_temperatureH°"
                }
                else {
                    D_thermostats.setCoolingSetpoint(D_temperatureC)
                    log.info "Set $D_thermostats Cool $D_temperatureC°"
                }
               }
        }
        if (D_phrase) {
            location.helloHome.execute(D_phrase)
        }

        if (D_triggerMode && location.mode != D_triggerMode) {
            if (location.modes?.find{it.name == D_triggerMode}) {
                setLocationMode(D_triggerMode)
            }
            else {
                log.debug "Unable to change to undefined mode '${D_triggerMode}'"
            }
        }

        if (D_volume) {
                D_sonos.setLevel(D_volume)
        }

        if (D_alarmType == "2" || (D_alarmType == "1" && D_secondAlarm =="1")) {
            state.fullMsgD = ""
            if (D_wakeMsg) {
                   getGreeting(D_wakeMsg, 4)
            }

            if (D_weatherReport || D_humidity || D_includeTemp || D_localTemp) {
                getWeatherReport(4, D_weatherReport, D_humidity, D_includeTemp, D_localTemp)
            }

            if (D_includeSunrise || D_includeSunset) {
                getSunriseSunset(4, D_includeSunrise, D_includeSunset)
            }

            if ((D_switches || D_dimmers || D_thermostats) && D_confirmSwitches) {
                getOnConfimation(D_switches, D_dimmers, D_thermostats, 4)
               }

            if (D_phrase && D_confirmPhrase) {
                getPhraseConfirmation(4, D_phrase)
            }

            if (D_triggerMode && D_confirmMode){
                getModeConfirmation(D_triggerMode, 4)
            }

            state.soundD = textToSpeech(state.fullMsgD, true)
        }

        if (D_alarmType == "1"){
            if (D_secondAlarm == "1" && state.soundAlarmD){
                D_sonos.playSoundAndTrack (state.soundAlarmD.uri, state.soundAlarmD.duration, state.soundD.uri)
            }
            if (D_secondAlarm == "2" && state.selectedSongD && state.soundAlarmD){
                D_sonos.playSoundAndTrack (state.soundAlarmD.uri, state.soundAlarmD.duration, state.selectedSongD)
            }
            if (!D_secondAlarm){
                D_sonos.playTrack(state.soundAlarmD.uri)
            }
        }

        if (D_alarmType == "2") {
            if (D_secondAlarmMusic && state.selectedSongD){
                D_sonos.playSoundAndTrack (state.soundD.uri, state.soundD.duration, state.selectedSongD)
            }
            else {
                D_sonos.playTrack(state.soundD.uri)
            }
        }

        if (D_alarmType == "3") {
               D_sonos.playTrack(state.selectedSongD)
        }
    }
}

def appTouchHandler(evt){
    if (!summaryMode || summaryMode.contains(location.mode)) {
        state.summaryMsg = "The following is a summary of the alarm settings. "
        getSummary (A_alarmOn, ScenarioNameA, A_timeStart, 1)
        getSummary (B_alarmOn, ScenarioNameB, B_timeStart, 2)
        getSummary (C_alarmOn, ScenarioNameC, C_timeStart, 3)
        getSummary (D_alarmOn, ScenarioNameD, D_timeStart, 4)

        log.debug "Summary message = ${state.summaryMsg}"
        def summarySound = textToSpeech(state.summaryMsg, true)
        if (summaryVolume) {
            summarySonos.setLevel(summaryVolume)
        }
        summarySonos.playTrack(summarySound.uri)
    }
}

def getSummary (alarmOn, scenarioName, timeStart, num){
    if (alarmOn && scenarioName) {
        state.summaryMsg = "${state.summaryMsg} Alarm ${num}, ${scenarioName}, set for ${parseDate(timeStart,"", "h:mm a" )}, is enabled. "
    }
    else if (summaryDisabled && !alarmOn && scenarioName) {
        state.summaryMsg = "${state.summaryMsg} Alarm ${num}, ${scenarioName}, set for ${parseDate(timeStart,"", "h:mm a")}, is disabled. "
    }
    else if (summaryDisabled && !scenarioName) {
        state.summaryMsg = "${state.summaryMsg} Alarm ${num} is not configured. "
    }
}

//--------------------------------------

def getDesc(timeStart, sonos, day, mode) {
    def desc = "Tap to set alarm"
    if (timeStart) {
        desc = "Alarm set to " + parseDate(timeStart,"", "h:mm a") +" on ${sonos}"

        def dayListSize = day ? day.size() : 7

        if (day && dayListSize < 7) {
            desc = desc + " on"
            for (dayName in day) {
                 desc = desc + " ${dayName}"
                dayListSize = dayListSize -1
                if (dayListSize) {
                    desc = "${desc}, "
                }
            }
        }
        else {
            desc = desc + " every day"
        }

        if (mode) {
            def modeListSize = mode.size()
            def modePrefix =" in the following modes: "
            if (modeListSize == 1) {
                modePrefix = " in the following mode: "
            }
            desc = desc + "${modePrefix}"
               for (modeName in mode) {
                desc = desc + "'${modeName}'"
                modeListSize = modeListSize -1
                if (modeListSize) {
                    desc = "${desc}, "
                }
                else {
                    desc = "${desc}"
                }
            }
        }
         else {
            desc = desc + " in all modes"
        }
    }
    desc
}
def greyOut(scenario, sonos, alarmTime, alarmOn, alarmType){
    def result = scenario && sonos  && alarmTime && alarmOn && alarmType ? "complete" : ""
}

def greyOut1(param1, param2, param3, param4, param5, param6){
    def result = param1 || param2 || param3 || param4 || param5 || param6 ? "complete" : ""
}

def getWeatherDesc(param1, param2, param3, param4, param5, param6) {
    def title = param1 || param2 || param3 || param4 || param5 || param6 ? "Tap to edit weather reporting options" : "Tap to setup weather reporting options"
}

def greyOutOption(param){
    def result = param ? "complete" : ""
}

def getTitle(scenario, num) {
    def title = scenario ? scenario : "Alarm ${num} not configured"
}

def dimmerDesc(dimmer){
    def desc = dimmer ? "Tap to edit dimmer settings" : "Tap to set dimmer setting"
}

def thermostatDesc(thermostat, heating, cooling){
    def tempText
    if (heating || cooling){
        if (heating){
            tempText = "${heating} heat"
        }
        if (cooling){
            tempText = "${cooling} cool"
        }
        if (heating && cooling) {
            tempText ="${heating} heat / ${cooling} cool"
        }
    }
    else {
        tempText="Tap to edit thermostat settings"
    }

    def desc = thermostat ? "${tempText}" : "Tap to set thermostat settings"
    return desc
}

private getDayOk(dayList) {
    def result = true
    if (dayList) {
        result = dayList.contains(getDay())
    }
    result
}

private getDay(){
    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())
}

private parseDate(date, epoch, type){
    def parseDate = ""
    if (epoch){
        long longDate = Long.valueOf(epoch).longValue()
        parseDate = new Date(longDate).format("yyyy-MM-dd'T'HH:mm:ss.SSSZ", location.timeZone)
    }
    else {
        parseDate = date
    }
    new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", parseDate).format("${type}", timeZone(parseDate))
}

private getSunriseSunset(scenario, includeSunrise, includeSunset){
    if (location.timeZone || zipCode) {
        def todayDate = new Date()
        def s = getSunriseAndSunset(zipcode: zipCode, date: todayDate)
        def riseTime = parseDate("", s.sunrise.time, "h:mm a")
        def setTime = parseDate ("", s.sunset.time, "h:mm a")
           def msg = ""
        def currTime = now()
        def verb1 = currTime >= s.sunrise.time ? "rose" : "will rise"
        def verb2 = currTime >= s.sunset.time ? "set" : "will set"

        if (includeSunrise && includeSunset) {
            msg = "The sun ${verb1} this morning at ${riseTime} and ${verb2} at ${setTime}. "
        }
        else if (includeSunrise && !includeSunset) {
            msg = "The sun ${verb1} this morning at ${riseTime}. "
        }
        else if (!includeSunrise && includeSunset) {
            msg = "The sun ${verb2} tonight at ${setTime}. "
        }
        compileMsg(msg, scenario)
    }
    else {
        msg = "Please set the location of your hub with the SmartThings mobile app, or enter a zip code to receive sunset and sunrise information. "
        compileMsg(msg, scenario)
    }
}

private getGreeting(msg, scenario) {
    def day = getDay()
    def time = parseDate("", now(), "h:mm a")
    def month = parseDate("", now(), "MMMM")
    def year = parseDate("", now(), "yyyy")
    def dayNum = parseDate("", now(), "dd")
    msg = msg.replace('%day%', day)
    msg = msg.replace('%date%', "${month} ${dayNum}, ${year}")
    msg = msg.replace('%time%', "${time}")
    msg = "${msg} "
    compileMsg(msg, scenario)
}

private getWeatherReport(scenario, weatherReport, humidity, includeTemp, localTemp) {
    if (location.timeZone || zipCode) {
        def isMetric = location.temperatureScale == "C"
        def sb = new StringBuilder()

        if (includeTemp){
            def current = getTwcConditions(zipCode)
            sb << "The current temperature is ${Math.round(current.temperature)} degrees. "
        }

        if (localTemp){
            sb << "The local temperature is ${Math.round(localTemp.currentTemperature)} degrees. "
        }

        if (humidity) {
            sb << "The local relative humidity is ${humidity.currentValue("humidity")}%. "
        }

        if (weatherReport) {
            def weather = getTwcForecast(zipCode)
            sb << "Today's forecast is "
            sb << weather.daypart[0].narrative[0]
        }

        def msg = sb.toString()
        msg = msg.replaceAll(/([0-9]+)C/,'$1 degrees')
        msg = msg.replaceAll(/([0-9]+)F/,'$1 degrees')
        compileMsg(msg, scenario)
    }
    else {
        msg = "Please set the location of your hub with the SmartThings mobile app, or enter a zip code to receive weather forecasts."
        compileMsg(msg, scenario)
    }
}

private getOnConfimation(switches, dimmers, thermostats, scenario) {
    def msg = ""
    if ((switches || dimmers) && !thermostats) {
        msg = "All switches"
    }
    if (!switches && !dimmers && thermostats) {
        msg = "All Thermostats"
    }
    if ((switches || dimmers) && thermostats) {
        msg = "All switches and thermostats"
    }
    msg = "${msg} are now on and set. "
    compileMsg(msg, scenario)
}

private getPhraseConfirmation(scenario, phrase) {
    def msg="The Smart Things Hello Home phrase, ${phrase}, has been activated. "
    compileMsg(msg, scenario)
}

private getModeConfirmation(mode, scenario) {
    def msg="The Smart Things mode is now being set to, ${mode}. "
    compileMsg(msg, scenario)
}

private compileMsg(msg, scenario) {
    log.debug "msg = ${msg}"
    if (scenario == 1) {state.fullMsgA = state.fullMsgA + "${msg}"}
    if (scenario == 2) {state.fullMsgB = state.fullMsgB + "${msg}"}
    if (scenario == 3) {state.fullMsgC = state.fullMsgC + "${msg}"}
    if (scenario == 4) {state.fullMsgD = state.fullMsgD + "${msg}"}
}

private alarmSoundUri(selection, length, scenario){
    def soundUri = ""
       def soundLength = ""
    switch(selection) {
        case "1":
            soundLength = length >0 && length < 8 ? length : 8
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmAlien.mp3", duration: "${soundLength}"]
            break
        case "2":
            soundLength = length >0 && length < 12 ? length : 12
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmBell.mp3", duration: "${soundLength}"]
            break
        case "3":
            soundLength = length >0 && length < 20 ? length : 20
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmBuzzer.mp3", duration: "${soundLength}"]
            break
        case "4":
            soundLength = length >0 && length < 20 ? length : 20
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmFire.mp3", duration: "${soundLength}"]
            break
        case "5":
            soundLength = length >0 && length < 2 ? length : 2
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmRooster.mp3", duration: "${soundLength}"]
            break
        case "6":
            soundLength = length >0 && length < 20 ? length : 20
            soundUri = [uri: "https://raw.githubusercontent.com/MichaelStruck/SmartThings/master/Other-SmartApps/Talking-Alarm-Clock/AlarmSounds/AlarmSiren.mp3", duration: "${soundLength}"]
            break
    }
    if (scenario == 1) {state.soundAlarmA = soundUri}
    if (scenario == 2) {state.soundAlarmB = soundUri}
    if (scenario == 3) {state.soundAlarmC = soundUri}
    if (scenario == 4) {state.soundAlarmD = soundUri}
}

//Sonos Aquire Track from SmartThings code
private songOptions(sonos, scenario) {
    if (sonos){
    // Make sure current selection is in the set
        def options = new LinkedHashSet()
        if (scenario == 1){
            if (state.selectedSongA?.station) {
                options << state.selectedSongA.station
            }
            else if (state.selectedSongA?.description) {
                options << state.selectedSongA.description
            }
        }
        if (scenario == 2){
            if (state.selectedSongB?.station) {
                options << state.selectedSongB.station
            }
            else if (state.selectedSongB?.description) {
                options << state.selectedSongB.description
            }
        }
        if (scenario == 3){
            if (state.selectedSongC?.station) {
                options << state.selectedSongC.station
            }
            else if (state.selectedSongC?.description) {
                options << state.selectedSongC.description
            }
        }
        if (scenario == 4){
            if (state.selectedSongD?.station) {
                options << state.selectedSongD.station
            }
            else if (state.selectedSongD?.description) {
                options << state.selectedSongD.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(sonos, song, scenario) {
    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) {
            if (scenario == 1) {state.selectedSongA = data}
            if (scenario == 2) {state.selectedSongB = data}
            if (scenario == 3) {state.selectedSongC = data}
            if (scenario == 4) {state.selectedSongD = data}
            log.debug "Selected song for Scenario ${scenario} = ${data}"
        }
        else if (song == state.selectedSongA?.station || song == state.selectedSongB?.station || song == state.selectedSongC?.station || song == state.selectedSongD?.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
    }
}

//Version/Copyright/Information/Help

private def textAppName() {
    def text = "Talking Alarm Clock"
}

private def textVersion() {
    def text = "Version 1.4.5 (06/17/2015)"
}

private def textCopyright() {
    def text = "Copyright © 2015 Michael Struck"
}

private def textLicense() {
    def text =
        "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"+
        "\n\n"+
        "    http://www.apache.org/licenses/LICENSE-2.0"+
        "\n\n"+
        "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."
}

private def textHelp() {
    def text =
        "Within each alarm scenario, choose a Sonos speaker, an alarm time and alarm type along with " +
        "switches, dimmers and thermostat to control when the alarm is triggered. Hello, Home phrases and modes can be triggered at alarm time. "+
        "You also have the option of setting up different alarm sounds, tracks and a personalized spoken greeting that can include a weather report. " +
        "Variables that can be used in the voice greeting include %day%, %time% and %date%.\n\n"+
        "From the main SmartApp convenience page, tapping the 'Talking Alarm Clock' icon (if enabled within the app) will "+
        "speak a summary of the alarms enabled or disabled without having to go into the application itself. This " +
        "functionality is optional and can be configured from the main setup page."
}
