Add availability to SmartThings devices (#143836)

* Bump pySmartThings to 3.1.0

* Bump pySmartThings to 3.2.0

* Add availability to SmartThings devices

* Add availability to SmartThings devices

* Add availability to SmartThings devices
This commit is contained in:
Joost Lekkerkerker 2025-04-29 10:29:07 +02:00 committed by GitHub
parent ae3925118c
commit 4f8363a5c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 710 additions and 23 deletions

View File

@ -24,6 +24,7 @@ from pysmartthings import (
SmartThingsSinkError,
Status,
)
from pysmartthings.models import HealthStatus
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@ -79,6 +80,7 @@ class FullDevice:
device: Device
status: dict[str, ComponentStatus]
online: bool
type SmartThingsConfigEntry = ConfigEntry[SmartThingsData]
@ -192,7 +194,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry)
devices = await client.get_devices()
for device in devices:
status = process_status(await client.get_device_status(device.device_id))
device_status[device.device_id] = FullDevice(device=device, status=status)
online = await client.get_device_health(device.device_id)
device_status[device.device_id] = FullDevice(
device=device, status=status, online=online.state == HealthStatus.ONLINE
)
except SmartThingsAuthenticationFailedError as err:
raise ConfigEntryAuthFailed from err

View File

@ -10,8 +10,10 @@ from pysmartthings import (
Command,
ComponentStatus,
DeviceEvent,
DeviceHealthEvent,
SmartThings,
)
from pysmartthings.models import HealthStatus
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
@ -48,6 +50,7 @@ class SmartThingsEntity(Entity):
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device.device.device_id)},
)
self._attr_available = device.online
async def async_added_to_hass(self) -> None:
"""Subscribe to updates."""
@ -61,8 +64,17 @@ class SmartThingsEntity(Entity):
self._update_handler,
)
)
self.async_on_remove(
self.client.add_device_availability_event_listener(
self.device.device.device_id, self._availability_handler
)
)
self._update_attr()
def _availability_handler(self, event: DeviceHealthEvent) -> None:
self._attr_available = event.status != HealthStatus.OFFLINE
self.async_write_ha_state()
def _update_handler(self, event: DeviceEvent) -> None:
self._internal_state[event.capability][event.attribute].value = event.value
self._internal_state[event.capability][event.attribute].data = event.data

View File

@ -37,7 +37,7 @@ rules:
docs-installation-parameters:
status: exempt
comment: No parameters needed during installation
entity-unavailable: todo
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
parallel-updates: todo

View File

