PyChromecast 14.0.2__tar.gz → 14.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PKG-INFO +1 -1
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/PKG-INFO +1 -1
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/SOURCES.txt +1 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/const.py +2 -0
- PyChromecast-14.0.4/pychromecast/controllers/heartbeat.py +90 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/socket_client.py +17 -79
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pyproject.toml +1 -1
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/requirements.txt +1 -1
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/LICENSE +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/MANIFEST.in +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/dependency_links.txt +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/not-zip-safe +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/requires.txt +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/PyChromecast.egg-info/top_level.txt +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/README.rst +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/__init__.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/config.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/__init__.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/bbciplayer.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/bbcsounds.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/bubbleupnp.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/dashcast.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/homeassistant.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/homeassistant_media.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/media.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/multizone.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/nrkradio.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/nrktv.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/plex.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/receiver.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/shaka.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/supla.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/yleareena.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/controllers/youtube.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/dial.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/discovery.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/error.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/authority_keys_pb2.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/authority_keys_pb2.pyi +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/cast_channel_pb2.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/cast_channel_pb2.pyi +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/logging_pb2.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/logging_pb2.pyi +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/generated/readme.txt +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/models.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/quick_play.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/pychromecast/response_handler.py +0 -0
- {PyChromecast-14.0.2 → PyChromecast-14.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyChromecast
|
|
3
|
-
Version: 14.0.
|
|
3
|
+
Version: 14.0.4
|
|
4
4
|
Summary: Python module to talk to Google Chromecast.
|
|
5
5
|
Author-email: Paulus Schoutsen <paulus@paulusschoutsen.nl>
|
|
6
6
|
Project-URL: Homepage, https://github.com/home-assistant-libs/pychromecast
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyChromecast
|
|
3
|
-
Version: 14.0.
|
|
3
|
+
Version: 14.0.4
|
|
4
4
|
Summary: Python module to talk to Google Chromecast.
|
|
5
5
|
Author-email: Paulus Schoutsen <paulus@paulusschoutsen.nl>
|
|
6
6
|
Project-URL: Homepage, https://github.com/home-assistant-libs/pychromecast
|
|
@@ -25,6 +25,7 @@ pychromecast/controllers/bbciplayer.py
|
|
|
25
25
|
pychromecast/controllers/bbcsounds.py
|
|
26
26
|
pychromecast/controllers/bubbleupnp.py
|
|
27
27
|
pychromecast/controllers/dashcast.py
|
|
28
|
+
pychromecast/controllers/heartbeat.py
|
|
28
29
|
pychromecast/controllers/homeassistant.py
|
|
29
30
|
pychromecast/controllers/homeassistant_media.py
|
|
30
31
|
pychromecast/controllers/media.py
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Controller to send and respond to heartbeat messages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from . import BaseController
|
|
9
|
+
|
|
10
|
+
from ..const import MESSAGE_TYPE, PLATFORM_DESTINATION_ID
|
|
11
|
+
from ..error import ControllerNotRegistered, NotConnected, PyChromecastStopped
|
|
12
|
+
|
|
13
|
+
# pylint: disable-next=no-name-in-module
|
|
14
|
+
from ..generated.cast_channel_pb2 import CastMessage
|
|
15
|
+
|
|
16
|
+
NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat"
|
|
17
|
+
|
|
18
|
+
TYPE_PING = "PING"
|
|
19
|
+
TYPE_PONG = "PONG"
|
|
20
|
+
|
|
21
|
+
HB_PING_TIME = 10
|
|
22
|
+
HB_PONG_TIME = 10
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class HeartbeatController(BaseController):
|
|
26
|
+
"""Controller to send and respond to heartbeat messages."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
super().__init__(NS_HEARTBEAT, target_platform=True)
|
|
30
|
+
self.last_ping = 0.0
|
|
31
|
+
self.last_pong = time.time()
|
|
32
|
+
self.logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
def receive_message(self, _message: CastMessage, data: dict) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Called when a heartbeat message is received.
|
|
37
|
+
|
|
38
|
+
data is message.payload_utf8 interpreted as a JSON dict.
|
|
39
|
+
"""
|
|
40
|
+
if self._socket_client is None:
|
|
41
|
+
raise ControllerNotRegistered
|
|
42
|
+
|
|
43
|
+
if self._socket_client.is_stopped:
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
if data[MESSAGE_TYPE] == TYPE_PING:
|
|
47
|
+
try:
|
|
48
|
+
self._socket_client.send_message(
|
|
49
|
+
PLATFORM_DESTINATION_ID,
|
|
50
|
+
self.namespace,
|
|
51
|
+
{MESSAGE_TYPE: TYPE_PONG},
|
|
52
|
+
no_add_request_id=True,
|
|
53
|
+
)
|
|
54
|
+
except PyChromecastStopped:
|
|
55
|
+
self._socket_client.logger.debug(
|
|
56
|
+
"Heartbeat error when sending response, "
|
|
57
|
+
"Chromecast connection has stopped"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
if data[MESSAGE_TYPE] == TYPE_PONG:
|
|
63
|
+
self.reset()
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
def ping(self) -> None:
|
|
69
|
+
"""Send a ping message."""
|
|
70
|
+
if self._socket_client is None:
|
|
71
|
+
raise ControllerNotRegistered
|
|
72
|
+
|
|
73
|
+
self.last_ping = time.time()
|
|
74
|
+
try:
|
|
75
|
+
self.send_message({MESSAGE_TYPE: TYPE_PING})
|
|
76
|
+
except NotConnected:
|
|
77
|
+
self._socket_client.logger.error(
|
|
78
|
+
"Chromecast is disconnected. Cannot ping until reconnected."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def reset(self) -> None:
|
|
82
|
+
"""Reset expired counter."""
|
|
83
|
+
self.last_pong = time.time()
|
|
84
|
+
|
|
85
|
+
def is_expired(self) -> bool:
|
|
86
|
+
"""Indicates if connection has expired."""
|
|
87
|
+
if time.time() - self.last_ping > HB_PING_TIME:
|
|
88
|
+
self.ping()
|
|
89
|
+
|
|
90
|
+
return (time.time() - self.last_pong) > HB_PING_TIME + HB_PONG_TIME
|
|
@@ -24,8 +24,9 @@ from struct import pack, unpack
|
|
|
24
24
|
|
|
25
25
|
import zeroconf
|
|
26
26
|
|
|
27
|
-
from .const import MESSAGE_TYPE, REQUEST_ID, SESSION_ID
|
|
27
|
+
from .const import MESSAGE_TYPE, PLATFORM_DESTINATION_ID, REQUEST_ID, SESSION_ID
|
|
28
28
|
from .controllers import BaseController, CallbackType
|
|
29
|
+
from .controllers.heartbeat import NS_HEARTBEAT, HeartbeatController
|
|
29
30
|
from .controllers.media import MediaController
|
|
30
31
|
from .controllers.receiver import CastStatus, CastStatusListener, ReceiverController
|
|
31
32
|
from .dial import get_host_from_service
|
|
@@ -42,12 +43,7 @@ from .generated.cast_channel_pb2 import CastMessage
|
|
|
42
43
|
from .models import HostServiceInfo, MDNSServiceInfo
|
|
43
44
|
|
|
44
45
|
NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection"
|
|
45
|
-
NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat"
|
|
46
46
|
|
|
47
|
-
PLATFORM_DESTINATION_ID = "receiver-0"
|
|
48
|
-
|
|
49
|
-
TYPE_PING = "PING"
|
|
50
|
-
TYPE_PONG = "PONG"
|
|
51
47
|
TYPE_CONNECT = "CONNECT"
|
|
52
48
|
TYPE_CLOSE = "CLOSE"
|
|
53
49
|
TYPE_LOAD = "LOAD"
|
|
@@ -65,8 +61,7 @@ CONNECTION_STATUS_FAILED_RESOLVE = "FAILED_RESOLVE"
|
|
|
65
61
|
# The socket connection was lost and needs to be retried
|
|
66
62
|
CONNECTION_STATUS_LOST = "LOST"
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
HB_PONG_TIME = 10
|
|
64
|
+
SELECT_TIMEOUT = 5.0
|
|
70
65
|
TIMEOUT_TIME = 30.0
|
|
71
66
|
RETRY_TIME = 5.0
|
|
72
67
|
|
|
@@ -566,9 +561,13 @@ class SocketClient(threading.Thread, CastStatusListener):
|
|
|
566
561
|
# A connection has been established at this point by self._check_connection
|
|
567
562
|
assert self.socket is not None
|
|
568
563
|
|
|
569
|
-
#
|
|
564
|
+
# Poll the socket and the socketpair, with a timeout of SELECT_TIMEOUT
|
|
565
|
+
# The timeout ensures we call _check_connection often enough to avoid
|
|
566
|
+
# the HeartbeatController from detecting a timeout
|
|
567
|
+
# The socketpair allow us to be interrupted on shutdown without waiting
|
|
568
|
+
# for the SELECT_TIMEOUT timout to expire
|
|
570
569
|
try:
|
|
571
|
-
ready = self.selector.select()
|
|
570
|
+
ready = self.selector.select(SELECT_TIMEOUT)
|
|
572
571
|
except (ValueError, OSError) as exc:
|
|
573
572
|
self.logger.error(
|
|
574
573
|
"[%s(%s):%s] Error in select call: %s",
|
|
@@ -687,7 +686,10 @@ class SocketClient(threading.Thread, CastStatusListener):
|
|
|
687
686
|
# route message to handlers
|
|
688
687
|
if message.namespace in self._handlers:
|
|
689
688
|
# debug messages
|
|
690
|
-
if
|
|
689
|
+
if (
|
|
690
|
+
message.namespace != NS_HEARTBEAT
|
|
691
|
+
or self.heartbeat_controller.logger.isEnabledFor(logging.DEBUG)
|
|
692
|
+
):
|
|
691
693
|
self.logger.debug(
|
|
692
694
|
"[%s(%s):%s] Received: %s",
|
|
693
695
|
self.fn or "",
|
|
@@ -871,7 +873,10 @@ class SocketClient(threading.Thread, CastStatusListener):
|
|
|
871
873
|
be_size = pack(">I", msg.ByteSize())
|
|
872
874
|
|
|
873
875
|
# Log all messages except heartbeat
|
|
874
|
-
if
|
|
876
|
+
if (
|
|
877
|
+
msg.namespace != NS_HEARTBEAT
|
|
878
|
+
or self.heartbeat_controller.logger.isEnabledFor(logging.DEBUG)
|
|
879
|
+
):
|
|
875
880
|
self.logger.debug(
|
|
876
881
|
"[%s(%s):%s] Sending: %s",
|
|
877
882
|
self.fn or "",
|
|
@@ -1060,73 +1065,6 @@ class ConnectionController(BaseController):
|
|
|
1060
1065
|
return False
|
|
1061
1066
|
|
|
1062
1067
|
|
|
1063
|
-
class HeartbeatController(BaseController):
|
|
1064
|
-
"""Controller to respond to heartbeat messages."""
|
|
1065
|
-
|
|
1066
|
-
def __init__(self) -> None:
|
|
1067
|
-
super().__init__(NS_HEARTBEAT, target_platform=True)
|
|
1068
|
-
self.last_ping = 0.0
|
|
1069
|
-
self.last_pong = time.time()
|
|
1070
|
-
|
|
1071
|
-
def receive_message(self, _message: CastMessage, data: dict) -> bool:
|
|
1072
|
-
"""
|
|
1073
|
-
Called when a heartbeat message is received.
|
|
1074
|
-
|
|
1075
|
-
data is message.payload_utf8 interpreted as a JSON dict.
|
|
1076
|
-
"""
|
|
1077
|
-
if self._socket_client is None:
|
|
1078
|
-
raise ControllerNotRegistered
|
|
1079
|
-
|
|
1080
|
-
if self._socket_client.is_stopped:
|
|
1081
|
-
return True
|
|
1082
|
-
|
|
1083
|
-
if data[MESSAGE_TYPE] == TYPE_PING:
|
|
1084
|
-
try:
|
|
1085
|
-
self._socket_client.send_message(
|
|
1086
|
-
PLATFORM_DESTINATION_ID,
|
|
1087
|
-
self.namespace,
|
|
1088
|
-
{MESSAGE_TYPE: TYPE_PONG},
|
|
1089
|
-
no_add_request_id=True,
|
|
1090
|
-
)
|
|
1091
|
-
except PyChromecastStopped:
|
|
1092
|
-
self._socket_client.logger.debug(
|
|
1093
|
-
"Heartbeat error when sending response, "
|
|
1094
|
-
"Chromecast connection has stopped"
|
|
1095
|
-
)
|
|
1096
|
-
|
|
1097
|
-
return True
|
|
1098
|
-
|
|
1099
|
-
if data[MESSAGE_TYPE] == TYPE_PONG:
|
|
1100
|
-
self.reset()
|
|
1101
|
-
return True
|
|
1102
|
-
|
|
1103
|
-
return False
|
|
1104
|
-
|
|
1105
|
-
def ping(self) -> None:
|
|
1106
|
-
"""Send a ping message."""
|
|
1107
|
-
if self._socket_client is None:
|
|
1108
|
-
raise ControllerNotRegistered
|
|
1109
|
-
|
|
1110
|
-
self.last_ping = time.time()
|
|
1111
|
-
try:
|
|
1112
|
-
self.send_message({MESSAGE_TYPE: TYPE_PING})
|
|
1113
|
-
except NotConnected:
|
|
1114
|
-
self._socket_client.logger.error(
|
|
1115
|
-
"Chromecast is disconnected. Cannot ping until reconnected."
|
|
1116
|
-
)
|
|
1117
|
-
|
|
1118
|
-
def reset(self) -> None:
|
|
1119
|
-
"""Reset expired counter."""
|
|
1120
|
-
self.last_pong = time.time()
|
|
1121
|
-
|
|
1122
|
-
def is_expired(self) -> bool:
|
|
1123
|
-
"""Indicates if connection has expired."""
|
|
1124
|
-
if time.time() - self.last_ping > HB_PING_TIME:
|
|
1125
|
-
self.ping()
|
|
1126
|
-
|
|
1127
|
-
return (time.time() - self.last_pong) > HB_PING_TIME + HB_PONG_TIME
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
1068
|
def new_socket() -> socket.socket:
|
|
1131
1069
|
"""
|
|
1132
1070
|
Create a new socket with OS-specific parameters
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|