3 # © Copyright 2021-2022, Scott Gasch
5 """Utilities for dealing with the chromecasts."""
11 from typing import Any, List
15 import smart_home.device as dev
16 from decorator_utils import memoized
18 logger = logging.getLogger(__name__)
21 class BaseChromecast(dev.Device):
22 """A base class to represent a Google Chromecase device."""
24 ccasts: List[Any] = []
27 lock = threading.Lock()
29 def __init__(self, name: str, mac: str, keywords: str = "") -> None:
30 super().__init__(name.strip(), mac.strip(), keywords)
32 now = datetime.datetime.now()
33 with BaseChromecast.lock:
35 BaseChromecast.refresh_ts is None
36 or (now - BaseChromecast.refresh_ts).total_seconds() > 60
38 logger.debug('Refreshing the shared chromecast info list')
39 if BaseChromecast.browser is not None:
40 BaseChromecast.browser.stop_discovery()
42 BaseChromecast.ccasts,
43 BaseChromecast.browser,
44 ) = pychromecast.get_chromecasts(timeout=15.0)
45 assert BaseChromecast.browser is not None
46 atexit.register(BaseChromecast.browser.stop_discovery)
47 BaseChromecast.refresh_ts = now
50 for cc in BaseChromecast.ccasts:
51 if cc.cast_info.host == ip and cc.cast_info.cast_type != 'group':
52 logger.debug('Found chromecast at %s: %s', ip, cc)
54 self.cast.wait(timeout=1.0)
56 raise Exception(f'Can\'t find ccast device at {ip}, is that really a ccast device?')
59 return self.cast.is_idle
66 def get_friendly_name(self):
73 def get_model_name(self):
74 return self.cast.model_name
77 def get_cast_type(self):
78 return self.cast.cast_type
82 return self.cast.app_id
84 def get_app_display_name(self):
85 return self.cast.app_display_name
87 def get_media_controller(self):
88 return self.cast.media_controller
93 app = self.get_app_display_name()
94 mc = self.get_media_controller()
96 return f'{app} / {status.title}'
98 def start_app(self, app_id, force_launch=False):
99 """Start an app on the Chromecast."""
100 self.cast.start_app(app_id, force_launch)
103 """Tells the Chromecast to quit current app_id."""
106 def volume_up(self, delta=0.1):
107 """Increment volume by 0.1 (or delta) unless it is already maxed.
108 Returns the new volume.
110 return self.cast.volume_up(delta)
112 def volume_down(self, delta=0.1):
113 """Decrement the volume by 0.1 (or delta) unless it is already 0.
114 Returns the new volume.
116 return self.cast.volume_down(delta)
120 f"Chromecast({self.cast.socket_client.host!r}, port={self.cast.socket_client.port!r}, "
121 f"device={self.cast.cast_info.friendly_name!r})"