mirror of
https://github.com/home-assistant/core.git
synced 2026-04-17 23:53:49 +01:00
Add battery sensors to Casper Glow (#166801)
This commit is contained in:
@@ -16,6 +16,7 @@ PLATFORMS: list[Platform] = [
|
|||||||
Platform.BUTTON,
|
Platform.BUTTON,
|
||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
Platform.SELECT,
|
Platform.SELECT,
|
||||||
|
Platform.SENSOR,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pycasperglow import GlowState
|
from pycasperglow import GlowState
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
from homeassistant.components.binary_sensor import (
|
||||||
|
BinarySensorDeviceClass,
|
||||||
|
BinarySensorEntity,
|
||||||
|
)
|
||||||
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
@@ -21,7 +25,12 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the binary sensor platform for Casper Glow."""
|
"""Set up the binary sensor platform for Casper Glow."""
|
||||||
async_add_entities([CasperGlowPausedBinarySensor(entry.runtime_data)])
|
async_add_entities(
|
||||||
|
[
|
||||||
|
CasperGlowPausedBinarySensor(entry.runtime_data),
|
||||||
|
CasperGlowChargingBinarySensor(entry.runtime_data),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CasperGlowPausedBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
class CasperGlowPausedBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
||||||
@@ -46,6 +55,34 @@ class CasperGlowPausedBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
|||||||
@callback
|
@callback
|
||||||
def _async_handle_state_update(self, state: GlowState) -> None:
|
def _async_handle_state_update(self, state: GlowState) -> None:
|
||||||
"""Handle a state update from the device."""
|
"""Handle a state update from the device."""
|
||||||
if state.is_paused is not None:
|
if state.is_paused is not None and state.is_paused != self._attr_is_on:
|
||||||
self._attr_is_on = state.is_paused
|
self._attr_is_on = state.is_paused
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class CasperGlowChargingBinarySensor(CasperGlowEntity, BinarySensorEntity):
|
||||||
|
"""Binary sensor indicating whether the Casper Glow is charging."""
|
||||||
|
|
||||||
|
_attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING
|
||||||
|
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
|
def __init__(self, coordinator: CasperGlowCoordinator) -> None:
|
||||||
|
"""Initialize the charging binary sensor."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._attr_unique_id = f"{format_mac(coordinator.device.address)}_charging"
|
||||||
|
if coordinator.device.state.is_charging is not None:
|
||||||
|
self._attr_is_on = coordinator.device.state.is_charging
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register state update callback when entity is added."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self.async_on_remove(
|
||||||
|
self._device.register_callback(self._async_handle_state_update)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_handle_state_update(self, state: GlowState) -> None:
|
||||||
|
"""Handle a state update from the device."""
|
||||||
|
if state.is_charging is not None and state.is_charging != self._attr_is_on:
|
||||||
|
self._attr_is_on = state.is_charging
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|||||||
@@ -53,15 +53,15 @@ rules:
|
|||||||
docs-use-cases: todo
|
docs-use-cases: todo
|
||||||
dynamic-devices: todo
|
dynamic-devices: todo
|
||||||
entity-category: done
|
entity-category: done
|
||||||
entity-device-class:
|
entity-device-class: done
|
||||||
status: exempt
|
|
||||||
comment: No applicable device classes for binary_sensor, button, light, or select entities.
|
|
||||||
entity-disabled-by-default: todo
|
entity-disabled-by-default: todo
|
||||||
entity-translations: done
|
entity-translations: done
|
||||||
exception-translations: done
|
exception-translations: done
|
||||||
icon-translations: done
|
icon-translations: done
|
||||||
reconfiguration-flow: todo
|
reconfiguration-flow: todo
|
||||||
repair-issues: todo
|
repair-issues:
|
||||||
|
status: exempt
|
||||||
|
comment: Integration does not register repair issues.
|
||||||
stale-devices: todo
|
stale-devices: todo
|
||||||
|
|
||||||
# Platinum
|
# Platinum
|
||||||
|
|||||||
61
homeassistant/components/casper_glow/sensor.py
Normal file
61
homeassistant/components/casper_glow/sensor.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"""Casper Glow integration sensor platform."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pycasperglow import GlowState
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
|
from homeassistant.const import PERCENTAGE, EntityCategory
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
|
from .coordinator import CasperGlowConfigEntry, CasperGlowCoordinator
|
||||||
|
from .entity import CasperGlowEntity
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: CasperGlowConfigEntry,
|
||||||
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the sensor platform for Casper Glow."""
|
||||||
|
async_add_entities([CasperGlowBatterySensor(entry.runtime_data)])
|
||||||
|
|
||||||
|
|
||||||
|
class CasperGlowBatterySensor(CasperGlowEntity, SensorEntity):
|
||||||
|
"""Sensor entity for Casper Glow battery level."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.BATTERY
|
||||||
|
_attr_native_unit_of_measurement = PERCENTAGE
|
||||||
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||||
|
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
|
def __init__(self, coordinator: CasperGlowCoordinator) -> None:
|
||||||
|
"""Initialize the battery sensor."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._attr_unique_id = f"{format_mac(coordinator.device.address)}_battery"
|
||||||
|
if coordinator.device.state.battery_level is not None:
|
||||||
|
self._attr_native_value = coordinator.device.state.battery_level.percentage
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register state update callback when entity is added."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self.async_on_remove(
|
||||||
|
self._device.register_callback(self._async_handle_state_update)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_handle_state_update(self, state: GlowState) -> None:
|
||||||
|
"""Handle a state update from the device."""
|
||||||
|
if state.battery_level is not None:
|
||||||
|
new_value = state.battery_level.percentage
|
||||||
|
if new_value != self._attr_native_value:
|
||||||
|
self._attr_native_value = new_value
|
||||||
|
self.async_write_ha_state()
|
||||||
@@ -1,4 +1,55 @@
|
|||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
|
# name: test_entities[binary_sensor.jar_charging-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': list([
|
||||||
|
None,
|
||||||
|
]),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'binary_sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'binary_sensor.jar_charging',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': 'Charging',
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <BinarySensorDeviceClass.BATTERY_CHARGING: 'battery_charging'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Charging',
|
||||||
|
'platform': 'casper_glow',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': None,
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_charging',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[binary_sensor.jar_charging-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'battery_charging',
|
||||||
|
'friendly_name': 'Jar Charging',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'binary_sensor.jar_charging',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_entities[binary_sensor.jar_dimming_paused-entry]
|
# name: test_entities[binary_sensor.jar_dimming_paused-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': list([
|
'aliases': list([
|
||||||
|
|||||||
56
tests/components/casper_glow/snapshots/test_sensor.ambr
Normal file
56
tests/components/casper_glow/snapshots/test_sensor.ambr
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_entities[sensor.jar_battery-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': list([
|
||||||
|
None,
|
||||||
|
]),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.jar_battery',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': 'Battery',
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Battery',
|
||||||
|
'platform': 'casper_glow',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': None,
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_battery',
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[sensor.jar_battery-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'battery',
|
||||||
|
'friendly_name': 'Jar Battery',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.jar_battery',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Test the Casper Glow binary sensor platform."""
|
"""Test the Casper Glow binary sensor platform."""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from pycasperglow import GlowState
|
from pycasperglow import GlowState
|
||||||
@@ -14,7 +15,8 @@ from . import setup_integration
|
|||||||
|
|
||||||
from tests.common import MockConfigEntry, snapshot_platform
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
ENTITY_ID = "binary_sensor.jar_dimming_paused"
|
PAUSED_ENTITY_ID = "binary_sensor.jar_dimming_paused"
|
||||||
|
CHARGING_ENTITY_ID = "binary_sensor.jar_charging"
|
||||||
|
|
||||||
|
|
||||||
async def test_entities(
|
async def test_entities(
|
||||||
@@ -37,31 +39,31 @@ async def test_entities(
|
|||||||
[(True, STATE_ON), (False, STATE_OFF)],
|
[(True, STATE_ON), (False, STATE_OFF)],
|
||||||
ids=["paused", "not-paused"],
|
ids=["paused", "not-paused"],
|
||||||
)
|
)
|
||||||
async def test_binary_sensor_state_update(
|
async def test_paused_state_update(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_casper_glow: MagicMock,
|
mock_casper_glow: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
is_paused: bool,
|
is_paused: bool,
|
||||||
expected_state: str,
|
expected_state: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that the binary sensor reflects is_paused state changes."""
|
"""Test that the paused binary sensor reflects is_paused state changes."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.casper_glow.PLATFORMS", [Platform.BINARY_SENSOR]
|
"homeassistant.components.casper_glow.PLATFORMS", [Platform.BINARY_SENSOR]
|
||||||
):
|
):
|
||||||
await setup_integration(hass, mock_config_entry)
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
cb = mock_casper_glow.register_callback.call_args[0][0]
|
fire_callbacks(GlowState(is_paused=is_paused))
|
||||||
|
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||||
cb(GlowState(is_paused=is_paused))
|
|
||||||
state = hass.states.get(ENTITY_ID)
|
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == expected_state
|
assert state.state == expected_state
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensor_ignores_none_paused_state(
|
async def test_paused_ignores_none_state(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_casper_glow: MagicMock,
|
mock_casper_glow: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that a callback with is_paused=None does not overwrite the state."""
|
"""Test that a callback with is_paused=None does not overwrite the state."""
|
||||||
with patch(
|
with patch(
|
||||||
@@ -69,16 +71,64 @@ async def test_binary_sensor_ignores_none_paused_state(
|
|||||||
):
|
):
|
||||||
await setup_integration(hass, mock_config_entry)
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
cb = mock_casper_glow.register_callback.call_args[0][0]
|
|
||||||
|
|
||||||
# Set a known value first
|
# Set a known value first
|
||||||
cb(GlowState(is_paused=True))
|
fire_callbacks(GlowState(is_paused=True))
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
# Callback with no is_paused data — state should remain unchanged
|
# Callback with no is_paused data — state should remain unchanged
|
||||||
cb(GlowState(is_on=True))
|
fire_callbacks(GlowState(is_on=True))
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("is_charging", "expected_state"),
|
||||||
|
[(True, STATE_ON), (False, STATE_OFF)],
|
||||||
|
ids=["charging", "not-charging"],
|
||||||
|
)
|
||||||
|
async def test_charging_state_update(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_casper_glow: MagicMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
|
is_charging: bool,
|
||||||
|
expected_state: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test that the charging binary sensor reflects is_charging state changes."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.casper_glow.PLATFORMS", [Platform.BINARY_SENSOR]
|
||||||
|
):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
fire_callbacks(GlowState(is_charging=is_charging))
|
||||||
|
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == expected_state
|
||||||
|
|
||||||
|
|
||||||
|
async def test_charging_ignores_none_state(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_casper_glow: MagicMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
|
) -> None:
|
||||||
|
"""Test that a callback with is_charging=None does not overwrite the state."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.casper_glow.PLATFORMS", [Platform.BINARY_SENSOR]
|
||||||
|
):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
# Set a known value first
|
||||||
|
fire_callbacks(GlowState(is_charging=True))
|
||||||
|
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
# Callback with no is_charging data — state should remain unchanged
|
||||||
|
fire_callbacks(GlowState(is_on=True))
|
||||||
|
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ async def test_select_ignores_remaining_time_updates(
|
|||||||
fire_callbacks: Callable[[GlowState], None],
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that callbacks with only remaining time do not change the select state."""
|
"""Test that callbacks with only remaining time do not change the select state."""
|
||||||
fire_callbacks(GlowState(dimming_time_remaining_ms=44))
|
fire_callbacks(GlowState(dimming_time_remaining_ms=2_640_000))
|
||||||
|
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
assert state is not None
|
assert state is not None
|
||||||
|
|||||||
72
tests/components/casper_glow/test_sensor.py
Normal file
72
tests/components/casper_glow/test_sensor.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
"""Test the Casper Glow sensor platform."""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from pycasperglow import BatteryLevel, GlowState
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from . import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
|
BATTERY_ENTITY_ID = "sensor.jar_battery"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entities(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_casper_glow: MagicMock,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test all sensor entities match the snapshot."""
|
||||||
|
with patch("homeassistant.components.casper_glow.PLATFORMS", [Platform.SENSOR]):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("battery_level", "expected_state"),
|
||||||
|
[
|
||||||
|
(BatteryLevel.PCT_75, "75"),
|
||||||
|
(BatteryLevel.PCT_50, "50"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_battery_state(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_casper_glow: MagicMock,
|
||||||
|
battery_level: BatteryLevel,
|
||||||
|
expected_state: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test that the battery sensor reflects device state at setup."""
|
||||||
|
mock_casper_glow.state = GlowState(battery_level=battery_level)
|
||||||
|
with patch("homeassistant.components.casper_glow.PLATFORMS", [Platform.SENSOR]):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
state = hass.states.get(BATTERY_ENTITY_ID)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == expected_state
|
||||||
|
|
||||||
|
|
||||||
|
async def test_battery_state_updated_via_callback(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_casper_glow: MagicMock,
|
||||||
|
fire_callbacks: Callable[[GlowState], None],
|
||||||
|
) -> None:
|
||||||
|
"""Test battery sensor updates when a device callback fires."""
|
||||||
|
with patch("homeassistant.components.casper_glow.PLATFORMS", [Platform.SENSOR]):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
fire_callbacks(GlowState(battery_level=BatteryLevel.PCT_50))
|
||||||
|
|
||||||
|
state = hass.states.get(BATTERY_ENTITY_ID)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "50"
|
||||||
Reference in New Issue
Block a user