-
-
Notifications
You must be signed in to change notification settings - Fork 37.2k
Add hivi speaker integration #165307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
swansmart
wants to merge
143
commits into
home-assistant:dev
Choose a base branch
from
swansmart:add-hivi-speaker-integration
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add hivi speaker integration #165307
Changes from all commits
Commits
Show all changes
143 commits
Select commit
Hold shift + click to select a range
ce837b9
Add HiVi Speaker integration
swansmart defeaef
Add HiVi Speaker integration for tests
swansmart 2f67448
Refactor config entry removal and device cleanup
swansmart 4bc2bae
Refactor device data cleanup and listener management
swansmart 2221a52
Update post-initialization method in device.py
swansmart 2b30d39
Refactor callback handling and improve operation management
swansmart 0e253ab
Update requirements for hivico to exact version
swansmart bf15185
Refactor directory scanning and remove unused code
swansmart 85a953b
Refactor service registration for speaker devices
swansmart 5f40524
Refactor device registry handling in HIVI switch
swansmart 85aa187
Update media_player.py
swansmart 2912df4
Implement error handling for missing master device
swansmart a81487b
Handle missing device_info in discovery scheduler
swansmart a0157c5
Refactor device data methods for clarity and efficiency
swansmart cc64cec
Refactor fields to use default factories in device model
swansmart 541852f
Refactor device management and update logging
swansmart 5d94503
Implement persistent storage for device data
swansmart e00fa02
Refactor options flow tests to use AsyncMock
swansmart 98bf04d
Delete homeassistant/components/hivi_speaker/translations/en.json
swansmart 46ef76f
Delete homeassistant/components/hivi_speaker/translations/zh-Hans.json
swansmart 7766b26
Add files via upload
swansmart f15f3d6
Add hivico dependency for hivi_speaker component
swansmart c9102ca
Remove unused switch_entities attribute
swansmart 9dd3630
Implement delayed save for device data registry
swansmart 3a8ed7c
Clean up device_manager.py by removing unused code
swansmart f2dcb15
Refactor discovery scheduler for improved readability
swansmart dcb7ca2
Replace asyncio.create_task with hass.async_create_task
swansmart 286da40
Refactor logging and XML handling in media player
swansmart 518ea8f
Improve logging for master and slave device lookups
swansmart 388a023
Refactor exception handling and improve docstrings
swansmart b9cc3ca
Update docstrings for HIVI speaker config flow
swansmart e5d896b
Add SIGNAL_DEVICE_STATUS_UPDATED constant
swansmart ddb7e9d
Refactor device data registry for type hints
swansmart 7ed0ff2
Refactor device manager for HiVi Speaker integration
swansmart 09f84f6
Refactor device.py to use optional type hints
swansmart b5d7014
Refactor discovery scheduler for HiVi Speaker
swansmart f4911d3
Refactor type hints and improve docstrings
swansmart 52b6c40
Remove dlna_dms dependency from manifest.json
swansmart 2b50d84
Delete homeassistant/components/hivi_speaker/media_player.py
swansmart 1761815
Refactor ha_device_id assignment for clarity
swansmart 604d0e2
Reformat services.yaml for HiVi Speaker integration
swansmart f32171a
Refactor HiVi Speaker switch integration code
swansmart e34c895
Update homeassistant/components/hivi_speaker/manifest.json
swansmart e1c6101
Refactor device removal process in HIVI speaker
swansmart b8bca57
Refactor device removal methods for clarity
swansmart 4916a18
Disable debug logging for HivicoClient
swansmart 947e5ee
Refactor online/offline device count calculation
swansmart 3648c60
Add method to get connection status counts
swansmart 14d2cfe
Initialize _attr_is_on to False in switch.py
swansmart 43df0a1
Remove persistent device data storage on entry removal
swansmart 08a5022
Refactor discovery task and update device dict handling
swansmart 38a1e63
Clarify comment for handling all online devices
swansmart e2c314d
Update group_coordinator.py
swansmart 6adbee8
Update discovery_scheduler.py
swansmart 9616745
Update __init__.py
swansmart 68a0a1b
Update config_flow.py
swansmart 9434023
Update device_data_registry.py
swansmart 6b3e028
Update device_manager.py
swansmart cb99f06
Update device.py
swansmart e3a518b
Update discovery_scheduler.py
swansmart 46cfa07
Update group_coordinator.py
swansmart 49c6697
Update switch.py
swansmart 1e24e68
Create quality_scale.yaml
swansmart fad531f
Update test_config_flow.py
swansmart c2fc6cc
Remove the use of service functions.
swansmart 9037f3e
Remove the use of service functions
swansmart 9bf6fba
Remove the use of service functions
swansmart a160a80
Delete services.yaml
swansmart da97e59
Delete services.py
swansmart 23474de
Add hivico for hivi_speaker
swansmart b35e6e8
Update test_config_flow.py
swansmart 1fe7e85
Update __init__.py
swansmart 6113a60
Update config_flow.py
swansmart 3d5845b
Update device_data_registry.py
swansmart 8dafe16
Update device_manager.py
swansmart 46ca1a4
Update device_manager.py
swansmart 525af44
Update discovery_scheduler.py
swansmart 8126300
Update device_manager.py
swansmart e78e623
Update group_coordinator.py
swansmart f281b41
Update manifest.json
swansmart c8a288a
Update quality_scale.yaml
swansmart 49aa977
Update strings.json
swansmart f2d95d5
Update switch.py
swansmart 438b47b
Update config_flow.py
swansmart b6df665
Update manifest.json
swansmart 802f854
Update switch.py
swansmart f3b3786
Update manifest.json
swansmart 15e751c
Update switch.py
swansmart 279ee6a
Update __init__.py
swansmart 80fa817
Update device_manager.py
swansmart 4c75005
Update discovery_scheduler.py
swansmart b614bda
Update group_coordinator.py
swansmart 3b6874d
Update switch.py
swansmart 8231948
Update __init__.py
swansmart b70ca70
Update discovery_scheduler.py
swansmart 42cfbb9
Update group_coordinator.py
swansmart f3800a3
Update __init__.py
swansmart e09f912
Update device_manager.py
swansmart bd5beaf
Create switch_hub.py
swansmart 68fae9b
Update discovery_scheduler.py
swansmart 5f6d96f
Update group_coordinator.py
swansmart d10e349
Update switch.py
swansmart a4e5e2d
Update __init__.py
swansmart cd0f3f3
Update device_manager.py
swansmart 282ee85
Update group_coordinator.py
swansmart d696262
Add CODEOWNERS for hivi_speaker component
swansmart 88dfa19
Add HiVi Speaker integration to integrations.json
swansmart ae7128c
Add 'hivi_speaker' to config flows
swansmart a6ece9e
Update homeassistant/components/hivi_speaker/discovery_scheduler.py
swansmart 43f55a8
Update tests/components/hivi_speaker/conftest.py
swansmart 61a8228
Remove default parameter in get_device_dict_by_ha_device_id
swansmart f6e3047
Refactor device data registry and remove listeners
swansmart 130dda8
Remove default=None from get_device_dict_by_ha_device_id calls
swansmart 42d0d3a
Update homeassistant/components/hivi_speaker/switch.py
swansmart a67f30a
Update tests/components/hivi_speaker/test_config_flow.py
swansmart 699da7d
Fix typo in variable name from 'entiy' to 'entity'
swansmart bfe6481
Change return type of mock_setup_entry fixture
swansmart 51f2bd6
Update homeassistant/components/hivi_speaker/device_manager.py
swansmart ce11c96
Merge branch 'dev' into add-hivi-speaker-integration
swansmart 3de4cd0
modify format
swansmart f0428b8
Update discovery_scheduler.py
swansmart 4f8e115
Update homeassistant/components/hivi_speaker/device.py
swansmart 9f9e2c8
Update homeassistant/components/hivi_speaker/switch.py
swansmart 9339a07
Update homeassistant/components/hivi_speaker/device.py
swansmart ae4c981
update
swansmart 715a55c
Add already_configured
swansmart 2d14cba
update format
swansmart d85009c
Merge branch 'home-assistant:dev' into add-hivi-speaker-integration
swansmart 136c776
add tz=UTC
swansmart 1abe0c5
Merge branch 'add-hivi-speaker-integration' of https://github.com/swa…
swansmart a249e6d
Update device_manager.py
swansmart a02e313
update device_manager.py format
swansmart 7e87200
update or add test file
swansmart 97e0cd0
update test file
swansmart 338a10b
add or update test file
swansmart da6078d
update format
swansmart d2a119e
update format
swansmart d4ce748
add docstring in public function
swansmart 473a5eb
Merge branch 'home-assistant:dev' into add-hivi-speaker-integration
swansmart 2193ac7
Added verification checks
swansmart 8d8ea03
Merge branch 'add-hivi-speaker-integration' of https://github.com/swa…
swansmart 7d336ba
Merge branch 'dev' into add-hivi-speaker-integration
swansmart 892a21d
Merge branch 'dev' into add-hivi-speaker-integration
swansmart File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| """The hivi_speaker integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| from typing import Any | ||
|
|
||
| from homeassistant.config_entries import ConfigEntry | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers import device_registry as dr | ||
| from homeassistant.helpers.device_registry import DeviceEntry | ||
| from homeassistant.helpers.storage import Store | ||
|
|
||
| from .const import DOMAIN | ||
| from .device import HIVIDevice | ||
| from .device_manager import HIVIDeviceManager | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| PLATFORMS = ["switch"] | ||
|
|
||
|
|
||
| async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: | ||
| """Set up the config entry.""" | ||
| device_manager = HIVIDeviceManager(hass, config_entry) | ||
| await device_manager.async_setup() | ||
|
|
||
| hass.data.setdefault(DOMAIN, {}) | ||
| hass.data[DOMAIN].setdefault(config_entry.entry_id, {}) | ||
| hass.data[DOMAIN][config_entry.entry_id]["device_manager"] = device_manager | ||
|
|
||
| await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
| async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
| """Unload config entry and clean up related resources.""" | ||
| _LOGGER.debug("Starting to unload config entry %s", entry.entry_id) | ||
|
|
||
| data = hass.data.get(DOMAIN, {}).get(entry.entry_id, {}) | ||
|
|
||
| device_manager = data.get("device_manager") | ||
| if device_manager: | ||
| try: | ||
| await device_manager.async_cleanup() | ||
| except Exception: | ||
| _LOGGER.exception("Exception occurred while cleaning up device_manager") | ||
|
|
||
| try: | ||
| unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) | ||
| except Exception: | ||
| _LOGGER.exception("Failed to call async_unload_platforms") | ||
| unload_ok = False | ||
|
|
||
| try: | ||
| if DOMAIN in hass.data and entry.entry_id in hass.data[DOMAIN]: | ||
| hass.data[DOMAIN].pop(entry.entry_id, None) | ||
| if not hass.data[DOMAIN]: | ||
| hass.data.pop(DOMAIN, None) | ||
| except Exception: | ||
| _LOGGER.exception("Exception occurred while cleaning up hass.data") | ||
|
|
||
| if not unload_ok: | ||
| _LOGGER.error( | ||
| "Platform unloading partially failed (unload_ok=False), there may be residual entities or platforms" | ||
| ) | ||
| else: | ||
| _LOGGER.debug("Config entry %s unloaded successfully", entry.entry_id) | ||
|
|
||
| return unload_ok | ||
|
|
||
|
|
||
| async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: | ||
| """Remove config entry and clean up devices and persistent storage.""" | ||
| _LOGGER.debug("Removing config entry %s – cleaning up devices", entry.entry_id) | ||
|
|
||
| try: | ||
| dev_reg = dr.async_get(hass) | ||
| for device in list(dev_reg.devices.values()): | ||
| config_entries = getattr(device, "config_entries", None) | ||
| if (config_entries and entry.entry_id in config_entries) or getattr( | ||
| device, "config_entry_id", None | ||
| ) == entry.entry_id: | ||
| dev_reg.async_remove_device(device.id) | ||
| except Exception: | ||
| _LOGGER.exception("Error cleaning up device registry on entry removal") | ||
|
|
||
| try: | ||
| store: Store[Any] = Store(hass, 1, "hivi_speaker_device_data") | ||
| await store.async_remove() | ||
| _LOGGER.debug("Persistent device data storage removed") | ||
| except Exception: | ||
| _LOGGER.exception("Error removing persistent storage on entry removal") | ||
|
|
||
|
|
||
| async def async_remove_config_entry_device( | ||
| hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry | ||
| ) -> bool: | ||
| """Handle removal of a device from the UI/device registry. | ||
|
|
||
| Return True to allow Home Assistant to remove the device entry. | ||
| Only devices belonging to this integration (DOMAIN in identifiers) are cleaned up. | ||
| """ | ||
| if not any(identifier[0] == DOMAIN for identifier in device_entry.identifiers): | ||
| return False | ||
|
|
||
| try: | ||
| domain_data = hass.data.get(DOMAIN, {}).get(config_entry.entry_id, {}) | ||
| device_manager = domain_data.get("device_manager") | ||
| if device_manager is None: | ||
| _LOGGER.debug( | ||
| "Device manager not found for entry %s, allowing device deletion %s", | ||
| config_entry.entry_id, | ||
| device_entry.id, | ||
| ) | ||
| return True | ||
|
|
||
| ha_device_id = device_entry.id | ||
| speaker_device_id = None | ||
| device_dict = ( | ||
| device_manager.device_data_registry.get_device_dict_by_ha_device_id( | ||
| ha_device_id | ||
| ) | ||
| ) | ||
| if device_dict is not None: | ||
| device_obj = HIVIDevice(**device_dict) | ||
| speaker_device_id = device_obj.speaker_device_id | ||
|
|
||
| await device_manager.async_remove_entities_for_device(ha_device_id) | ||
| await device_manager.device_data_registry.async_remove_device_data(ha_device_id) | ||
| _LOGGER.debug( | ||
| "Cleaned up entities and data for device %s, HA will remove the device entry", | ||
| ha_device_id, | ||
| ) | ||
|
|
||
| if speaker_device_id: | ||
| await device_manager.remove_control_entities_by_speaker_device_id( | ||
| speaker_device_id | ||
| ) | ||
| _LOGGER.debug( | ||
| "Requested device manager to delete control entities for speaker device ID %s", | ||
| speaker_device_id, | ||
| ) | ||
|
|
||
| except Exception: | ||
| _LOGGER.exception( | ||
| "Error in async_remove_config_entry_device for device %s", | ||
| device_entry.id, | ||
| ) | ||
| return True | ||
| else: | ||
| return True | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| """Config flow for HiVi Speaker integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| from typing import Any | ||
|
|
||
| import voluptuous as vol | ||
|
|
||
| from homeassistant import config_entries | ||
| from homeassistant.config_entries import ( | ||
| ConfigEntry, | ||
| ConfigFlow, | ||
| ConfigFlowResult, | ||
| OptionsFlow, | ||
| ) | ||
| from homeassistant.core import callback | ||
|
|
||
| from .const import DOMAIN | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class HIVISpeakerConfigFlow(ConfigFlow, domain=DOMAIN): | ||
| """HIVI speaker configuration flow.""" | ||
|
|
||
| async def async_step_user( | ||
| self, user_input: dict[str, Any] | None = None | ||
| ) -> ConfigFlowResult: | ||
| """User step - configure integration.""" | ||
| await self.async_set_unique_id(DOMAIN) | ||
| self._abort_if_unique_id_configured() | ||
| return self.async_create_entry(title="HiVi Speaker", data={}) | ||
|
|
||
| @staticmethod | ||
| @callback | ||
| def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: | ||
| """Get options flow.""" | ||
| return HIVISpeakerOptionsFlow() | ||
|
|
||
|
|
||
| class HIVISpeakerOptionsFlow(config_entries.OptionsFlow): | ||
| """Options flow - using confirmation switch.""" | ||
|
|
||
| def __init__(self) -> None: | ||
| """Initialize the options flow.""" | ||
| super().__init__() | ||
| self.open_num = 0 | ||
|
|
||
| async def async_step_init( | ||
| self, user_input: dict[str, Any] | None = None | ||
| ) -> ConfigFlowResult: | ||
| """Show initial step of the options flow.""" | ||
| _LOGGER.debug("Entering initial step of options flow") | ||
| self.open_num = 0 | ||
| if user_input is not None: | ||
| if user_input.get("confirm_refresh"): | ||
| domain_data = self.hass.data.get(DOMAIN, {}).get( | ||
| self.config_entry.entry_id, {} | ||
| ) | ||
| device_manager = domain_data.get("device_manager") | ||
| if device_manager is not None: | ||
| await device_manager.refresh_discovery() | ||
| else: | ||
| _LOGGER.warning( | ||
| "Device manager not available; skipping refresh from options" | ||
| ) | ||
| return await self.async_step_success() | ||
|
|
||
| return self.async_create_entry(title="", data={}) | ||
|
|
||
| return self.async_show_form( | ||
| step_id="init", | ||
| data_schema=vol.Schema( | ||
| { | ||
| vol.Required("confirm_refresh", default=True): bool, | ||
| } | ||
| ), | ||
| ) | ||
|
|
||
| async def async_step_success( | ||
| self, user_input: dict[str, Any] | None = None | ||
| ) -> ConfigFlowResult: | ||
| """Success page.""" | ||
| _LOGGER.debug("Displaying success page") | ||
| if self.open_num == 0: | ||
| self.open_num += 1 | ||
| return self.async_show_form( | ||
| step_id="success", | ||
| data_schema=vol.Schema({}), | ||
| ) | ||
| return self.async_create_entry(title="", data={}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| """Constants for the HiVi Speaker integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| DOMAIN = "hivi_speaker" | ||
|
|
||
| DISCOVERY_UPDATED = "hivi_speaker_discovery_updated" | ||
| DEVICE_RELATION_CHANGED = "hivi_speaker_device_relation_changed" | ||
| SIGNAL_DEVICE_DISCOVERED = "hivi_speaker_signal_device_discovered" | ||
swansmart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| SIGNAL_DEVICE_STATUS_UPDATED = "hivi_speaker_signal_device_status_updated" | ||
|
|
||
| DISCOVERY_BASE_INTERVAL = 300 # seconds | ||
| DEVICE_OFFLINE_THRESHOLD = 180 # seconds | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| """Device models for the HiVi Speaker integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from datetime import UTC, datetime | ||
| from enum import Enum | ||
|
|
||
| from pydantic import BaseModel, Field | ||
|
|
||
|
|
||
| class SyncGroupStatus(Enum): | ||
| """Sync group status or master/slave relationship status of a device.""" | ||
|
|
||
| UNKNOWN = "unknown" | ||
| MASTER = "master" | ||
| SLAVE = "slave" | ||
| STANDALONE = "standalone" | ||
|
|
||
|
|
||
| class ConnectionStatus(Enum): | ||
| """Device status.""" | ||
|
|
||
| ONLINE = "online" | ||
| OFFLINE = "offline" | ||
| UNAVAILABLE = "unavailable" | ||
|
|
||
|
|
||
| class SlaveDeviceInfo(BaseModel): | ||
| """Slave speaker information class.""" | ||
|
|
||
| friendly_name: str | ||
| ssid: str | ||
| mask: int | None | ||
| volume: int | ||
| mute: bool | ||
| channel: int | ||
| battery: int | None | ||
| ip_addr: str | ||
| version: str | ||
| uuid: str | ||
|
|
||
|
|
||
| class HIVIDevice(BaseModel): | ||
| """HIVI speaker device base class.""" | ||
|
|
||
| speaker_device_id: str = "" | ||
| unique_id: str = "" | ||
| friendly_name: str = "" | ||
| model: str = "" | ||
| manufacturer: str = "" | ||
| ha_device_id: str = "" | ||
| hardware: str = "" | ||
|
|
||
| ip_addr: str = "" | ||
| mac_address: str = "" | ||
| hostname: str = "" | ||
|
|
||
| supports_dlna: bool = True | ||
| supports_private_protocol: bool = True | ||
| sync_group_status: SyncGroupStatus = SyncGroupStatus.STANDALONE | ||
|
|
||
| connection_status: ConnectionStatus = ConnectionStatus.ONLINE | ||
| last_seen: datetime = Field(default_factory=lambda: datetime.now(tz=UTC)) | ||
|
|
||
| master_speaker_device_id: str | None = None | ||
| slave_device_num: int = 0 | ||
| slave_device_list: list[SlaveDeviceInfo] = Field(default_factory=list) | ||
|
|
||
| dlna_udn: str | None = None | ||
| dlna_location: str | None = None | ||
|
|
||
| private_protocol_version: str | None = None | ||
| private_port: int = 9527 | ||
|
|
||
| entity_id: str | None = None | ||
| config_entry_id: str | None = None | ||
|
|
||
| wifi_channel: str = "0" | ||
| ssid: str | None = None | ||
| auth_mode: str | None = None | ||
| encryption_mode: str | None = None | ||
| psk: str | None = None | ||
| uuid: str | None = None | ||
|
|
||
| def model_post_init(self, __context, /) -> None: | ||
| """Auto-generate unique_id from mac_address if not provided.""" | ||
| if not self.unique_id: | ||
| mac_address = self.mac_address.replace(":", "") | ||
| if mac_address: | ||
| self.unique_id = f"hivi_{mac_address}" | ||
|
|
||
| @property | ||
| def is_available_for_media(self) -> bool: | ||
| """Whether available as media player (non-slave speaker).""" | ||
| return ( | ||
| self.sync_group_status != SyncGroupStatus.SLAVE | ||
| and self.connection_status == ConnectionStatus.ONLINE | ||
| ) | ||
|
|
||
| @property | ||
| def can_be_master(self) -> bool: | ||
| """Whether can be set as master speaker.""" | ||
| return ( | ||
| self.sync_group_status | ||
| in {SyncGroupStatus.MASTER, SyncGroupStatus.STANDALONE} | ||
| and self.connection_status == ConnectionStatus.ONLINE | ||
| ) | ||
|
|
||
| @property | ||
| def can_be_slave(self) -> bool: | ||
| """Whether can be set as slave speaker.""" | ||
| return ( | ||
| self.sync_group_status == SyncGroupStatus.STANDALONE | ||
| and self.connection_status == ConnectionStatus.ONLINE | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.