spotifywebapiPython 1.0.243__py3-none-any.whl → 1.0.244__py3-none-any.whl
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.
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +9 -2
- spotifywebapipython/docs/index.html +2 -2
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +228 -76
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py +2 -2
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonecontrollerlistener.py +247 -0
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonemanagerlistener.py +384 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/METADATA +1 -1
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/RECORD +24 -23
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonelistener.py +0 -212
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/LICENSE +0 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/WHEEL +0 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# external package imports.
|
|
2
2
|
from abc import abstractmethod
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
import copy
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
import hashlib
|
|
7
6
|
from pychromecast import CastBrowser, CastInfo, Chromecast, get_chromecast_from_cast_info, get_chromecast_from_host
|
|
8
|
-
|
|
9
|
-
from pychromecast.
|
|
7
|
+
import pychromecast
|
|
8
|
+
from pychromecast.dial import MultizoneInfo, MultizoneStatus, get_multizone_status, get_device_info
|
|
9
|
+
from pychromecast.controllers.multizone import MultiZoneManagerListener, MultizoneController, MultizoneManager
|
|
10
10
|
from pychromecast.const import CAST_TYPE_GROUP
|
|
11
11
|
import socket
|
|
12
12
|
from soco import SoCo
|
|
@@ -19,7 +19,8 @@ from zeroconf import Zeroconf, ServiceBrowser
|
|
|
19
19
|
from .spotifyconnectzeroconfexceptions import SpotifyConnectDeviceNotFound
|
|
20
20
|
from .spotifyconnectzeroconfcastapptask import SpotifyConnectZeroconfCastAppTask
|
|
21
21
|
from .spotifyconnectzeroconfcastlistener import SpotifyConnectZeroconfCastListener
|
|
22
|
-
from .
|
|
22
|
+
from .spotifyconnectzeroconfcastmultizonecontrollerlistener import SpotifyConnectZeroconfCastMultiZoneControllerListener
|
|
23
|
+
from .spotifyconnectzeroconfcastmultizonemanagerlistener import SpotifyConnectZeroconfCastMultiZoneManagerListener
|
|
23
24
|
from .spotifyconnectdeviceeventargs import SpotifyConnectDeviceEventArgs
|
|
24
25
|
from .spotifyconnectzeroconflistener import ZEROCONF_SERVICETYPE_SPOTIFYCONNECT, SpotifyConnectZeroconfListener
|
|
25
26
|
from .spotifyconnectzeroconfcastcontroller import (
|
|
@@ -108,6 +109,8 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
108
109
|
self._CastAppTasks:dict[str, SpotifyConnectZeroconfCastAppTask] = {}
|
|
109
110
|
self._CastBrowser:CastBrowser = None
|
|
110
111
|
self._CastMultiZoneControllers:dict[str, MultizoneController] = {}
|
|
112
|
+
self._CastMultiZoneManager:MultizoneManager = None
|
|
113
|
+
self._CastMultiZoneManagerListeners:dict[str, MultiZoneManagerListener] = {}
|
|
111
114
|
self._InitialDiscoveryTimeout = initialDiscoveryTimeout
|
|
112
115
|
self._IsStopRequested:bool = False
|
|
113
116
|
self._SonosPlayers:dict = {}
|
|
@@ -269,6 +272,9 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
269
272
|
handlers=[handler.OnServiceStateChange]
|
|
270
273
|
)
|
|
271
274
|
|
|
275
|
+
# create new chromecast multizone manager that will inform us of multizone events.
|
|
276
|
+
self._CastMultiZoneManager = MultizoneManager()
|
|
277
|
+
|
|
272
278
|
# create new chromecast zeroconf discovery browser instance.
|
|
273
279
|
# this will discover Chromecasts on the local network.
|
|
274
280
|
# when a Chromecast is found, cast_listener.add_cast is called.
|
|
@@ -401,6 +407,107 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
401
407
|
# ignore exceptions, since we are shutting down.
|
|
402
408
|
|
|
403
409
|
|
|
410
|
+
def _TraceMultizoneGroupMembers(
|
|
411
|
+
self,
|
|
412
|
+
group_uuid:str,
|
|
413
|
+
friendly_name:str,
|
|
414
|
+
) -> None:
|
|
415
|
+
"""
|
|
416
|
+
Adds user-friendly multizone group member list to the trace log.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
uuid (str):
|
|
420
|
+
CastInfo uuid of the multizone group.
|
|
421
|
+
friendly_name (str):
|
|
422
|
+
Friendly name of the multizone group.
|
|
423
|
+
"""
|
|
424
|
+
try:
|
|
425
|
+
|
|
426
|
+
# get multizone controller reference for the group.
|
|
427
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(group_uuid, None)
|
|
428
|
+
if (castMultizoneController is not None):
|
|
429
|
+
|
|
430
|
+
# trace.
|
|
431
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
432
|
+
membersSummary:list[str] = []
|
|
433
|
+
for member in castMultizoneController.members:
|
|
434
|
+
scMemberDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.GetDeviceByDiscoveryKey(member)
|
|
435
|
+
if (scMemberDevice is None):
|
|
436
|
+
membersSummary.append(member)
|
|
437
|
+
else:
|
|
438
|
+
membersSummary.append("%s [ip=%s]" % (scMemberDevice.Title, scMemberDevice.DiscoveryResult.HostIpTitle))
|
|
439
|
+
_logsi.LogArray(SILevel.Verbose, "%s - Chromecast multizone group members for group: \"%s\" (count=%d)" % (self.name, friendly_name, len(membersSummary)), membersSummary, colorValue=SIColors.Coral)
|
|
440
|
+
|
|
441
|
+
except Exception as ex:
|
|
442
|
+
|
|
443
|
+
# trace.
|
|
444
|
+
_logsi.LogException("Chromecast Multizone group members trace exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
445
|
+
# ignore exception, as nothing can be done about it.
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _VerifyCastGroupMultiZoneController(
|
|
449
|
+
self,
|
|
450
|
+
castDevice:Chromecast,
|
|
451
|
+
scDevice:SpotifyConnectDevice,
|
|
452
|
+
) -> None:
|
|
453
|
+
"""
|
|
454
|
+
Verifies that a multizone controller is registered for the the cast group device,
|
|
455
|
+
and that the controller is connected.
|
|
456
|
+
|
|
457
|
+
A new controller will be registered (or re-registered) if necessary.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
castDevice (Chromecast):
|
|
461
|
+
Cast group device to register the controller for.
|
|
462
|
+
scDevice (SpotifyConnectDevice):
|
|
463
|
+
SpotifyConnectDevice instance that contains extended device information.
|
|
464
|
+
|
|
465
|
+
A controller can go inactive if the device drops off the network and is re-attached.
|
|
466
|
+
This will be detected if it happens, and a new controller instantiated.
|
|
467
|
+
"""
|
|
468
|
+
try:
|
|
469
|
+
|
|
470
|
+
# multizone controllers only need to be registered to group devices.
|
|
471
|
+
if (castDevice.cast_type == CAST_TYPE_GROUP):
|
|
472
|
+
|
|
473
|
+
# have we registered a multizone controller for this group yet?
|
|
474
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(str(castDevice.uuid), None)
|
|
475
|
+
|
|
476
|
+
# is multizone controller still registered, and connected to the network?
|
|
477
|
+
# the controller can go inactive if the device drops off the network and is re-attached.
|
|
478
|
+
if (castMultizoneController) and (castMultizoneController._socket_client is None):
|
|
479
|
+
|
|
480
|
+
# trace
|
|
481
|
+
_logsi.LogVerbose("Chromecast Multizone Controller is no longer active; removing the controller instance so it can be re-created for the device", colorValue=SIColors.Red)
|
|
482
|
+
|
|
483
|
+
# remove inactive multizone controller listener.
|
|
484
|
+
castMultizoneController.tear_down()
|
|
485
|
+
self._CastMultiZoneControllers.pop(str(castDevice.uuid), None)
|
|
486
|
+
castMultizoneController = None
|
|
487
|
+
|
|
488
|
+
# is multizone controller still registered?
|
|
489
|
+
if (castMultizoneController is None):
|
|
490
|
+
|
|
491
|
+
# trace.
|
|
492
|
+
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being added for Cast device: %s [%s]" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), colorValue=SIColors.Lavender)
|
|
493
|
+
|
|
494
|
+
# add cast device to multizone controller instance.
|
|
495
|
+
# this will add a listener for multizone status events to be processed.
|
|
496
|
+
castMultizoneController = MultizoneController(castDevice.uuid)
|
|
497
|
+
castMultizoneController.register_listener(SpotifyConnectZeroconfCastMultiZoneControllerListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, castMultizoneController, castDevice))
|
|
498
|
+
castDevice.register_handler(castMultizoneController)
|
|
499
|
+
self._CastMultiZoneControllers[str(castDevice.uuid)] = castMultizoneController
|
|
500
|
+
|
|
501
|
+
# request an initial status update.
|
|
502
|
+
castMultizoneController.update_members()
|
|
503
|
+
|
|
504
|
+
except Exception as ex:
|
|
505
|
+
|
|
506
|
+
# trace.
|
|
507
|
+
_logsi.LogException("Chromecast Multizone verify controller exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
508
|
+
# ignore exception, as nothing can be done about it.
|
|
509
|
+
|
|
510
|
+
|
|
404
511
|
def ActivateCastAppSpotify(
|
|
405
512
|
self,
|
|
406
513
|
deviceName:str,
|
|
@@ -530,6 +637,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
530
637
|
scDevice.ZeroconfResponseInfo = ZeroconfResponse()
|
|
531
638
|
|
|
532
639
|
castDevice:Chromecast = None
|
|
640
|
+
deviceWaitTimeoutSecs:float = 10.0
|
|
533
641
|
|
|
534
642
|
# is this a cast group?
|
|
535
643
|
if (castInfo.cast_type == CAST_TYPE_GROUP):
|
|
@@ -544,46 +652,66 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
544
652
|
# current group leader host info for each group. we will then create a cast device to
|
|
545
653
|
# the group leader host address, and start the SpotifyCastApp on that specific host.
|
|
546
654
|
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
655
|
+
# trace.
|
|
656
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
657
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
550
658
|
|
|
551
659
|
# default host ip and port in case we don't find the group coordinator.
|
|
552
660
|
groupHost:str = scDevice.DiscoveryResult.HostIpAddress
|
|
553
661
|
groupPort:int = scDevice.DiscoveryResult.HostIpPort
|
|
554
662
|
|
|
555
|
-
|
|
556
|
-
zoneInfo:MultizoneInfo = None
|
|
557
|
-
for zoneInfo in castMultiZoneStatus.groups:
|
|
663
|
+
try:
|
|
558
664
|
|
|
559
|
-
#
|
|
560
|
-
|
|
665
|
+
# request a multizone group member update.
|
|
666
|
+
# note that this will just send a request to the cast group device for its
|
|
667
|
+
# status, which will then update the members list via event listener callbacks.
|
|
668
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(str(castInfo.uuid), None)
|
|
669
|
+
if (castMultizoneController) and (castMultizoneController._socket_client):
|
|
670
|
+
castMultizoneController.update_members()
|
|
561
671
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
groupPort = scDevice.DiscoveryResult.HostIpPort
|
|
568
|
-
break
|
|
672
|
+
except Exception as ex:
|
|
673
|
+
|
|
674
|
+
# trace.
|
|
675
|
+
_logsi.LogException("Chromecast Multizone group update_members exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
676
|
+
# ignore exception, as nothing can be done about it.
|
|
569
677
|
|
|
570
|
-
#
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
678
|
+
# get current multizone status for all groups.
|
|
679
|
+
castMultiZoneStatus:MultizoneStatus = get_multizone_status(castInfo.host, castInfo.services, self.ZeroconfInstance, 5)
|
|
680
|
+
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast group multizone status for device: %s [ip=%s:%s]" % (self.name, scDevice.Title, castInfo.host, castInfo.port), castMultiZoneStatus, colorValue=SIColors.Coral)
|
|
681
|
+
|
|
682
|
+
# if group device dropped off the network, then we can't query it!
|
|
683
|
+
if (castMultiZoneStatus is not None):
|
|
576
684
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
685
|
+
# find the group coordinator for the selected device.
|
|
686
|
+
zoneInfo:MultizoneInfo = None
|
|
687
|
+
for zoneInfo in castMultiZoneStatus.groups:
|
|
688
|
+
|
|
689
|
+
# is this our group?
|
|
690
|
+
if (str(zoneInfo.uuid) == scDevice.DiscoveryResult.Key):
|
|
691
|
+
|
|
692
|
+
# yes - get host info to use for the device.
|
|
693
|
+
# we will use the device discovery result HostIpAddress instead of the zoneInfo.host
|
|
694
|
+
# value here, as the zoneInfo MAY contain an IPV6 formatted address, which was causing
|
|
695
|
+
# issues if the device was originally activated using an IPV4 address!
|
|
696
|
+
groupHost = scDevice.DiscoveryResult.HostIpAddress
|
|
697
|
+
groupPort = scDevice.DiscoveryResult.HostIpPort
|
|
698
|
+
break
|
|
699
|
+
|
|
700
|
+
# connect to the device and build a Chromecast instance from group coordinator host info.
|
|
701
|
+
castDevice = get_chromecast_from_host(
|
|
702
|
+
host=(groupHost, groupPort, castInfo.uuid, castInfo.model_name, castInfo.friendly_name),
|
|
703
|
+
tries=2,
|
|
704
|
+
retry_wait=0.5,
|
|
705
|
+
timeout=5)
|
|
583
706
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
707
|
+
# calculate wait timeout for devices to become active, based on number of devices in group.
|
|
708
|
+
if (castMultiZoneStatus.groups is not None):
|
|
709
|
+
groupCount:int = len(castMultiZoneStatus.groups)
|
|
710
|
+
if (groupCount > 1):
|
|
711
|
+
deviceWaitTimeoutSecs = (groupCount * 5.0)
|
|
712
|
+
|
|
713
|
+
# trace.
|
|
714
|
+
_logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast group multizone device to activate: %s [ip=%s:%s]" % (self.name, deviceWaitTimeoutSecs, scDevice.Title, groupHost, groupPort), colorValue=SIColors.Coral)
|
|
587
715
|
|
|
588
716
|
else:
|
|
589
717
|
|
|
@@ -597,13 +725,27 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
597
725
|
retry_wait=0.5,
|
|
598
726
|
timeout=3)
|
|
599
727
|
|
|
600
|
-
#
|
|
601
|
-
|
|
728
|
+
# trace.
|
|
729
|
+
_logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast device to activate: %s [ip=%s:%s]" % (self.name, deviceWaitTimeoutSecs, scDevice.Title, castInfo.host, castInfo.port))
|
|
730
|
+
|
|
731
|
+
# were we able to obtain the device reference? if not, then it's an error!
|
|
732
|
+
# this can happen if the device falls off the network and tries to be activated
|
|
733
|
+
# again before zeroconf detects that it was removed.
|
|
734
|
+
if (castDevice is None):
|
|
735
|
+
raise SpotifyApiError("Chromecast device %s [ip=%s] reference could not be obtained; check to ensure it is still available on the network" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), logsi=_logsi)
|
|
736
|
+
|
|
737
|
+
# wait for the device to become ready.
|
|
738
|
+
# this will raise an exception if the device is not available.
|
|
739
|
+
castDevice.wait(deviceWaitTimeoutSecs)
|
|
602
740
|
|
|
603
741
|
# trace.
|
|
604
742
|
if (_logsi.IsOn(SILevel.Verbose)):
|
|
605
743
|
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device object: \"%s\" (ip=%s:%s)" % (self.name, castInfo.friendly_name, castDevice.socket_client.host, castDevice.socket_client.port), castDevice)
|
|
606
|
-
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device status: %s [ip=%s:%s]" % (self.name, scDevice.Title, castDevice.socket_client.host, castDevice.socket_client.port), castDevice.status
|
|
744
|
+
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device status: %s [ip=%s:%s]" % (self.name, scDevice.Title, castDevice.socket_client.host, castDevice.socket_client.port), castDevice.status)
|
|
745
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
746
|
+
|
|
747
|
+
# verify multizone controller instance is registered for cast group device.
|
|
748
|
+
self._VerifyCastGroupMultiZoneController(castDevice, scDevice)
|
|
607
749
|
|
|
608
750
|
# start the Spotify Cast application on the Chromecast device.
|
|
609
751
|
# this will start the spotify app on the cast device.
|
|
@@ -655,6 +797,10 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
655
797
|
# at this point we have received an `adduserResponse` from the Chromecast device, indicating
|
|
656
798
|
# that it has launched the Spotify Cast App and that the user is logged in.
|
|
657
799
|
|
|
800
|
+
# trace.
|
|
801
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
802
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
803
|
+
|
|
658
804
|
# was transfer playback specified?
|
|
659
805
|
if (transferPlayback == True):
|
|
660
806
|
|
|
@@ -1675,6 +1821,19 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1675
1821
|
# remove the dynamic device from the collection.
|
|
1676
1822
|
self.RemoveDevice(spDynamicDevice.Id, dynamicDeviceOnly=True)
|
|
1677
1823
|
|
|
1824
|
+
try:
|
|
1825
|
+
|
|
1826
|
+
# register the multizone manager listener with each Chromecast non-group device.
|
|
1827
|
+
if (castInfo.cast_type != CAST_TYPE_GROUP):
|
|
1828
|
+
_logsi.LogVerbose("Chromecast Multizone Manager Listener is being added for non-group device: \"%s\"" % (castInfo.friendly_name))
|
|
1829
|
+
castMultiZoneManagerListener = SpotifyConnectZeroconfCastMultiZoneManagerListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, self._CastMultiZoneManager, castDevice)
|
|
1830
|
+
self._CastMultiZoneManagerListeners[str(castDevice.uuid)] = castMultiZoneManagerListener
|
|
1831
|
+
self._CastMultiZoneManager.register_listener(castDevice.uuid, castMultiZoneManagerListener)
|
|
1832
|
+
|
|
1833
|
+
except Exception as ex:
|
|
1834
|
+
|
|
1835
|
+
_logsi.LogException("%s - MultiZone Manager register_listener Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
|
|
1836
|
+
|
|
1678
1837
|
# is this a Google Cast Group?
|
|
1679
1838
|
if (castInfo.cast_type == CAST_TYPE_GROUP):
|
|
1680
1839
|
|
|
@@ -1698,6 +1857,14 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1698
1857
|
# RemoteName = "Kitchen Parlor"
|
|
1699
1858
|
# DeviceID = 3fb9b12379e4fa1957d065b4c4568939
|
|
1700
1859
|
|
|
1860
|
+
# verify multizone controller instance is registered for cast group device.
|
|
1861
|
+
self._VerifyCastGroupMultiZoneController(castDevice, scDevice)
|
|
1862
|
+
|
|
1863
|
+
# add group to multizone manager.
|
|
1864
|
+
# make sure we do this AFTER adding the MultizoneController listener.
|
|
1865
|
+
_logsi.LogVerbose("ChromeCast Multizone Manager is adding multizone group: \"%s\"" % (castDevice.cast_info.friendly_name))
|
|
1866
|
+
self._CastMultiZoneManager.add_multizone(castDevice)
|
|
1867
|
+
|
|
1701
1868
|
# ignore "update_cast" entries, as we don't want to create duplicates.
|
|
1702
1869
|
if (serviceType == "update_cast"):
|
|
1703
1870
|
_logsi.LogVerbose("SpotifyConnectDevice instance will be ignored for Google Cast Group data: \"%s\" (%s)" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name))
|
|
@@ -1716,36 +1883,6 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1716
1883
|
self._SpotifyConnectDevices.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
1717
1884
|
return
|
|
1718
1885
|
|
|
1719
|
-
# register a multizone listener to listen for group change events; these
|
|
1720
|
-
# events will (hopefully) help us to determine the leader of a group.
|
|
1721
|
-
try:
|
|
1722
|
-
|
|
1723
|
-
# is this an add_cast event?
|
|
1724
|
-
if (serviceType == "add_cast"):
|
|
1725
|
-
|
|
1726
|
-
# do we have a multizone controller registered for this device yet?
|
|
1727
|
-
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(scDevice.DiscoveryResult.Key, None)
|
|
1728
|
-
if (castMultizoneController is None):
|
|
1729
|
-
|
|
1730
|
-
# trace.
|
|
1731
|
-
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being added for Cast device: %s [%s]" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), colorValue=SIColors.Lavender)
|
|
1732
|
-
|
|
1733
|
-
# add cast device to multizone controller instance. this will add a listener
|
|
1734
|
-
# for multizone status events to be processed, which allows us to keep track
|
|
1735
|
-
# of which device is a group leader.
|
|
1736
|
-
castMultizoneController = MultizoneController(castDevice.uuid)
|
|
1737
|
-
castMultizoneController.register_listener(SpotifyConnectZeroconfCastMultiZoneListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, castMultizoneController, castDevice))
|
|
1738
|
-
castDevice.register_handler(castMultizoneController)
|
|
1739
|
-
castMultizoneController.update_members() # Request an initial status
|
|
1740
|
-
|
|
1741
|
-
# add cast multizone controller to active multizone controllers collection.
|
|
1742
|
-
self._CastMultiZoneControllers[str(castDevice.uuid)] = castMultizoneController
|
|
1743
|
-
|
|
1744
|
-
except Exception as ex:
|
|
1745
|
-
|
|
1746
|
-
# trace.
|
|
1747
|
-
_logsi.LogException("%s - MultiZoneController register_listener Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
|
|
1748
|
-
|
|
1749
1886
|
# add new Spotify Connect Device to devices collection.
|
|
1750
1887
|
self._SpotifyConnectDevices.Items.append(scDevice)
|
|
1751
1888
|
self._SpotifyConnectDevices.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
@@ -1779,6 +1916,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1779
1916
|
|
|
1780
1917
|
# did the device name change? if so, then update name and id properties,
|
|
1781
1918
|
# as well as the corresponding getInfo properties.
|
|
1919
|
+
# note that the discovery key value does not change!
|
|
1782
1920
|
deviceNameChanged:bool = False
|
|
1783
1921
|
if (scDevice.Name != zeroconfDiscoveryResult.DeviceName):
|
|
1784
1922
|
|
|
@@ -1899,26 +2037,40 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1899
2037
|
|
|
1900
2038
|
else:
|
|
1901
2039
|
|
|
1902
|
-
#
|
|
2040
|
+
# get device information.
|
|
2041
|
+
scDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.Items[idx]
|
|
2042
|
+
|
|
1903
2043
|
try:
|
|
1904
2044
|
|
|
1905
|
-
#
|
|
2045
|
+
# unregister multizone controller listener for the device.
|
|
1906
2046
|
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(zeroconfDiscoveryResult.Key, None)
|
|
1907
2047
|
if (castMultizoneController is not None):
|
|
2048
|
+
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being removed for Cast device: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key), colorValue=SIColors.Lavender)
|
|
2049
|
+
castMultizoneController.tear_down()
|
|
2050
|
+
self._CastMultiZoneControllers.pop(zeroconfDiscoveryResult.Key, None)
|
|
1908
2051
|
|
|
1909
|
-
|
|
1910
|
-
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being removed: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key), colorValue=SIColors.Lavender)
|
|
2052
|
+
except Exception as ex:
|
|
1911
2053
|
|
|
1912
|
-
|
|
1913
|
-
|
|
2054
|
+
# trace.
|
|
2055
|
+
_logsi.LogException("Chromecast MultiZoneController tear_down Exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
1914
2056
|
|
|
1915
|
-
|
|
1916
|
-
|
|
2057
|
+
try:
|
|
2058
|
+
|
|
2059
|
+
# remove group from multizone manager.
|
|
2060
|
+
# make sure we do this AFTER removing the MultizoneController listener.
|
|
2061
|
+
_logsi.LogVerbose("Chromecast Multizone Manager is removing multizone device: \"%s\"" % (scDevice.Title))
|
|
2062
|
+
self._CastMultiZoneManager.remove_multizone(scDevice.DiscoveryResult.Key)
|
|
2063
|
+
|
|
2064
|
+
# deregister multizone manager listener for the device.
|
|
2065
|
+
_logsi.LogVerbose("Chromecast Multizone Manager Listener is deregistering for device: \"%s\"" % (scDevice.Title))
|
|
2066
|
+
castMultiZoneManagerListener = self._CastMultiZoneManagerListeners.pop(scDevice.DiscoveryResult.Key, None)
|
|
2067
|
+
if castMultiZoneManagerListener:
|
|
2068
|
+
self._CastMultiZoneManager.deregister_listener(scDevice.DiscoveryResult.Key, castMultiZoneManagerListener)
|
|
1917
2069
|
|
|
1918
2070
|
except Exception as ex:
|
|
1919
2071
|
|
|
1920
2072
|
# trace.
|
|
1921
|
-
_logsi.LogException("
|
|
2073
|
+
_logsi.LogException("Chromecast MultizoneManager deregister_listener Exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
1922
2074
|
|
|
1923
2075
|
# trace.
|
|
1924
2076
|
_logsi.LogVerbose("Removing existing SpotifyConnectDevice instance from CastInfo data: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key))
|
|
@@ -151,11 +151,11 @@ class SpotifyConnectZeroconfCastController(BaseController):
|
|
|
151
151
|
"""
|
|
152
152
|
Callback function
|
|
153
153
|
"""
|
|
154
|
-
# for cast groups, we will wait a
|
|
154
|
+
# for cast groups, we will wait a few seconds for any cast messages to be
|
|
155
155
|
# processed, before we query the device for Spotify Connect getInfo.
|
|
156
156
|
if (self.castDevice.cast_type == CAST_TYPE_GROUP):
|
|
157
157
|
delay:float = 3.0
|
|
158
|
-
_logsi.LogVerbose("Cast controller is for a Cast Group request; delaying processing for %s seconds to allow cast zeroconf responses to be processed" % (str(delay)), colorValue=SIColors.
|
|
158
|
+
_logsi.LogVerbose("Cast controller is for a Cast Group request; delaying processing for %s seconds to allow cast zeroconf responses to be processed" % (str(delay)), colorValue=SIColors.Coral)
|
|
159
159
|
time.sleep(delay)
|
|
160
160
|
|
|
161
161
|
# send the initial getInfo request after launching the app.
|