[BETA] Google Chromecast+

So, terminology matters. Debug logging is something different than Info logging.

Debug Logging can be turned off by the method I mentioned.

Sounds like you are asking the developer to add additional logging level restrictions such as Info Only, Debug, Warning, Error, etc. Like is available in Rule Machine

SetVolume is only called on startup and every time a subroutine is called to announce an arrival, doorbell, etc. so not really that often.

Yeah, it's Info logging. The only Switch available is Debug in the app so one can assume there is no toggle for Info disable, yet. :wink:

Fair point on the terminology - and thanks for taking the time to clarify!

What I meant is that most of those current "Info" lines look more like internal execution details than high-level user events. Since they’re sent as log.info, they keep spamming the live log even when debug mode is turned off.

In standard Hubitat practice, everyday Info logs are usually just for major events (like an actual state change you'd want to track), while all the fine-grained operational updates belong in log.debug.

If one could reclassify those detailed messages to log.debug, it would make the daily logs so much cleaner without losing any useful info when troubleshooting.

So, I found the issue. The app MERGES the mdns discovery without removing any prior discovered devices. There were no ghost on my home app, and looking at mDNS discovery, they did not show up there either.

I had chat give me a few snippets to modify the existing app that would list any "stale" entries (Devices in the APP list that had no current corresponding mDNS value) and allow you to select which ones you want to remove. Selecting them, and then clicking done removes them from the app list.

This way, if you had manually added something that was not available in mDNS, it will not be removed unless you toggle it. But, you can remove it by selecting it in the stale section and clicking done. Also, if you have a device that is just offline at the time of the scan, it will show as stale. So, you wouldn't want to select it there either.

Tested and verified it worked by selecting only one and it left the remainder of the devices. Then I went back in and selected the rest.

Code is here if you are interested in merging it. Search on "STALE" and "Stale" for the blocks I added. ALL are enclosed in major comment blocks and indented too far to make it easier to find.

I am sure you could make it better (like add a specific button for the cleanup)

https://raw.githubusercontent.com/tweas/HubitatPublic/refs/heads/master/Chromecast%2BRemoveStaleDevices

I want to use "break time" to structure my spoken words...

When I use the device GUI (Play Text) and enter something like
Hello<break time="2s"/>World!
it works 100%!

But when I use Rule Machine, everytime I enter a text with "<" and ">" this part is removed!
(I even tried using HTML codes (60, 62) - but even this is removed...)

Any idea how I can write one(!) text with pauses in between?

I seem to recall in the past inserting . (Periods) for pauses. If I get time today, I'll test it.

It works, puts a small pause between words when you add a period betwen them. I tried adding three periods and the pause didn't change.

It's a brief pause, but noticeable.

Thanks for checking. What if you add a period, a space then another period so that Google thinks it's a new sentence. I am not at home to try this

The pause remains short. :person_shrugging:t2:

I've tried also many other combinations of special chars - no success.

The real problem is that RM drops all <...> parts out of strings...
Maybe this driver can integrate some other way to enter SSML... e.g. by using "{...}" instead?

My quick hack (in function "withLeadIn" adding this as new first line)

text = text.replace('{', '<').replace('}', '>')

is working! :smiling_face_with_sunglasses:

Now I can just use Hello {break time="5s"/} World! ...

Other possibilities (depends on language and voice):

  • {say-as interpret-as='spell-out'}hello{/say-as}
  • {amazon:effect name='whispered'}This is my secret.{/amazon:effect}

@jpage4500 Would you add :up_arrow:this​:up_arrow: in the official driver?

I've added the following code to broadcast only to all those devices, that are actually idle (i.e. not speaking or playing => stopped):

// custom command: play text only on idle devices
def broadcastIdle(text, volume = null, voice = null) {
if (isEmpty(text)) { logWarn "broadcastIdle: empty message, ignoring"; return }
def kids = getChildDevices().findAll{ it.currentStatus == "stopped" }
logInfo "broadcastIdle: '${text}'${volume != null ? " @${volume}" : ""} -> ${kids.size()} device(s)"
kids.each { child ->
if (voice != null) child.speak(text, volume, voice)
else if (volume != null) child.speak(text, volume)
else child.speak(text)
}
}

@jpage4500 Would you add :up_arrow:this​ :up_arrow: in the official driver? :wink:

Of course, don't forget the definition:
command "broadcastIdle", [
[name: "Message*", type: "STRING", description: "text to speak on idle Chromecast"],
[name: "Volume", type: "NUMBER", description: "0-100, blank = leave each device's current volume"]
]

thanks for finding this! I'll run it through Claude and have it suggest ways to fix

>> Would you add :up_arrow:this​ :up_arrow: in the official driver?

Sure, that makes sense

Yep - I'll add this too

I pushed a new version with 3 changes suggested above. I have NOT tested these - just reviewed the code changes and they look good to me but please let me know if you have/see any issues:

  • flag devices that haven't been seen in 5 minutes as 'stale'
  • support modified SSML for use in Rule Manager (use {} instead of <>)
  • add broadcastIdle() function

@jpage4500 Getting HPM error on update or repair

Attached log error.

Error Occurred During Installation

An error occurred while installing the package: Failed to upgrade app https://raw.githubusercontent.com/jpage4500/hubitat-drivers/master/google-chromecast-plus/google-chromecast-plus-app.groovy.

app:21122026-08-21 10:10:37.603errororg.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: General error during canonicalization: Expression [MethodCallExpression] is not allowed: log.$level(GC+ [App] $msg) at line number 369 java.lang.SecurityException: Expression [MethodCallExpression] is not allowed: log.$level(GC+ [App] $msg) at line number 369 at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.a(SecureASTCustomizerHub.java:738) at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitMethodCallExpression(SecureASTCustomizerHub.java:918) at org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:70) at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitExpressionStatement(SecureASTCustomizerHub.java:844) at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42) at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitBlockStatement(SecureASTCustomizerHub.java:804) at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:71) at com.hubitat.hub.executor.SecureASTCustomizerHub.call(SecureASTCustomizerHub.java:568) at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087) at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624) at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602) at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579) at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293) at com.hubitat.hub.executor.ExecutorHelper.parseClassInternal(ExecutorHelper.groovy:787) at com.hubitat.hub.executor.ExecutorHelper.parseAppClass(ExecutorHelper.groovy:755) at com.hubitat.hub.executor.ExecutorHelper.loadAppTypeClass(ExecutorHelper.groovy:901) at com.hubitat.hub.executor.ExecutorHelper.access$3(ExecutorHelper.groovy) at com.hubitat.hub.executor.ExecutorHelper$_getCachedAppExecutorClass_closure11.doCall(ExecutorHelper.groovy:882) at jdk.internal.reflect.GeneratedMethodAccessor635.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034) at groovy.lang.Closure.call(Closure.java:420) at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:54) at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:124) at com.sun.proxy.$Proxy42.apply(Unknown Source) at com.hubitat.hub.executor.ExecutorHelper.getCachedClassInternal(ExecutorHelper.groovy:828) at com.hubitat.hub.executor.ExecutorHelper.getCachedAppExecutorClass(ExecutorHelper.groovy:882) at com.hubitat.hub.executor.ExecutorHelper.getCachedAppExecutorClass(ExecutorHelper.groovy) at com.hubitat.hub.executor.ExecutorHelper.getAppExecutorScript(ExecutorHelper.groovy:692) at com.hubitat.hub.executor.ExecutorHelper$getAppExecutorScript$0.call(Unknown Source) at com.hubitat.handler.AppHandler.getAppExecutor(AppHandler.groovy:243) at com.hubitat.handler.AppHandler.runAppMethodWithException(AppHandler.groovy:133) at com.hubitat.handler.AppHandler$runAppMethodWithException$0.callCurrent(Unknown Source) at com.hubitat.handler.AppHandler.runAppMethod(AppHandler.groovy:59) at com.hubitat.handler.AppHandler$runAppMethod.call(Unknown Source) at com.hubitat.hub.job.GenericJob.execute(GenericJob.groovy:68) at org.quartz.core.JobRunShell.run(JobRunShell.java:202) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) 1 error (scanMdns)