@ -3,7 +3,8 @@
from typing import Any
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, DeviceEvent
from pysmartthings import Attribute, Capability, DeviceEvent, DeviceHealthEvent
from pysmartthings.models import HealthStatus
from syrupy import SnapshotAssertion
from homeassistant.components.smartthings.const import MAIN
@ -78,3 +79,14 @@ async def trigger_update(
if call[0][0] == device_id and call[0][2] == capability:
call[0][3](event)
await hass.async_block_till_done()
async def trigger_health_update(
hass: HomeAssistant, mock: AsyncMock, device_id: str, status: HealthStatus
) -> None:
"""Trigger a health update."""
event = DeviceHealthEvent("abc", "abc", status)
for call in mock.add_device_availability_event_listener.call_args_list:
if call[0][0] == device_id:
call[0][1](event)
await hass.async_block_till_done()

View File

@ -5,6 +5,7 @@ import time
from unittest.mock import AsyncMock, patch
from pysmartthings import (
DeviceHealth,
DeviceResponse,
DeviceStatus,
LocationResponse,
@ -12,6 +13,7 @@ from pysmartthings import (
SceneResponse,
Subscription,
)
from pysmartthings.models import HealthStatus
import pytest
from homeassistant.components.application_credentials import (
@ -86,6 +88,9 @@ def mock_smartthings() -> Generator[AsyncMock]:
client.create_subscription.return_value = Subscription.from_json(
load_fixture("subscription.json", DOMAIN)
)
client.get_device_health.return_value = DeviceHealth.from_json(
load_fixture("device_health.json", DOMAIN)
)
yield client
@ -170,6 +175,13 @@ def devices(mock_smartthings: AsyncMock, device_fixture: str) -> Generator[Async
return mock_smartthings
@pytest.fixture
def unavailable_device(devices: AsyncMock) -> AsyncMock:
"""Mock an unavailable device."""
devices.get_device_health.return_value.state = HealthStatus.OFFLINE
return devices
@pytest.fixture
def mock_config_entry(expires_at: int) -> MockConfigEntry:
"""Mock a config entry."""

View File

@ -0,0 +1,5 @@
{
"deviceId": "612ab3c2-3bb0-48f7-b2c0-15b169cb2fc3",
"state": "ONLINE",
"lastUpdatedDate": "2025-04-28T11:43:31.600Z"
}

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -11,12 +12,17 @@ from homeassistant.components.automation import automations_with_entity
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.script import scripts_with_entity
from homeassistant.components.smartthings import DOMAIN, MAIN
from homeassistant.const import STATE_OFF, STATE_ON, Platform
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -60,6 +66,47 @@ async def test_state_update(
assert hass.states.get("binary_sensor.refrigerator_cooler_door").state == STATE_ON
@pytest.mark.parametrize("device_fixture", ["da_ref_normal_000001"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("binary_sensor.refrigerator_cooler_door").state == STATE_OFF
await trigger_health_update(
hass, devices, "7db87911-7dce-1cf2-7119-b953432a2f09", HealthStatus.OFFLINE
)
assert (
hass.states.get("binary_sensor.refrigerator_cooler_door").state
== STATE_UNAVAILABLE
)
await trigger_health_update(
hass, devices, "7db87911-7dce-1cf2-7119-b953432a2f09", HealthStatus.ONLINE
)
assert hass.states.get("binary_sensor.refrigerator_cooler_door").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["da_ref_normal_000001"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("binary_sensor.refrigerator_cooler_door").state
== STATE_UNAVAILABLE
)
@pytest.mark.parametrize(
("device_fixture", "unique_id", "suggested_object_id", "issue_string", "entity_id"),
[

View File

@ -4,16 +4,22 @@ from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory
from pysmartthings import Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.smartthings import MAIN
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.const import (
ATTR_ENTITY_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities
from . import setup_integration, snapshot_smartthings_entities, trigger_health_update
from tests.common import MockConfigEntry
@ -54,3 +60,38 @@ async def test_press(
Command.STOP,
MAIN,
)
@pytest.mark.parametrize("device_fixture", ["da_ks_microwave_0101x"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("button.microwave_stop").state == STATE_UNKNOWN
await trigger_health_update(
hass, devices, "2bad3237-4886-e699-1b90-4a51a3d55c8a", HealthStatus.OFFLINE
)
assert hass.states.get("button.microwave_stop").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "2bad3237-4886-e699-1b90-4a51a3d55c8a", HealthStatus.ONLINE
)
assert hass.states.get("button.microwave_stop").state == STATE_UNKNOWN
@pytest.mark.parametrize("device_fixture", ["da_ks_microwave_0101x"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("button.microwave_stop").state == STATE_UNAVAILABLE

View File

@ -4,6 +4,7 @@ from typing import Any
from unittest.mock import AsyncMock, call
from pysmartthings import Attribute, Capability, Command, Status
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -36,6 +37,8 @@ from homeassistant.const import (
ATTR_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
@ -45,6 +48,7 @@ from . import (
set_attribute_value,
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
@ -857,3 +861,38 @@ async def test_thermostat_state_attributes_update(
)
assert hass.states.get("climate.asd").attributes[state_attribute] == expected_value
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("climate.ac_office_granit").state == STATE_OFF
await trigger_health_update(
hass, devices, "96a5ef74-5832-a84b-f1f7-ca799957065d", HealthStatus.OFFLINE
)
assert hass.states.get("climate.ac_office_granit").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "96a5ef74-5832-a84b-f1f7-ca799957065d", HealthStatus.ONLINE
)
assert hass.states.get("climate.ac_office_granit").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("climate.ac_office_granit").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command, Status
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -20,12 +21,18 @@ from homeassistant.const import (
SERVICE_SET_COVER_POSITION,
STATE_OPEN,
STATE_OPENING,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -190,3 +197,38 @@ async def test_position_update(
)
assert hass.states.get("cover.curtain_1a").attributes[ATTR_CURRENT_POSITION] == 50
@pytest.mark.parametrize("device_fixture", ["c2c_shade"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("cover.curtain_1a").state == STATE_OPEN
await trigger_health_update(
hass, devices, "571af102-15db-4030-b76b-245a691f74a5", HealthStatus.OFFLINE
)
assert hass.states.get("cover.curtain_1a").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "571af102-15db-4030-b76b-245a691f74a5", HealthStatus.ONLINE
)
assert hass.states.get("cover.curtain_1a").state == STATE_OPEN
@pytest.mark.parametrize("device_fixture", ["c2c_shade"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("cover.curtain_1a").state == STATE_UNAVAILABLE

View File

@ -4,15 +4,21 @@ from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory
from pysmartthings import Attribute, Capability
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.event import ATTR_EVENT_TYPES
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -97,3 +103,48 @@ async def test_supported_button_values_update(
assert hass.states.get("event.livingroom_smart_switch_button1").attributes[
ATTR_EVENT_TYPES
] == ["pushed", "held", "down_hold", "pushed_2x"]
@pytest.mark.parametrize("device_fixture", ["heatit_zpushwall"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("event.livingroom_smart_switch_button1").state == STATE_UNKNOWN
)
await trigger_health_update(
hass, devices, "5e5b97f3-3094-44e6-abc0-f61283412d6a", HealthStatus.OFFLINE
)
assert (
hass.states.get("event.livingroom_smart_switch_button1").state
== STATE_UNAVAILABLE
)
await trigger_health_update(
hass, devices, "5e5b97f3-3094-44e6-abc0-f61283412d6a", HealthStatus.ONLINE
)
assert (
hass.states.get("event.livingroom_smart_switch_button1").state == STATE_UNKNOWN
)
@pytest.mark.parametrize("device_fixture", ["heatit_zpushwall"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("event.livingroom_smart_switch_button1").state
== STATE_UNAVAILABLE
)

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -18,12 +19,14 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities
from . import setup_integration, snapshot_smartthings_entities, trigger_health_update
from tests.common import MockConfigEntry
@ -166,3 +169,38 @@ async def test_set_preset_mode(
MAIN,
argument="turbo",
)
@pytest.mark.parametrize("device_fixture", ["fake_fan"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("fan.fake_fan").state == STATE_OFF
await trigger_health_update(
hass, devices, "f1af21a2-d5a1-437c-b10a-b34a87394b71", HealthStatus.OFFLINE
)
assert hass.states.get("fan.fake_fan").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "f1af21a2-d5a1-437c-b10a-b34a87394b71", HealthStatus.ONLINE
)
assert hass.states.get("fan.fake_fan").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["fake_fan"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("fan.fake_fan").state == STATE_UNAVAILABLE

View File

@ -4,6 +4,7 @@ from typing import Any
from unittest.mock import AsyncMock, call
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -28,6 +29,7 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant, State
@ -37,6 +39,7 @@ from . import (
set_attribute_value,
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
@ -413,3 +416,38 @@ async def test_color_mode_after_startup(
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.COLOR_TEMP
)
@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("light.standing_light").state == STATE_OFF
await trigger_health_update(
hass, devices, "cb958955-b015-498c-9e62-fc0c51abd054", HealthStatus.OFFLINE
)
assert hass.states.get("light.standing_light").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "cb958955-b015-498c-9e62-fc0c51abd054", HealthStatus.ONLINE
)
assert hass.states.get("light.standing_light").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("light.standing_light").state == STATE_UNAVAILABLE

View File

@ -3,16 +3,28 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN, LockState
from homeassistant.components.smartthings.const import MAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK, Platform
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_LOCK,
SERVICE_UNLOCK,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -83,3 +95,38 @@ async def test_state_update(
)
assert hass.states.get("lock.basement_door_lock").state == LockState.UNLOCKED
@pytest.mark.parametrize("device_fixture", ["yale_push_button_deadbolt_lock"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("lock.basement_door_lock").state == LockState.LOCKED
await trigger_health_update(
hass, devices, "a9f587c5-5d8b-4273-8907-e7f609af5158", HealthStatus.OFFLINE
)
assert hass.states.get("lock.basement_door_lock").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "a9f587c5-5d8b-4273-8907-e7f609af5158", HealthStatus.ONLINE
)
assert hass.states.get("lock.basement_door_lock").state == LockState.LOCKED
@pytest.mark.parametrize("device_fixture", ["yale_push_button_deadbolt_lock"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("lock.basement_door_lock").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command, Status
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -34,12 +35,18 @@ from homeassistant.const import (
SERVICE_VOLUME_UP,
STATE_OFF,
STATE_PLAYING,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -430,3 +437,38 @@ async def test_state_update(
)
assert hass.states.get("media_player.soundbar").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("media_player.soundbar").state == STATE_PLAYING
await trigger_health_update(
hass, devices, "afcf3b91-0000-1111-2222-ddff2a0a6577", HealthStatus.OFFLINE
)
assert hass.states.get("media_player.soundbar").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "afcf3b91-0000-1111-2222-ddff2a0a6577", HealthStatus.ONLINE
)
assert hass.states.get("media_player.soundbar").state == STATE_PLAYING
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("media_player.soundbar").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -12,11 +13,16 @@ from homeassistant.components.number import (
SERVICE_SET_VALUE,
)
from homeassistant.components.smartthings import MAIN
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -79,3 +85,38 @@ async def test_state_update(
)
assert hass.states.get("number.washer_rinse_cycles").state == "3"
@pytest.mark.parametrize("device_fixture", ["da_wm_wm_000001"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("number.washer_rinse_cycles").state == "2"
await trigger_health_update(
hass, devices, "f984b91d-f250-9d42-3436-33f09a422a47", HealthStatus.OFFLINE
)
assert hass.states.get("number.washer_rinse_cycles").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "f984b91d-f250-9d42-3436-33f09a422a47", HealthStatus.ONLINE
)
assert hass.states.get("number.washer_rinse_cycles").state == "2"
@pytest.mark.parametrize("device_fixture", ["da_wm_wm_000001"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("number.washer_rinse_cycles").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -12,7 +13,7 @@ from homeassistant.components.select import (
SERVICE_SELECT_OPTION,
)
from homeassistant.components.smartthings import MAIN
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import entity_registry as er
@ -21,6 +22,7 @@ from . import (
set_attribute_value,
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
@ -119,3 +121,38 @@ async def test_select_option_without_remote_control(
blocking=True,
)
devices.execute_device_command.assert_not_called()
@pytest.mark.parametrize("device_fixture", ["da_wm_wd_000001"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("select.dryer").state == "stop"
await trigger_health_update(
hass, devices, "02f7256e-8353-5bdd-547f-bd5b1647e01b", HealthStatus.OFFLINE
)
assert hass.states.get("select.dryer").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "02f7256e-8353-5bdd-547f-bd5b1647e01b", HealthStatus.ONLINE
)
assert hass.states.get("select.dryer").state == "stop"
@pytest.mark.parametrize("device_fixture", ["da_wm_wd_000001"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("select.dryer").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -11,12 +12,17 @@ from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.smartthings.const import DOMAIN, MAIN
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -296,3 +302,44 @@ async def test_create_issue(
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 0
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("sensor.ac_office_granit_temperature").state == "25"
await trigger_health_update(
hass, devices, "96a5ef74-5832-a84b-f1f7-ca799957065d", HealthStatus.OFFLINE
)
assert (
hass.states.get("sensor.ac_office_granit_temperature").state
== STATE_UNAVAILABLE
)
await trigger_health_update(
hass, devices, "96a5ef74-5832-a84b-f1f7-ca799957065d", HealthStatus.ONLINE
)
assert hass.states.get("sensor.ac_office_granit_temperature").state == "25"
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("sensor.ac_office_granit_temperature").state
== STATE_UNAVAILABLE
)

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -17,13 +18,19 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -377,3 +384,38 @@ async def test_create_issue(
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 0
@pytest.mark.parametrize("device_fixture", ["c2c_arlo_pro_3_switch"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("switch.2nd_floor_hallway").state == STATE_ON
await trigger_health_update(
hass, devices, "10e06a70-ee7d-4832-85e9-a0a06a7a05bd", HealthStatus.OFFLINE
)
assert hass.states.get("switch.2nd_floor_hallway").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "10e06a70-ee7d-4832-85e9-a0a06a7a05bd", HealthStatus.ONLINE
)
assert hass.states.get("switch.2nd_floor_hallway").state == STATE_ON
@pytest.mark.parametrize("device_fixture", ["c2c_arlo_pro_3_switch"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("switch.2nd_floor_hallway").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -12,11 +13,22 @@ from homeassistant.components.update import (
DOMAIN as UPDATE_DOMAIN,
SERVICE_INSTALL,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform
from homeassistant.const import (
ATTR_ENTITY_ID,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -140,3 +152,38 @@ async def test_state_update_available(
)
assert hass.states.get("update.dimmer_debian_firmware").state == STATE_ON
@pytest.mark.parametrize("device_fixture", ["centralite"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("update.dimmer_debian_firmware").state == STATE_OFF
await trigger_health_update(
hass, devices, "d0268a69-abfb-4c92-a646-61cec2e510ad", HealthStatus.OFFLINE
)
assert hass.states.get("update.dimmer_debian_firmware").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "d0268a69-abfb-4c92-a646-61cec2e510ad", HealthStatus.ONLINE
)
assert hass.states.get("update.dimmer_debian_firmware").state == STATE_OFF
@pytest.mark.parametrize("device_fixture", ["centralite"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("update.dimmer_debian_firmware").state == STATE_UNAVAILABLE

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy import SnapshotAssertion
@ -12,12 +13,18 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_CLOSE_VALVE,
SERVICE_OPEN_VALVE,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration, snapshot_smartthings_entities, trigger_update
from . import (
setup_integration,
snapshot_smartthings_entities,
trigger_health_update,
trigger_update,
)
from tests.common import MockConfigEntry
@ -85,3 +92,38 @@ async def test_state_update(
)
assert hass.states.get("valve.volvo").state == ValveState.OPEN
@pytest.mark.parametrize("device_fixture", ["virtual_valve"])
async def test_availability(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test availability."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("valve.volvo").state == ValveState.CLOSED
await trigger_health_update(
hass, devices, "612ab3c2-3bb0-48f7-b2c0-15b169cb2fc3", HealthStatus.OFFLINE
)
assert hass.states.get("valve.volvo").state == STATE_UNAVAILABLE
await trigger_health_update(
hass, devices, "612ab3c2-3bb0-48f7-b2c0-15b169cb2fc3", HealthStatus.ONLINE
)
assert hass.states.get("valve.volvo").state == ValveState.CLOSED
@pytest.mark.parametrize("device_fixture", ["virtual_valve"])
async def test_availability_at_start(
hass: HomeAssistant,
unavailable_device: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test unavailable at boot."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("valve.volvo").state == STATE_UNAVAILABLE