/* HubiThings Replica Samsung Soundbar Driver HubiThings Replica Applications Copyright 2023 by Bloodtick HubiThings Replica Samsung Soundbar Copyright 2023 by Dave Gutheinz 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. Changes: 1.0-1: a. Changed link to help file b. Added logging prefs to library logging. Issues with this driver: Contact davegut via Private Message on the Hubitat Community site: https://community.hubitat.com/ ==========================================================================*/ import groovy.json.JsonOutput def driverVer() { return "1.0-1" } def appliance() { return "ReplicaSamsungSoundbar" } metadata { definition (name: appliance(), namespace: "replica", author: "David Gutheinz", importUrl: "https://raw.githubusercontent.com/DaveGut/HubithingsReplica/main/Drivers/${appliance()}.groovy" ){ capability "Switch" capability "MediaInputSource" command "toggleInputSource" capability "MediaTransport" capability "AudioVolume" attribute "audioTrackData", "JSON_OBJECT" attribute "trackDescription", "STRING" capability "Refresh" capability "Configuration" attribute "healthStatus", "enum", ["offline", "online"] // ===== Audio Notification Function ===== capability "AudioNotification" // Test Phase Only command "testAudioNotify" } preferences { // ===== Audio Notification Preferences ===== // if !deviceIp, SmartThings Notify, else Local Notify // Alt TTS Available under all conditions input ("altTts", "bool", title: "Use Alternate TTS Method", defalutValue: false) if (altTts) { input ("ttsApiKey", "string", title: "TTS Site Key", defaultValue: null, description: "From http://www.voicerss.org/registration.aspx") input ("ttsLang", "enum", title: "TTS Language", options: ttsLanguages(), defaultValue: "en-us") } input ("deviceIp", "string", title: "Device IP. For Local Notification.") } } String helpLogo() { return """Soundbar Help""" } def installed() { runIn(1, updated) } def updated() { unschedule() def updStatus = [:] initialize() if (!getDataValue("driverVersion") || getDataValue("driverVersion") != driverVer()) { updateDataValue("driverVersion", driverVer()) updStatus << [driverVer: driverVer()] } if (logEnable) { runIn(1800, debugLogOff) } if (traceLog) { runIn(600, traceLogOff) } updStatus << [logEnable: logEnable, infoLog: infoLog, traceLog: traceLog] clearQueue() runEvery15Minutes(kickStartQueue) runIn(10, refresh) pauseExecution(5000) listAttributes(true) logInfo("updated: ${updStatus}") } def initialize() { updateDataValue("triggers", groovy.json.JsonOutput.toJson(getReplicaTriggers())) updateDataValue("commands", groovy.json.JsonOutput.toJson(getReplicaCommands())) logInfo("initialize: initialize device-specific data") } // ===== HubiThings Device Settings ===== Map getReplicaCommands() { return ([ "replicaEvent":[[name:"parent*",type:"OBJECT"],[name:"event*",type:"JSON_OBJECT"]], "replicaStatus":[[name:"parent*",type:"OBJECT"],[name:"event*",type:"JSON_OBJECT"]], "setHealthStatusValue":[[name:"healthStatus*",type:"ENUM"]]]) } Map getReplicaTriggers() { def replicaTriggers = [ off:[], on:[], setInputSource: [[name:"inputName*", type: "STRING"]], play:[], pause:[], stop:[], volumeUp:[], volumeDown:[], setVolume: [[name:"volumelevel*", type: "NUMBER"]], mute:[], unmute:[], playTrack:[ [name:"trackuri*", type: "STRING"], [name:"volumelevel", type:"NUMBER", data:"volumelevel"]], playTrackAndRestore:[ [name:"trackuri*", type: "STRING"], [name:"volumelevel", type:"NUMBER", data:"volumelevel"]], playTrackAndResume:[ [name:"trackuri*", type: "STRING"], [name:"volumelevel", type:"NUMBER", data:"volumelevel"]], refresh:[], deviceRefresh:[] ] return replicaTriggers } def configure() { initialize() setReplicaRules() sendCommand("configure") logInfo("configure: configuring default rules") } String setReplicaRules() { def rules = """{"version":1,"components":[{"trigger":{"type":"attribute","properties":{"value":{"title":"HealthState","type":"string"}},"additionalProperties":false,"required":["value"],"capability":"healthCheck","attribute":"healthStatus","label":"attribute: healthStatus.*"},"command":{"name":"setHealthStatusValue","label":"command: setHealthStatusValue(healthStatus*)","type":"command","parameters":[{"name":"healthStatus*","type":"ENUM"}]},"type":"smartTrigger"}, {"trigger":{"name":"deviceRefresh","label":"command: deviceRefresh()","type":"command"},"command":{"name":"refresh","type":"command","capability":"refresh","label":"command: refresh()"},"type":"hubitatTrigger"}, {"trigger":{"name":"mute","label":"command: mute()","type":"command"},"command":{"name":"mute","type":"command","capability":"audioMute","label":"command: mute()"},"type":"hubitatTrigger"}, {"trigger":{"name":"off","label":"command: off()","type":"command"},"command":{"name":"off","type":"command","capability":"switch","label":"command: off()"},"type":"hubitatTrigger"}, {"trigger":{"name":"on","label":"command: on()","type":"command"},"command":{"name":"on","type":"command","capability":"switch","label":"command: on()"},"type":"hubitatTrigger"}, {"trigger":{"name":"pause","label":"command: pause()","type":"command"},"command":{"name":"pause","type":"command","capability":"mediaPlayback","label":"command: pause()"},"type":"hubitatTrigger"}, {"trigger":{"name":"play","label":"command: play()","type":"command"},"command":{"name":"play","type":"command","capability":"mediaPlayback","label":"command: play()"},"type":"hubitatTrigger"}, {"trigger":{"name":"playTrack","label":"command: playTrack(trackuri*, volumelevel)","type":"command","parameters":[{"name":"trackuri*","type":"STRING"},{"name":"volumelevel","type":"NUMBER","data":"volumelevel"}]},"command":{"name":"playTrack","arguments":[{"name":"uri","optional":false,"schema":{"title":"URI","type":"string","format":"uri"}},{"name":"level","optional":true,"schema":{"type":"integer","minimum":0,"maximum":100}}],"type":"command","capability":"audioNotification","label":"command: playTrack(uri*, level)"},"type":"hubitatTrigger"}, {"trigger":{"name":"playTrackAndRestore","label":"command: playTrackAndRestore(trackuri*, volumelevel)","type":"command","parameters":[{"name":"trackuri*","type":"STRING"},{"name":"volumelevel","type":"NUMBER","data":"volumelevel"}]},"command":{"name":"playTrackAndRestore","arguments":[{"name":"uri","optional":false,"schema":{"title":"URI","type":"string","format":"uri"}},{"name":"level","optional":true,"schema":{"type":"integer","minimum":0,"maximum":100}}],"type":"command","capability":"audioNotification","label":"command: playTrackAndRestore(uri*, level)"},"type":"hubitatTrigger"}, {"trigger":{"name":"playTrackAndResume","label":"command: playTrackAndResume(trackuri*, volumelevel)","type":"command","parameters":[{"name":"trackuri*","type":"STRING"},{"name":"volumelevel","type":"NUMBER","data":"volumelevel"}]},"command":{"name":"playTrackAndResume","arguments":[{"name":"uri","optional":false,"schema":{"title":"URI","type":"string","format":"uri"}},{"name":"level","optional":true,"schema":{"type":"integer","minimum":0,"maximum":100}}],"type":"command","capability":"audioNotification","label":"command: playTrackAndResume(uri*, level)"},"type":"hubitatTrigger"}, {"trigger":{"name":"refresh","label":"command: refresh()","type":"command"},"command":{"name":"refresh","type":"command","capability":"refresh","label":"command: refresh()"},"type":"hubitatTrigger"}, {"trigger":{"name":"setVolume","label":"command: setVolume(volumelevel*)","type":"command","parameters":[{"name":"volumelevel*","type":"NUMBER"}]},"command":{"name":"setVolume","arguments":[{"name":"volume","optional":false,"schema":{"type":"integer","minimum":0,"maximum":100}}],"type":"command","capability":"audioVolume","label":"command: setVolume(volume*)"},"type":"hubitatTrigger"}, {"trigger":{"name":"setInputSource","label":"command: setInputSource(inputSource*)","type":"command","parameters":[{"name":"inputName*","type":"string"}]},"command":{"name":"setInputSource","arguments":[{"name":"mode","optional":false,"schema":{"title":"MediaSource","enum":["AM","CD","FM","HDMI","HDMI1","HDMI2","HDMI3","HDMI4","HDMI5","HDMI6","digitalTv","USB","YouTube","aux","bluetooth","digital","melon","wifi"],"type":"string"}}],"type":"command","capability":"mediaInputSource","label":"command: setInputSource(mode*)"},"type":"hubitatTrigger"}, {"trigger":{"name":"stop","label":"command: stop()","type":"command"},"command":{"name":"stop","type":"command","capability":"mediaPlayback","label":"command: stop()"},"type":"hubitatTrigger"}, {"trigger":{"name":"unmute","label":"command: unmute()","type":"command"},"command":{"name":"unmute","type":"command","capability":"audioMute","label":"command: unmute()"},"type":"hubitatTrigger"}, {"trigger":{"name":"volumeDown","label":"command: volumeDown()","type":"command"},"command":{"name":"volumeDown","type":"command","capability":"audioVolume","label":"command: volumeDown()"},"type":"hubitatTrigger"}, {"trigger":{"name":"volumeUp","label":"command: volumeUp()","type":"command"},"command":{"name":"volumeUp","type":"command","capability":"audioVolume","label":"command: volumeUp()"},"type":"hubitatTrigger"}]}""" updateDataValue("rules", rules) } // ===== Event Parse Interface s===== void replicaStatus(def parent=null, Map status=null) { def logData = [parent: parent, status: status] if (state.refreshAttributes) { refreshAttributes(status.components.main) } logTrace("replicaStatus: ${logData}") } def refreshAttributes(mainData) { logDebug("refreshAttributes: ${mainData}") def value try { value = mainData.mediaInputSource.supportedInputSources.value } catch(e) { value = ["n/a"] pauseExecution(200) } parse_main([attribute: "supportedInputSources", value: value]) pauseExecution(200) parse_main([attribute: "switch", value: mainData.switch.switch.value]) pauseExecution(200) parse_main([attribute: "volume", value: mainData.audioVolume.volume.value.toInteger(), unit: "%"]) pauseExecution(200) parse_main([attribute: "mute", value: mainData.audioMute.mute.value]) pauseExecution(200) parse_main([attribute: "playbackStatus", value: mainData.mediaPlayback.playbackStatus.value]) pauseExecution(200) try { value = mainData.mediaInputSource.inputSource.value } catch(e) { value = "n/a" } parse_main([attribute: "inputSource", value: value]) pauseExecution(200) try { value = mainData.audioTrackData.audioTrackData.value } catch(e) { value = "n/a" } parse_main([attribute: "audioTrackData", value: value]) state.refreshAttributes = false } void replicaHealth(def parent=null, Map health=null) { if(parent) { logInfo("replicaHealth: ${parent?.getLabel()}") } if(health) { logInfo("replicaHealth: ${health}") } } void replicaEvent(def parent=null, Map event=null) { logDebug("replicaEvent: [parent: ${parent}, event: ${event}]") def eventData = event.deviceEvent try { "parse_${event.deviceEvent.componentId}"(event.deviceEvent) } catch (err) { logWarn("replicaEvent: [event = ${event}, error: ${err}") } } def parse_main(event) { logInfo("parse_main: [attribute: ${event.attribute}, value: ${event.value}, unit: ${event.unit}]") switch(event.attribute) { case "switch": case "mute": sendEvent(name: event.attribute, value: event.value) break case "audioTrackData": sendEvent(name: event.attribute, value: event.value) def title = " " if (event.value != "n/a" && event.value.title != null) { title = event.value.title } sendEvent(name: "trackDescription", value: title) break case "volume": sendEvent(name: event.attribute, value: event.value) sendEvent(name: "level", value: event.value) break case "inputSource": if (event.capability == "mediaInputSource") { sendEvent(name: "mediaInputSource", value: event.value) } break case "playbackStatus": sendEvent(name: "transportStatus", value: event.value) break case "supportedInputSources": state.inputSources = event.value break default: logDebug("parse_main: [unhandledEvent: ${event}]") break } logTrace("parse_main: [event: ${event}]") } // ===== HubiThings Send Command and Device Health ===== def setHealthStatusValue(value) { sendEvent(name: "healthStatus", value: value) } private def sendCommand(String name, def value=null, String unit=null, data=[:]) { parent?.deviceTriggerHandler(device, [name:name, value:value, unit:unit, data:data, now:now]) } def refresh() { state.refreshAttributes = true sendCommand("deviceRefresh") pauseExecution(500) sendCommand("refresh") } def deviceRefresh() { sendCommand("deviceRefresh") } // ===== Samsung Soundbar Commands ===== def on() { sendCommand("on") } def off() { sendCommand("off") } def setAttrSwitch(onOff) { sendEvent(name: "switch", value: onOff) } // ===== Media Input Source ===== def toggleInputSource() { if (state.inputSources) { def inputSources = state.inputSources def totalSources = inputSources.size() def currentSource = device.currentValue("mediaInputSource") def sourceNo = inputSources.indexOf(currentSource) def newSourceNo = sourceNo + 1 if (newSourceNo == totalSources) { newSourceNo = 0 } def inputSource = inputSources[newSourceNo] setInputSource(inputSource) } else { logWarn("toggleInputSource: [status: FAILED, reason: no state.inputSources, correction: try running refresh]") } } def setInputSource(inputSource) { if (inputSource == "n/a") { logWarn("setInputSource: [status: FAILED, reason: ST Device does not support input source]") } else { def inputSources = state.inputSources if (inputSources == null) { logWarn("setInputSource: [status: FAILED, reason: no state.inputSources, correction: try running refresh]") } else if (state.inputSources.contains(inputSource)) { sendCommand("setInputSource", inputSource) } else { logWarn("setInputSource: [status: FAILED, inputSource: ${inputSource}, inputSources: ${inputSources}]") } } } // ===== Media Transport ===== def play() { sendCommand("play") runIn(5, deviceRefresh) } def pause() { sendCommand("pause") runIn(5, deviceRefresh) } def stop() { sendCommand("stop") runIn(5, deviceRefresh) } // ===== Audio Volume ===== def volumeUp() { sendCommand("volumeUp") } def volumeDown() { sendCommand("volumeDown") } def setVolume(volume) { if (volume == null) { volume = device.currentValue("volume").toInteger() } if (volume < 0) { volume = 0 } else if (volume > 100) { volume = 100 } sendCommand("setVolume", volume) } def mute() { sendCommand("mute") } def unmute() { sendCommand("unmute") } // ===== Libraries ===== // ~~~~~ start include (1312) davegut.samsungAudioNotify ~~~~~ library ( // library marker davegut.samsungAudioNotify, line 1 name: "samsungAudioNotify", // library marker davegut.samsungAudioNotify, line 2 namespace: "davegut", // library marker davegut.samsungAudioNotify, line 3 author: "Dave Gutheinz", // library marker davegut.samsungAudioNotify, line 4 description: "Samsung Audio Notify Functions", // library marker davegut.samsungAudioNotify, line 5 category: "utilities", // library marker davegut.samsungAudioNotify, line 6 documentationLink: "" // library marker davegut.samsungAudioNotify, line 7 ) // library marker davegut.samsungAudioNotify, line 8 import org.json.JSONObject // library marker davegut.samsungAudioNotify, line 9 // ===== Voices.rss TTS Languages ===== // library marker davegut.samsungAudioNotify, line 11 def ttsLanguages() { // library marker davegut.samsungAudioNotify, line 12 def languages = [ // library marker davegut.samsungAudioNotify, line 13 "en-au":"English (Australia)","en-ca":"English (Canada)", // library marker davegut.samsungAudioNotify, line 14 "en-gb":"English (Great Britain)","en-us":"English (United States)", // library marker davegut.samsungAudioNotify, line 15 "en-in":"English (India)","ca-es":"Catalan","zh-cn":"Chinese (China)", // library marker davegut.samsungAudioNotify, line 16 "zh-hk":"Chinese (Hong Kong)","zh-tw":"Chinese (Taiwan)", // library marker davegut.samsungAudioNotify, line 17 "da-dk":"Danish", "nl-nl":"Dutch","fi-fi":"Finnish", // library marker davegut.samsungAudioNotify, line 18 "fr-ca":"French (Canada)","fr-fr":"French (France)","de-de":"German", // library marker davegut.samsungAudioNotify, line 19 "it-it":"Italian","ja-jp":"Japanese","ko-kr":"Korean", // library marker davegut.samsungAudioNotify, line 20 "nb-no":"Norwegian","pl-pl":"Polish","pt-br":"Portuguese (Brazil)", // library marker davegut.samsungAudioNotify, line 21 "pt-pt":"Portuguese (Portugal)","ru-ru":"Russian", // library marker davegut.samsungAudioNotify, line 22 "es-mx":"Spanish (Mexico)","es-es":"Spanish (Spain)", // library marker davegut.samsungAudioNotify, line 23 "sv-se":"Swedish (Sweden)"] // library marker davegut.samsungAudioNotify, line 24 return languages // library marker davegut.samsungAudioNotify, line 25 } // library marker davegut.samsungAudioNotify, line 26 // ===== Audio Notification / URL-URI Playback functions // library marker davegut.samsungAudioNotify, line 28 def testAudioNotify() { // library marker davegut.samsungAudioNotify, line 29 logInfo("testAudioNotify: Testing audio notification interfaces") // library marker davegut.samsungAudioNotify, line 30 runIn(3, testTextNotify) // library marker davegut.samsungAudioNotify, line 31 playTrack("""http://s3.amazonaws.com/smartapp-media/sonos/dogs.mp3""", 8) // library marker davegut.samsungAudioNotify, line 32 } // library marker davegut.samsungAudioNotify, line 33 def testTextNotify() { // library marker davegut.samsungAudioNotify, line 34 playText("This is a test of the Text-to-speech function", 9) // library marker davegut.samsungAudioNotify, line 35 } // library marker davegut.samsungAudioNotify, line 36 def playText(text, volume = null) { // library marker davegut.samsungAudioNotify, line 38 createTextData(text, volume, true, "playText") // library marker davegut.samsungAudioNotify, line 39 } // library marker davegut.samsungAudioNotify, line 40 def playTextAndRestore(text, volume = null) { // library marker davegut.samsungAudioNotify, line 42 createTextData(text, volume, false, "playTextAndRestore") // library marker davegut.samsungAudioNotify, line 43 } // library marker davegut.samsungAudioNotify, line 44 def playTextAndResume(text, volume = null) { // library marker davegut.samsungAudioNotify, line 46 createTextData(text, volume, true, "playTextAndResume") // library marker davegut.samsungAudioNotify, line 47 } // library marker davegut.samsungAudioNotify, line 48 def createTextData(text, volume, resume, method) { // library marker davegut.samsungAudioNotify, line 50 if (volume == null) { volume = device.currentValue("volume") } // library marker davegut.samsungAudioNotify, line 51 def logData = [method: method, text: text, volume: volume, resume: resume] // library marker davegut.samsungAudioNotify, line 52 def trackUri // library marker davegut.samsungAudioNotify, line 53 def duration // library marker davegut.samsungAudioNotify, line 54 text = " ${text}." // library marker davegut.samsungAudioNotify, line 55 if (altTts) { // library marker davegut.samsungAudioNotify, line 56 if (ttsApiKey == null) { // library marker davegut.samsungAudioNotify, line 57 logWarn("convertToTrack: [FAILED: No ttsApiKey]") // library marker davegut.samsungAudioNotify, line 58 } else { // library marker davegut.samsungAudioNotify, line 59 def uriText = URLEncoder.encode(text, "UTF-8").replaceAll(/\+/, "%20") // library marker davegut.samsungAudioNotify, line 60 trackUri = "http://api.voicerss.org/?" + // library marker davegut.samsungAudioNotify, line 61 "key=${ttsApiKey.trim()}" + // library marker davegut.samsungAudioNotify, line 62 "&c=MP3" + // library marker davegut.samsungAudioNotify, line 63 "&hl=${ttsLang}" + // library marker davegut.samsungAudioNotify, line 64 "&src=${uriText}" // library marker davegut.samsungAudioNotify, line 65 duration = (2 + text.length() / 10).toInteger() // library marker davegut.samsungAudioNotify, line 66 track = [uri: trackUri, duration: duration] // library marker davegut.samsungAudioNotify, line 67 } // library marker davegut.samsungAudioNotify, line 68 } else { // library marker davegut.samsungAudioNotify, line 69 def track = textToSpeech(text, voice) // library marker davegut.samsungAudioNotify, line 70 trackUri = track.uri // library marker davegut.samsungAudioNotify, line 71 duration = track.duration // library marker davegut.samsungAudioNotify, line 72 } // library marker davegut.samsungAudioNotify, line 73 logData << [trackUri: trackUri, duration: duration] // library marker davegut.samsungAudioNotify, line 74 // addToQueue(trackUri, duration, volume, resume) // library marker davegut.samsungAudioNotify, line 75 addToQueue(trackUri, duration, volume) // library marker davegut.samsungAudioNotify, line 76 logInfo("createTextData: ${logData}") // library marker davegut.samsungAudioNotify, line 77 } // library marker davegut.samsungAudioNotify, line 78 def playTrack(trackData, volume = null) { // library marker davegut.samsungAudioNotify, line 80 createPlayData(trackData, volume, true, "playTrack") // library marker davegut.samsungAudioNotify, line 81 } // library marker davegut.samsungAudioNotify, line 82 def playTrackAndRestore(trackData, volume=null) { // library marker davegut.samsungAudioNotify, line 84 createPlayData(trackData, volume, false, "playTrackAndRestore") // library marker davegut.samsungAudioNotify, line 85 } // library marker davegut.samsungAudioNotify, line 86 def playTrackAndResume(trackData, volume=null) { // library marker davegut.samsungAudioNotify, line 88 createPlayData(trackData, volume, true, "playTrackAndResume") // library marker davegut.samsungAudioNotify, line 89 } // library marker davegut.samsungAudioNotify, line 90 def createPlayData(trackData, volume, resume, method) { // library marker davegut.samsungAudioNotify, line 92 if (volume == null) { volume = device.currentValue("volume") } // library marker davegut.samsungAudioNotify, line 93 def logData = [method: method, trackData: text, volume: volume, resume: resume] // library marker davegut.samsungAudioNotify, line 94 def trackUri // library marker davegut.samsungAudioNotify, line 95 def duration // library marker davegut.samsungAudioNotify, line 96 if (trackData[0] == "[") { // library marker davegut.samsungAudioNotify, line 97 logData << [status: "aborted", reason: "trackData not formated as {uri: , duration: }"] // library marker davegut.samsungAudioNotify, line 98 } else { // library marker davegut.samsungAudioNotify, line 99 if (trackData[0] == "{") { // library marker davegut.samsungAudioNotify, line 100 trackData = new JSONObject(trackData) // library marker davegut.samsungAudioNotify, line 101 trackUri = trackData.uri // library marker davegut.samsungAudioNotify, line 102 duration = trackData.duration // library marker davegut.samsungAudioNotify, line 103 } else { // library marker davegut.samsungAudioNotify, line 104 trackUri = trackData // library marker davegut.samsungAudioNotify, line 105 duration = 15 // library marker davegut.samsungAudioNotify, line 106 } // library marker davegut.samsungAudioNotify, line 107 logData << [status: "addToQueue", trackData: trackData, volume: volume, resume: resume] // library marker davegut.samsungAudioNotify, line 108 // addToQueue(trackUri, duration, volume, resume) // library marker davegut.samsungAudioNotify, line 109 addToQueue(trackUri, duration, volume) // library marker davegut.samsungAudioNotify, line 110 } // library marker davegut.samsungAudioNotify, line 111 logDebug("createPlayData: ${logData}") // library marker davegut.samsungAudioNotify, line 112 } // library marker davegut.samsungAudioNotify, line 113 // ========== Play Queue Execution ========== // library marker davegut.samsungAudioNotify, line 115 def addToQueue(trackUri, duration, volume){ // library marker davegut.samsungAudioNotify, line 116 if(device.currentValue("switch") == "on") { // library marker davegut.samsungAudioNotify, line 117 def logData = [:] // library marker davegut.samsungAudioNotify, line 118 duration = duration + 3 // library marker davegut.samsungAudioNotify, line 119 playData = ["trackUri": trackUri, // library marker davegut.samsungAudioNotify, line 120 "duration": duration, // library marker davegut.samsungAudioNotify, line 121 "requestVolume": volume] // library marker davegut.samsungAudioNotify, line 122 state.playQueue.add(playData) // library marker davegut.samsungAudioNotify, line 123 logData << [addedToQueue: [uri: trackUri, duration: duration, volume: volume]] // library marker davegut.samsungAudioNotify, line 124 if (state.playingNotification == false) { // library marker davegut.samsungAudioNotify, line 126 runInMillis(100, startPlayViaQueue) // library marker davegut.samsungAudioNotify, line 127 } // library marker davegut.samsungAudioNotify, line 128 logDebug("addToQueue: ${logData}") // library marker davegut.samsungAudioNotify, line 129 } // library marker davegut.samsungAudioNotify, line 130 } // library marker davegut.samsungAudioNotify, line 131 def startPlayViaQueue() { // library marker davegut.samsungAudioNotify, line 133 logDebug("startPlayViaQueue: [queueSize: ${state.playQueue.size()}]") // library marker davegut.samsungAudioNotify, line 134 if (state.playQueue.size() == 0) { return } // library marker davegut.samsungAudioNotify, line 135 state.recoveryVolume = device.currentValue("volume") // library marker davegut.samsungAudioNotify, line 136 state.recoverySource = device.currentValue("mediaInputSource") // library marker davegut.samsungAudioNotify, line 137 state.playingNotification = true // library marker davegut.samsungAudioNotify, line 138 playViaQueue() // library marker davegut.samsungAudioNotify, line 139 } // library marker davegut.samsungAudioNotify, line 140 def playViaQueue() { // library marker davegut.samsungAudioNotify, line 142 def logData = [:] // library marker davegut.samsungAudioNotify, line 143 if (state.playQueue.size() == 0) { // library marker davegut.samsungAudioNotify, line 144 resumePlayer() // library marker davegut.samsungAudioNotify, line 145 logData << [status: "resumingPlayer", reason: "Zero Queue"] // library marker davegut.samsungAudioNotify, line 146 } else { // library marker davegut.samsungAudioNotify, line 147 def playData = state.playQueue.get(0) // library marker davegut.samsungAudioNotify, line 148 state.playQueue.remove(0) // library marker davegut.samsungAudioNotify, line 149 runInMillis(100, setVolume, [data:playData.requestVolume]) // library marker davegut.samsungAudioNotify, line 151 runInMillis(400, execPlay, [data: playData]) // library marker davegut.samsungAudioNotify, line 153 runIn(playData.duration, resumePlayer) // library marker davegut.samsungAudioNotify, line 154 runIn(30, kickStartQueue) // library marker davegut.samsungAudioNotify, line 155 logData << [playData: playData, recoveryVolume: recVolume] // library marker davegut.samsungAudioNotify, line 156 } // library marker davegut.samsungAudioNotify, line 157 logDebug("playViaQueue: ${logData}") // library marker davegut.samsungAudioNotify, line 158 } // library marker davegut.samsungAudioNotify, line 159 def execPlay(data) { // library marker davegut.samsungAudioNotify, line 161 if (deviceIp) { // library marker davegut.samsungAudioNotify, line 162 sendUpnpCmd("SetAVTransportURI", // library marker davegut.samsungAudioNotify, line 163 [InstanceID: 0, // library marker davegut.samsungAudioNotify, line 164 CurrentURI: data.trackUri, // library marker davegut.samsungAudioNotify, line 165 CurrentURIMetaData: ""]) // library marker davegut.samsungAudioNotify, line 166 pauseExecution(300) // library marker davegut.samsungAudioNotify, line 167 sendUpnpCmd("Play", // library marker davegut.samsungAudioNotify, line 168 ["InstanceID" :0, // library marker davegut.samsungAudioNotify, line 169 "Speed": "1"]) // library marker davegut.samsungAudioNotify, line 170 } else { // library marker davegut.samsungAudioNotify, line 171 sendCommand("playTrack", data.trackUri) // library marker davegut.samsungAudioNotify, line 172 } // library marker davegut.samsungAudioNotify, line 173 } // library marker davegut.samsungAudioNotify, line 174 def resumePlayer() { // library marker davegut.samsungAudioNotify, line 176 // should be able to recover data here. At least, recover the current value of the inputSource // library marker davegut.samsungAudioNotify, line 177 def logData = [:] // library marker davegut.samsungAudioNotify, line 178 if (state.playQueue.size() > 0) { // library marker davegut.samsungAudioNotify, line 179 logData << [status: "aborted", reason: "playQueue not 0"] // library marker davegut.samsungAudioNotify, line 180 playViaQueue() // library marker davegut.samsungAudioNotify, line 181 } else { // library marker davegut.samsungAudioNotify, line 182 state.playingNotification = false // library marker davegut.samsungAudioNotify, line 183 setVolume(state.recoveryVolume) // library marker davegut.samsungAudioNotify, line 184 def source = state.recoverySource // library marker davegut.samsungAudioNotify, line 185 if (source != "n/a") { // library marker davegut.samsungAudioNotify, line 186 setInputSource(source) // library marker davegut.samsungAudioNotify, line 187 } // library marker davegut.samsungAudioNotify, line 188 } // library marker davegut.samsungAudioNotify, line 189 logDebug("resumePlayer: ${logData}") // library marker davegut.samsungAudioNotify, line 190 } // library marker davegut.samsungAudioNotify, line 191 def kickStartQueue() { // library marker davegut.samsungAudioNotify, line 193 logInfo("kickStartQueue: [size: ${state.playQueue.size()}]") // library marker davegut.samsungAudioNotify, line 194 if (state.playQueue.size() > 0) { // library marker davegut.samsungAudioNotify, line 195 resumePlayer() // library marker davegut.samsungAudioNotify, line 196 } else { // library marker davegut.samsungAudioNotify, line 197 state.playingNotification = false // library marker davegut.samsungAudioNotify, line 198 } // library marker davegut.samsungAudioNotify, line 199 } // library marker davegut.samsungAudioNotify, line 200 def clearQueue() { // library marker davegut.samsungAudioNotify, line 202 logDebug("clearQueue") // library marker davegut.samsungAudioNotify, line 203 state.playQueue = [] // library marker davegut.samsungAudioNotify, line 204 state.playingNotification = false // library marker davegut.samsungAudioNotify, line 205 } // library marker davegut.samsungAudioNotify, line 206 private sendUpnpCmd(String action, Map body){ // library marker davegut.samsungAudioNotify, line 208 logDebug("sendUpnpCmd: upnpAction = ${action}, upnpBody = ${body}") // library marker davegut.samsungAudioNotify, line 209 def host = "${deviceIp}:9197" // library marker davegut.samsungAudioNotify, line 210 def hubCmd = new hubitat.device.HubSoapAction( // library marker davegut.samsungAudioNotify, line 211 path: "/upnp/control/AVTransport1", // library marker davegut.samsungAudioNotify, line 212 urn: "urn:schemas-upnp-org:service:AVTransport:1", // library marker davegut.samsungAudioNotify, line 213 action: action, // library marker davegut.samsungAudioNotify, line 214 body: body, // library marker davegut.samsungAudioNotify, line 215 headers: [Host: host, // library marker davegut.samsungAudioNotify, line 216 CONNECTION: "close"] // library marker davegut.samsungAudioNotify, line 217 ) // library marker davegut.samsungAudioNotify, line 218 sendHubCommand(hubCmd) // library marker davegut.samsungAudioNotify, line 219 } // library marker davegut.samsungAudioNotify, line 220 def upnpParse(resp) { // library marker davegut.samsungAudioNotify, line 222 resp = parseLanMessage(resp) // library marker davegut.samsungAudioNotify, line 223 if (resp.status != 200) { // library marker davegut.samsungAudioNotify, line 224 logWarn("upnpParse: [status: failed, data: ${resp}]") // library marker davegut.samsungAudioNotify, line 225 } // library marker davegut.samsungAudioNotify, line 226 } // library marker davegut.samsungAudioNotify, line 227 // Send parse to appropriate method for UPNP or WS! // library marker davegut.samsungAudioNotify, line 229 def parse(resp) { // library marker davegut.samsungAudioNotify, line 230 if (resp.toString().contains("mac:")) { // library marker davegut.samsungAudioNotify, line 231 upnpParse(resp) // library marker davegut.samsungAudioNotify, line 232 } else { // library marker davegut.samsungAudioNotify, line 233 parseWs(resp) // library marker davegut.samsungAudioNotify, line 234 } // library marker davegut.samsungAudioNotify, line 235 } // library marker davegut.samsungAudioNotify, line 236 /* ===== Sample Audio Notification URIs ===== // library marker davegut.samsungAudioNotify, line 239 [title: "Bell 1", uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"] // library marker davegut.samsungAudioNotify, line 240 [title: "Dogs Barking", uri: "http://s3.amazonaws.com/smartapp-media/sonos/dogs.mp3", duration: "10"] // library marker davegut.samsungAudioNotify, line 241 [title: "Fire Alarm", uri: "http://s3.amazonaws.com/smartapp-media/sonos/alarm.mp3", duration: "17"] // library marker davegut.samsungAudioNotify, line 242 [title: "The mail has arrived",uri: "http://s3.amazonaws.com/smartapp-media/sonos/the+mail+has+arrived.mp3", duration: "1"] // library marker davegut.samsungAudioNotify, line 243 [title: "A door opened", uri: "http://s3.amazonaws.com/smartapp-media/sonos/a+door+opened.mp3", duration: "1"] // library marker davegut.samsungAudioNotify, line 244 [title: "There is motion", uri: "http://s3.amazonaws.com/smartapp-media/sonos/there+is+motion.mp3", duration: "1"] // library marker davegut.samsungAudioNotify, line 245 [title: "Someone is arriving", uri: "http://s3.amazonaws.com/smartapp-media/sonos/someone+is+arriving.mp3", duration: "1"] // library marker davegut.samsungAudioNotify, line 246 ===== Some working Streaming Stations ===== // library marker davegut.samsungAudioNotify, line 247 [title:"Cafe del Mar", uri:"https://streams.radio.co/se1a320b47/listen", duration: 0] // library marker davegut.samsungAudioNotify, line 248 [title:"UT-KUTX", uri: "https://kut.streamguys1.com/kutx-web", duration: 0] // library marker davegut.samsungAudioNotify, line 249 [title:"89.7 FM Perth", uri: "https://ice8.securenetsystems.net/897FM", duration: 0] // library marker davegut.samsungAudioNotify, line 250 [title:"Euro1", uri:"https://streams.radio.co/se1a320b47/listen", duration: 0] // library marker davegut.samsungAudioNotify, line 251 [title:"Easy Hits Florida", uri:"http://airspectrum.cdnstream1.com:8114/1648_128", duration: 0] // library marker davegut.samsungAudioNotify, line 252 [title:"Austin Blues", uri:"http://158.69.131.71:8036/stream/1/", duration: 0] // library marker davegut.samsungAudioNotify, line 253 */ // library marker davegut.samsungAudioNotify, line 254 // ~~~~~ end include (1312) davegut.samsungAudioNotify ~~~~~ // ~~~~~ start include (1303) davegut.Logging ~~~~~ library ( // library marker davegut.Logging, line 1 name: "Logging", // library marker davegut.Logging, line 2 namespace: "davegut", // library marker davegut.Logging, line 3 author: "Dave Gutheinz", // library marker davegut.Logging, line 4 description: "Common Logging Methods", // library marker davegut.Logging, line 5 category: "utilities", // library marker davegut.Logging, line 6 documentationLink: "" // library marker davegut.Logging, line 7 ) // library marker davegut.Logging, line 8 preferences { // library marker davegut.Logging, line 10 input ("logEnable", "bool", title: "Enable debug logging for 30 minutes", defaultValue: false) // library marker davegut.Logging, line 11 input ("infoLog", "bool", title: "Enable information logging${helpLogo()}",defaultValue: true) // library marker davegut.Logging, line 12 input ("traceLog", "bool", title: "Enable trace logging as directed by developer", defaultValue: false) // library marker davegut.Logging, line 13 } // library marker davegut.Logging, line 14 // Logging during development // library marker davegut.Logging, line 16 def listAttributes(trace = false) { // library marker davegut.Logging, line 17 def attrs = device.getSupportedAttributes() // library marker davegut.Logging, line 18 def attrList = [:] // library marker davegut.Logging, line 19 attrs.each { // library marker davegut.Logging, line 20 def val = device.currentValue("${it}") // library marker davegut.Logging, line 21 attrList << ["${it}": val] // library marker davegut.Logging, line 22 } // library marker davegut.Logging, line 23 if (trace == true) { // library marker davegut.Logging, line 24 logInfo("Attributes: ${attrList}") // library marker davegut.Logging, line 25 } else { // library marker davegut.Logging, line 26 logDebug("Attributes: ${attrList}") // library marker davegut.Logging, line 27 } // library marker davegut.Logging, line 28 } // library marker davegut.Logging, line 29 def logTrace(msg){ // library marker davegut.Logging, line 31 if (traceLog == true) { // library marker davegut.Logging, line 32 log.trace "${device.displayName}-${driverVer()}: ${msg}" // library marker davegut.Logging, line 33 } // library marker davegut.Logging, line 34 } // library marker davegut.Logging, line 35 def traceLogOff() { // library marker davegut.Logging, line 37 device.updateSetting("traceLog", [type:"bool", value: false]) // library marker davegut.Logging, line 38 logInfo("traceLogOff") // library marker davegut.Logging, line 39 } // library marker davegut.Logging, line 40 def logInfo(msg) { // library marker davegut.Logging, line 42 if (textEnable || infoLog) { // library marker davegut.Logging, line 43 log.info "${device.displayName}-${driverVer()}: ${msg}" // library marker davegut.Logging, line 44 } // library marker davegut.Logging, line 45 } // library marker davegut.Logging, line 46 def debugLogOff() { // library marker davegut.Logging, line 48 device.updateSetting("logEnable", [type:"bool", value: false]) // library marker davegut.Logging, line 49 logInfo("debugLogOff") // library marker davegut.Logging, line 50 } // library marker davegut.Logging, line 51 def logDebug(msg) { // library marker davegut.Logging, line 53 if (logEnable || debugLog) { // library marker davegut.Logging, line 54 log.debug "${device.displayName}-${driverVer()}: ${msg}" // library marker davegut.Logging, line 55 } // library marker davegut.Logging, line 56 } // library marker davegut.Logging, line 57 def logWarn(msg) { log.warn "${device.displayName}-${driverVer()}: ${msg}" } // library marker davegut.Logging, line 59 // ~~~~~ end include (1303) davegut.Logging ~~~~~