mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-08 05:31:56 +00:00
1341 lines
60 KiB
Groovy
1341 lines
60 KiB
Groovy
/**
|
|
* 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"
|
|
)
|
|
|
|
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 = getWeatherFeature("conditions", zipCode)
|
|
if (isMetric) {
|
|
sb << "The current temperature is ${Math.round(current.current_observation.temp_c)} degrees. "
|
|
}
|
|
else {
|
|
sb << "The current temperature is ${Math.round(current.current_observation.temp_f)} 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 = getWeatherFeature("forecast", zipCode)
|
|
|
|
sb << "Today's forecast is "
|
|
if (isMetric) {
|
|
sb << weather.forecast.txt_forecast.forecastday[0].fcttext_metric
|
|
}
|
|
else {
|
|
sb << weather.forecast.txt_forecast.forecastday[0].fcttext
|
|
}
|
|
}
|
|
|
|
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."
|
|
}
|
|
|