For a specific reason, I need a high-speed polling service to track volume on a Samsung TV. I've searched everywhere, and while there's a slick WebSocket interface for sending (some) commands, there's no documented way to receive volume events. The only way to get volume levels is via a SOAP call.
Right now, this polling runs every 5 seconds if the TV is on and there haven’t been any volume changes in the last 10 minutes. If a volume event occurred within the last 10 minutes, it polls every 1 second. During an active volume change, it bumps to 500ms for about 10 seconds.
I’m looking for a code review to see if there’s a way to reduce hub overhead — not a discussion about why this approach sucks since this is how Home Assistant does it too.
The dirty details are here at line 524, but the basics for initial discussion are:
@Field static final Integer VOLUME_POLL_DELAY = 1000
@Field static final Integer VOLUME_POLL_INACTIVITY_DELAY = 5000
@Field volatile static Map<Long,Map> g_cachePollVolumeMap = [:]
// this is called 'again' after the callback so loops
void pollVolume(Boolean initialize=false) {
if (g_cachePollVolumeMap[devId]==null || initialize) {
// ... cache a SOAP structure for use in the hubAction. only build it once.
}
Map pollData = g_cachePollVolumeMap[devId]
// Use sendHubCommand because it is faster and lower overhead then normal http stack
def hubAction = new hubitat.device.HubAction([
method: "POST",
path: "/upnp/control/AVTransport1",
body: pollData.body,
headers: pollData.headers
], null, [callback: pollVolumeCallback])
// There is no error try/catch with sendHubCommand. So use a watchdog to restart if callback doesn't happen.
sendHubCommand(hubAction)
// A watchdog if we have problems.
runInMillis((VOLUME_POLL_INACTIVITY_DELAY + 2000) as Integer, "pollVolumeWatchdog")
}
void pollVolumeWatchdog(String descriptionText="watchdog timer expired") {
// ... do some watchdog stuff in the case we have an error
}
void pollVolumeCallback(hubResponse) {
try {
Map msg = parseLanMessage(hubResponse.description)
// ... do checks if volume changed and do events on that change, then figure out poll rate.
} catch (Exception e) {
pollVolumeWatchdog(e.message)
return
}
// remove our function delay, and never allow lower than 250ms.
Integer runInMillisDelay = Math.max((pollData?.runInMillis ?: VOLUME_POLL_DELAY) - (now() - (pollData?.timestamp ?: 0)), 250)
runInMillis(runInMillisDelay as Integer, "pollVolume")
}