Fix chromecasts to work with new version of pychromecast; rename
[python_utils.git] / smart_home / chromecasts.py
1 #!/usr/bin/env python3
2
3 """Utilities for dealing with the webcams."""
4
5 import atexit
6 import datetime
7 import logging
8 import time
9
10 import pychromecast
11
12 from decorator_utils import memoized
13 import smart_home.device as dev
14
15 logger = logging.getLogger(__name__)
16
17
18 class BaseChromecast(dev.Device):
19     ccasts = []
20     refresh_ts = None
21     browser = None
22
23     def __init__(self, name: str, mac: str, keywords: str = "") -> None:
24         super().__init__(name.strip(), mac.strip(), keywords)
25         ip = self.get_ip()
26         now = datetime.datetime.now()
27         if (
28                 BaseChromecast.refresh_ts is None
29                 or (now - BaseChromecast.refresh_ts).total_seconds() > 60
30         ):
31             logger.debug('Refreshing the shared chromecast info list')
32             BaseChromecast.ccasts, BaseChromecast.browser = pychromecast.get_chromecasts()
33             atexit.register(BaseChromecast.browser.stop_discovery)
34             BaseChromecast.refresh_ts = now
35
36         self.cast = None
37         for cc in BaseChromecast.ccasts:
38             if cc.cast_info.host == ip:
39                 logger.debug(f'Found chromecast at {ip}: {cc}')
40                 self.cast = cc
41                 self.cast.wait()
42                 time.sleep(0.1)
43         if self.cast is None:
44             raise Exception(f'Can\'t find ccast device at {ip}, is that really a ccast device?')
45
46     def is_idle(self):
47         return self.cast.is_idle
48
49     @memoized
50     def get_uuid(self):
51         return self.cast.uuid
52
53     @memoized
54     def get_friendly_name(self):
55         return self.cast.name
56
57     def get_uri(self):
58         return self.cast.url
59
60     @memoized
61     def get_model_name(self):
62         return self.cast.model_name
63
64     @memoized
65     def get_cast_type(self):
66         return self.cast.cast_type
67
68     @memoized
69     def app_id(self):
70         return self.cast.app_id
71
72     def get_app_display_name(self):
73         return self.cast.app_display_name
74
75     def get_media_controller(self):
76         return self.cast.media_controller
77
78     def status(self):
79         if self.is_idle():
80             return 'idle'
81         app = self.get_app_display_name()
82         mc = self.get_media_controller()
83         status = mc.status
84         return f'{app} / {status.title}'
85
86     def start_app(self, app_id, force_launch=False):
87         """Start an app on the Chromecast."""
88         self.cast.start_app(app_id, force_launch)
89
90     def quit_app(self):
91         """Tells the Chromecast to quit current app_id."""
92         self.cast.quit_app()
93
94     def volume_up(self, delta=0.1):
95         """Increment volume by 0.1 (or delta) unless it is already maxed.
96         Returns the new volume.
97         """
98         return self.cast.volume_up(delta)
99
100     def volume_down(self, delta=0.1):
101         """Decrement the volume by 0.1 (or delta) unless it is already 0.
102         Returns the new volume.
103         """
104         return self.cast.volume_down(delta)
105
106     def __repr__(self):
107         return (
108             f"Chromecast({self.cast.socket_client.host!r}, port={self.cast.socket_client.port!r}, "
109             f"device={self.cast.cast_info.friendly_name!r})"
110         )
111