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.
Files changed (25) hide show
  1. spotifywebapipython/const.py +1 -1
  2. spotifywebapipython/docs/__init__.html +9 -2
  3. spotifywebapipython/docs/index.html +2 -2
  4. spotifywebapipython/docs/search.js +1 -1
  5. spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
  6. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
  7. spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
  8. spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
  9. spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
  10. spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
  11. spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +2 -2
  12. spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
  13. spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
  14. spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
  15. spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
  16. spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +228 -76
  17. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py +2 -2
  18. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonecontrollerlistener.py +247 -0
  19. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonemanagerlistener.py +384 -0
  20. {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/METADATA +1 -1
  21. {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/RECORD +24 -23
  22. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonelistener.py +0 -212
  23. {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/LICENSE +0 -0
  24. {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.244.dist-info}/WHEEL +0 -0
  25. {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
- from pychromecast.dial import MultizoneInfo, MultizoneStatus, get_multizone_status
9
- from pychromecast.controllers.multizone import MultizoneController
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 .spotifyconnectzeroconfcastmultizonelistener import SpotifyConnectZeroconfCastMultiZoneListener
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
- # get current multizone status for all groups.
548
- castMultiZoneStatus:MultizoneStatus = get_multizone_status(castInfo.host, castInfo.services, self.ZeroconfInstance, 5)
549
- _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)
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
- # find the group coordinator for the selected device.
556
- zoneInfo:MultizoneInfo = None
557
- for zoneInfo in castMultiZoneStatus.groups:
663
+ try:
558
664
 
559
- # is this our group?
560
- if (str(zoneInfo.uuid) == scDevice.DiscoveryResult.Key):
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
- # yes - get host info to use for the device.
563
- # we will use the device discovery result HostIpAddress instead of the zoneInfo.host
564
- # value here, as the zoneInfo MAY contain an IPV6 formatted address, which was causing
565
- # issues if the device was originally activated using an IPV4 address!
566
- groupHost = scDevice.DiscoveryResult.HostIpAddress
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
- # connect to the device and build a Chromecast instance from group coordinator host info.
571
- castDevice = get_chromecast_from_host(
572
- host=(groupHost, groupPort, castInfo.uuid, castInfo.model_name, castInfo.friendly_name),
573
- tries=2,
574
- retry_wait=0.5,
575
- timeout=5)
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
- # calculate wait timeout for devices to become active, based on number of devices in group.
578
- groupWaitTimeoutSecs:float = 15.0
579
- if (castMultiZoneStatus.groups is not None):
580
- groupCount:int = len(castMultiZoneStatus.groups)
581
- if (groupCount > 1):
582
- groupWaitTimeoutSecs = (groupCount * 7.5)
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
- # wait for the group device to become ready.
585
- _logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast group multizone device to activate: %s [ip=%s:%s]" % (self.name, groupWaitTimeoutSecs, scDevice.Title, castInfo.host, castInfo.port), colorValue=SIColors.Coral)
586
- castDevice.wait(groupWaitTimeoutSecs)
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
- # wait for the device to become ready (10 seconds max).
601
- castDevice.wait(10)
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, colorValue=SIColors.Coral)
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
- # unregister the multizone listener, if one was registered.
2040
+ # get device information.
2041
+ scDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.Items[idx]
2042
+
1903
2043
  try:
1904
2044
 
1905
- # do we have a multizone controller registered for this device?
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
- # trace.
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
- # tear down the listener.
1913
- castMultizoneController.tear_down()
2054
+ # trace.
2055
+ _logsi.LogException("Chromecast MultiZoneController tear_down Exception: %s" % (str(ex)), ex, logToSystemLogger=False)
1914
2056
 
1915
- # remove cast multizone controller from active multizone controllers collection.
1916
- self._CastMultiZoneControllers.pop(zeroconfDiscoveryResult.Key, None)
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("%s - MultiZoneController register_listener Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
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 couple of seconds for any cast messages to be
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.Red)
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.