Don't be tricked by speaker groups living on the same IP address
[python_utils.git] / smart_home / chromecasts.py
index 08290e538262a1d2a56646f769d3e82e69798334..a5db86f3f003395d61ee2436688e44eaaa55d963 100644 (file)
@@ -2,8 +2,10 @@
 
 """Utilities for dealing with the webcams."""
 
+import atexit
+import datetime
 import logging
-import time
+import threading
 
 import pychromecast
 
@@ -14,12 +16,40 @@ logger = logging.getLogger(__name__)
 
 
 class BaseChromecast(dev.Device):
+    ccasts = []
+    refresh_ts = None
+    browser = None
+    lock = threading.Lock()
+
     def __init__(self, name: str, mac: str, keywords: str = "") -> None:
         super().__init__(name.strip(), mac.strip(), keywords)
         ip = self.get_ip()
-        self.cast = pychromecast.Chromecast(ip)
-        self.cast.wait()
-        time.sleep(0.1)
+        now = datetime.datetime.now()
+        with BaseChromecast.lock as l:
+            if (
+                    BaseChromecast.refresh_ts is None
+                    or (now - BaseChromecast.refresh_ts).total_seconds() > 60
+            ):
+                logger.debug('Refreshing the shared chromecast info list')
+                if BaseChromecast.browser is not None:
+                    BaseChromecast.browser.stop_discovery()
+                BaseChromecast.ccasts, BaseChromecast.browser = pychromecast.get_chromecasts(
+                    timeout=15.0
+                )
+                atexit.register(BaseChromecast.browser.stop_discovery)
+                BaseChromecast.refresh_ts = now
+
+        self.cast = None
+        for cc in BaseChromecast.ccasts:
+            if (
+                    cc.cast_info.host == ip
+                    and cc.cast_info.cast_type != 'group'
+            ):
+                logger.debug(f'Found chromecast at {ip}: {cc}')
+                self.cast = cc
+                self.cast.wait(timeout=1.0)
+        if self.cast is None:
+            raise Exception(f'Can\'t find ccast device at {ip}, is that really a ccast device?')
 
     def is_idle(self):
         return self.cast.is_idle
@@ -84,5 +114,6 @@ class BaseChromecast(dev.Device):
     def __repr__(self):
         return (
             f"Chromecast({self.cast.socket_client.host!r}, port={self.cast.socket_client.port!r}, "
-            f"device={self.cast.device!r})"
+            f"device={self.cast.cast_info.friendly_name!r})"
         )
+