-
Notifications
You must be signed in to change notification settings - Fork 0
Add player join/leave events to Satisfactory integration #3
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
37f6ada
Add player join/leave events to Satisfactory integration
Programmer-Timmy 401c7dc
Add event handling for player activity in Satisfactory
Programmer-Timmy 3dfe75e
Merge branch 'main' into events
Programmer-Timmy 45cf8e6
Initial plan
Copilot 6db80b5
Apply suggestions from code review
Programmer-Timmy c42eddd
Add entry_id to player activity event data for multi-server disambigu…
Copilot 53fd9a7
Update docs and Dutch translations; verify ruff passes
Copilot 60cdc20
Revert Dutch translation changes (handled by gitlocalize)
Copilot aa5d1ed
Merge branch 'events' into copilot/sub-pr-3
Programmer-Timmy 845ef8b
Merge pull request #4 from Programmer-Timmy/copilot/sub-pr-3
Programmer-Timmy 53b84b4
Remove entry_id from player event data
Programmer-Timmy 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
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
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,87 @@ | ||
| """Event platform for the Satisfactory integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from homeassistant.components.event import EventEntity | ||
| from homeassistant.core import callback | ||
| from homeassistant.helpers.device_registry import DeviceInfo | ||
| from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
|
||
| from .const import DOMAIN | ||
| from .coordinator import SatisfactoryCoordinator | ||
|
|
||
| if TYPE_CHECKING: | ||
| from homeassistant.config_entries import ConfigEntry | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
|
|
||
| PARALLEL_UPDATES = 0 | ||
|
|
||
|
|
||
| async def async_setup_entry( | ||
| hass: HomeAssistant, # noqa: ARG001 | ||
| entry: ConfigEntry, | ||
| async_add_entities: AddEntitiesCallback, | ||
| ) -> None: | ||
| """Set up Satisfactory event entities from a config entry.""" | ||
| coordinator: SatisfactoryCoordinator = entry.runtime_data | ||
| async_add_entities([SatisfactoryPlayerActivityEventEntity(coordinator, entry)]) | ||
|
|
||
|
|
||
| class SatisfactoryPlayerActivityEventEntity( | ||
| CoordinatorEntity[SatisfactoryCoordinator], EventEntity | ||
| ): | ||
| """Event entity that fires when players join or leave the server.""" | ||
|
|
||
| _attr_event_types: list[str] = ["player_joined", "player_left"] # noqa: RUF012 | ||
| _attr_translation_key = "player_activity" | ||
| _attr_has_entity_name = True | ||
|
|
||
| def __init__( | ||
| self, | ||
| coordinator: SatisfactoryCoordinator, | ||
| entry: ConfigEntry, | ||
| ) -> None: | ||
| """Initialize the event entity.""" | ||
| super().__init__(coordinator) | ||
| self._entry_id = entry.entry_id | ||
| self._attr_unique_id = f"{entry.unique_id}_player_activity" | ||
| self._attr_device_info = DeviceInfo( | ||
| identifiers={(DOMAIN, entry.unique_id or entry.entry_id)}, | ||
|
Programmer-Timmy marked this conversation as resolved.
|
||
| name=entry.title, | ||
| manufacturer="Coffee Stain Studios", | ||
| model="Satisfactory Dedicated Server", | ||
| configuration_url=entry.data.get("configuration_url") | ||
| if isinstance(entry.data, dict) | ||
| else None, | ||
| ) | ||
| self._prev_players: int | None = None | ||
|
|
||
| @callback | ||
| def _handle_coordinator_update(self) -> None: | ||
| """Trigger a player event when the connected player count changes.""" | ||
| new_players: int = self.coordinator.data.get("numConnectedPlayers", 0) | ||
| player_limit: int = self.coordinator.data.get("playerLimit", 0) | ||
|
|
||
| if self._prev_players is not None: | ||
| if new_players > self._prev_players: | ||
| self._trigger_event( | ||
| "player_joined", | ||
| { | ||
| "num_connected_players": new_players, | ||
| "player_limit": player_limit, | ||
| }, | ||
| ) | ||
|
Programmer-Timmy marked this conversation as resolved.
|
||
| elif new_players < self._prev_players: | ||
| self._trigger_event( | ||
| "player_left", | ||
| { | ||
| "num_connected_players": new_players, | ||
| "player_limit": player_limit, | ||
| }, | ||
| ) | ||
|
|
||
| self._prev_players = new_players | ||
| super()._handle_coordinator_update() | ||
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
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
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,95 @@ | ||
| """Tests for the Satisfactory event platform.""" | ||
|
|
||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
|
|
||
| from custom_components.satisfactory.event import SatisfactoryPlayerActivityEventEntity | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_coordinator() -> MagicMock: | ||
| """Return a mock coordinator with sample data.""" | ||
| coordinator = MagicMock() | ||
| coordinator.data = {"numConnectedPlayers": 2, "playerLimit": 4} | ||
| return coordinator | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def entity( | ||
| mock_coordinator: MagicMock, | ||
| ) -> SatisfactoryPlayerActivityEventEntity: | ||
| """Return a SatisfactoryPlayerActivityEventEntity with mocked dependencies.""" | ||
| with patch("homeassistant.helpers.update_coordinator.CoordinatorEntity.__init__"): | ||
| ent = SatisfactoryPlayerActivityEventEntity.__new__( | ||
| SatisfactoryPlayerActivityEventEntity | ||
| ) | ||
| ent.coordinator = mock_coordinator | ||
| ent._prev_players = None # noqa: SLF001 | ||
| ent.async_write_ha_state = MagicMock() | ||
| return ent | ||
|
|
||
|
|
||
| class TestSatisfactoryPlayerActivityEventEntity: | ||
| """Tests for SatisfactoryPlayerActivityEventEntity.""" | ||
|
|
||
| def test_event_types(self, entity: SatisfactoryPlayerActivityEventEntity) -> None: | ||
| assert entity.event_types == ["player_joined", "player_left"] | ||
|
|
||
| def test_player_joined_fires_event( | ||
| self, | ||
| entity: SatisfactoryPlayerActivityEventEntity, | ||
| mock_coordinator: MagicMock, | ||
| ) -> None: | ||
| entity._prev_players = 1 # noqa: SLF001 | ||
| mock_coordinator.data = {"numConnectedPlayers": 2, "playerLimit": 4} | ||
| with patch.object(entity, "_trigger_event") as mock_trigger: | ||
| entity._handle_coordinator_update() # noqa: SLF001 | ||
| mock_trigger.assert_called_once_with( | ||
| "player_joined", | ||
| {"num_connected_players": 2, "player_limit": 4}, | ||
| ) | ||
|
|
||
| def test_player_left_fires_event( | ||
| self, | ||
| entity: SatisfactoryPlayerActivityEventEntity, | ||
| mock_coordinator: MagicMock, | ||
| ) -> None: | ||
| entity._prev_players = 3 # noqa: SLF001 | ||
| mock_coordinator.data = {"numConnectedPlayers": 2, "playerLimit": 4} | ||
| with patch.object(entity, "_trigger_event") as mock_trigger: | ||
| entity._handle_coordinator_update() # noqa: SLF001 | ||
| mock_trigger.assert_called_once_with( | ||
| "player_left", | ||
| {"num_connected_players": 2, "player_limit": 4}, | ||
| ) | ||
|
|
||
| def test_no_event_on_first_update( | ||
| self, entity: SatisfactoryPlayerActivityEventEntity | ||
| ) -> None: | ||
| entity._prev_players = None # noqa: SLF001 | ||
| with patch.object(entity, "_trigger_event") as mock_trigger: | ||
| entity._handle_coordinator_update() # noqa: SLF001 | ||
| mock_trigger.assert_not_called() | ||
|
|
||
| def test_no_event_when_count_unchanged( | ||
| self, | ||
| entity: SatisfactoryPlayerActivityEventEntity, | ||
| mock_coordinator: MagicMock, | ||
| ) -> None: | ||
| entity._prev_players = 2 # noqa: SLF001 | ||
| mock_coordinator.data = {"numConnectedPlayers": 2, "playerLimit": 4} | ||
| with patch.object(entity, "_trigger_event") as mock_trigger: | ||
| entity._handle_coordinator_update() # noqa: SLF001 | ||
| mock_trigger.assert_not_called() | ||
|
|
||
| def test_prev_players_updated_after_call( | ||
| self, | ||
| entity: SatisfactoryPlayerActivityEventEntity, | ||
| mock_coordinator: MagicMock, | ||
| ) -> None: | ||
| entity._prev_players = 1 # noqa: SLF001 | ||
| mock_coordinator.data = {"numConnectedPlayers": 3, "playerLimit": 4} | ||
| with patch.object(entity, "_trigger_event"): | ||
| entity._handle_coordinator_update() # noqa: SLF001 | ||
| assert entity._prev_players == 3 # noqa: SLF001 |
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.