... and that's what I get for not testing it :frowning:

EDIT: should be fixed - I installed all 3 drivers and made sure the app displays

The interesting thing is this might be related to a newer Hub firmware..

Not caused by the recent changes. Line 369 in commit 9a43902 — the version before the stale-device and SSML work — is exactly that helper. It had compiled fine for a long time; the sandbox check appears to have tightened in a firmware update, so it breaks on hubs newer than yours.

Updates now, thanks.

Possible beta issue maybe.

All my devices (15 or so) disappear when I do a refresh except the original ticked one.

I also get this on all my Devices:

image

2026-08-21 10:50:37.836errororg.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during canonicalization: Expression [MethodCallExpression] is not allowed: log.$level(GC+ [App] $msg) at line number 369

java.lang.SecurityException: Expression [MethodCallExpression] is not allowed: log.$level(GC+ [App] $msg) at line number 369
at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.a(SecureASTCustomizerHub.java:738)
at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitMethodCallExpression(SecureASTCustomizerHub.java:918)
at org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:70)
at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitExpressionStatement(SecureASTCustomizerHub.java:844)
at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42)
at com.hubitat.hub.executor.SecureASTCustomizerHub$SecuringCodeVisitor.visitBlockStatement(SecureASTCustomizerHub.java:804)
at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:71)
at com.hubitat.hub.executor.SecureASTCustomizerHub.call(SecureASTCustomizerHub.java:568)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
at com.hubitat.hub.executor.ExecutorHelper.parseClassInternal(ExecutorHelper.groovy:787)
at com.hubitat.hub.executor.ExecutorHelper.parseAppClass(ExecutorHelper.groovy:755)
at com.hubitat.hub.executor.ExecutorHelper.loadAppTypeClass(ExecutorHelper.groovy:901)
at com.hubitat.hub.executor.ExecutorHelper.access$3(ExecutorHelper.groovy)
at com.hubitat.hub.executor.ExecutorHelper$_getCachedAppExecutorClass_closure11.doCall(ExecutorHelper.groovy:882)
at jdk.internal.reflect.GeneratedMethodAccessor635.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
at groovy.lang.Closure.call(Closure.java:420)
at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:54)
at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:124)
at com.sun.proxy.$Proxy42.apply(Unknown Source)
at com.hubitat.hub.executor.ExecutorHelper.getCachedClassInternal(ExecutorHelper.groovy:828)
at com.hubitat.hub.executor.ExecutorHelper.getCachedAppExecutorClass(ExecutorHelper.groovy:882)
at com.hubitat.hub.executor.ExecutorHelper.getCachedAppExecutorClass(ExecutorHelper.groovy)
at com.hubitat.hub.executor.ExecutorHelper.getAppExecutorScript(ExecutorHelper.groovy:692)
at com.hubitat.hub.executor.ExecutorHelper$getAppExecutorScript$0.call(Unknown Source)
at com.hubitat.handler.AppHandler.getAppExecutor(AppHandler.groovy:243)
at com.hubitat.handler.AppHandler.runAppMethodWithException(AppHandler.groovy:133)
at com.hubitat.handler.AppHandler$runAppMethodWithException$0.callCurrent(Unknown Source)
at com.hubitat.handler.AppHandler.runAppMethod(AppHandler.groovy:59)
at com.hubitat.handler.AppHandler$runAppMethod.call(Unknown Source)
at com.hubitat.hub.job.GenericJob.execute(GenericJob.groovy:68)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)

1 error
(scanMdns)

I don't see this on my hub - with 10 or so devices. But, I did push a fix if you want to see if that works