mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add tip connected detection to IronOS (#131946)
* Add binary platform and tip connected detection to IronOS * suggested changes * fix * fix mypy * revert accidental overwriting * Remove binary sensor * snapshot
This commit is contained in:
parent
5e8012f3f5
commit
ee8f720253
@ -92,6 +92,17 @@ class IronOSLiveDataCoordinator(IronOSBaseCoordinator[LiveDataResponse]):
|
||||
except CommunicationError as e:
|
||||
raise UpdateFailed("Cannot connect to device") from e
|
||||
|
||||
@property
|
||||
def has_tip(self) -> bool:
|
||||
"""Return True if the tip is connected."""
|
||||
if (
|
||||
self.data.max_tip_temp_ability is not None
|
||||
and self.data.live_temp is not None
|
||||
):
|
||||
threshold = self.data.max_tip_temp_ability - 5
|
||||
return self.data.live_temp <= threshold
|
||||
return False
|
||||
|
||||
|
||||
class IronOSFirmwareUpdateCoordinator(DataUpdateCoordinator[GitHubReleaseModel]):
|
||||
"""IronOS coordinator for retrieving update information from github."""
|
||||
|
@ -28,6 +28,7 @@ from homeassistant.helpers.typing import StateType
|
||||
|
||||
from . import IronOSConfigEntry
|
||||
from .const import OHM
|
||||
from .coordinator import IronOSLiveDataCoordinator
|
||||
from .entity import IronOSBaseEntity
|
||||
|
||||
# Coordinator is used to centralize the data updates
|
||||
@ -57,7 +58,7 @@ class PinecilSensor(StrEnum):
|
||||
class IronOSSensorEntityDescription(SensorEntityDescription):
|
||||
"""IronOS sensor entity descriptions."""
|
||||
|
||||
value_fn: Callable[[LiveDataResponse], StateType]
|
||||
value_fn: Callable[[LiveDataResponse, bool], StateType]
|
||||
|
||||
|
||||
PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
@ -67,7 +68,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.live_temp,
|
||||
value_fn=lambda data, has_tip: data.live_temp if has_tip else None,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
key=PinecilSensor.DC_VOLTAGE,
|
||||
@ -75,7 +76,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.dc_voltage,
|
||||
value_fn=lambda data, _: data.dc_voltage,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -84,7 +85,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.handle_temp,
|
||||
value_fn=lambda data, _: data.handle_temp,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
key=PinecilSensor.PWMLEVEL,
|
||||
@ -93,7 +94,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.pwm_level,
|
||||
value_fn=lambda data, _: data.pwm_level,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -101,14 +102,16 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
translation_key=PinecilSensor.POWER_SRC,
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=[item.name.lower() for item in PowerSource],
|
||||
value_fn=lambda data: data.power_src.name.lower() if data.power_src else None,
|
||||
value_fn=(
|
||||
lambda data, _: data.power_src.name.lower() if data.power_src else None
|
||||
),
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
key=PinecilSensor.TIP_RESISTANCE,
|
||||
translation_key=PinecilSensor.TIP_RESISTANCE,
|
||||
native_unit_of_measurement=OHM,
|
||||
value_fn=lambda data: data.tip_resistance,
|
||||
value_fn=lambda data, has_tip: data.tip_resistance if has_tip else None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
@ -118,7 +121,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda data: data.uptime,
|
||||
value_fn=lambda data, _: data.uptime,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -127,7 +130,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.movement_time,
|
||||
value_fn=lambda data, _: data.movement_time,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -135,7 +138,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
translation_key=PinecilSensor.MAX_TIP_TEMP_ABILITY,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
value_fn=lambda data: data.max_tip_temp_ability,
|
||||
value_fn=lambda data, has_tip: data.max_tip_temp_ability if has_tip else None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -145,7 +148,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda data: data.tip_voltage,
|
||||
value_fn=lambda data, has_tip: data.tip_voltage if has_tip else None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -153,7 +156,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
translation_key=PinecilSensor.HALL_SENSOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
value_fn=lambda data: data.hall_sensor,
|
||||
value_fn=lambda data, _: data.hall_sensor,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
@ -162,7 +165,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=[item.name.lower() for item in OperatingMode],
|
||||
value_fn=(
|
||||
lambda data: data.operating_mode.name.lower()
|
||||
lambda data, _: data.operating_mode.name.lower()
|
||||
if data.operating_mode
|
||||
else None
|
||||
),
|
||||
@ -173,7 +176,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.estimated_power,
|
||||
value_fn=lambda data, _: data.estimated_power,
|
||||
),
|
||||
)
|
||||
|
||||
@ -196,8 +199,11 @@ class IronOSSensorEntity(IronOSBaseEntity, SensorEntity):
|
||||
"""Representation of a IronOS sensor entity."""
|
||||
|
||||
entity_description: IronOSSensorEntityDescription
|
||||
coordinator: IronOSLiveDataCoordinator
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return sensor state."""
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
return self.entity_description.value_fn(
|
||||
self.coordinator.data, self.coordinator.has_tip
|
||||
)
|
||||
|
@ -4,13 +4,13 @@ from collections.abc import AsyncGenerator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pynecil import CommunicationError
|
||||
from pynecil import CommunicationError, LiveDataResponse
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.iron_os.coordinator import SCAN_INTERVAL
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
@ -71,3 +71,34 @@ async def test_sensors_unavailable(
|
||||
)
|
||||
for entity_entry in entity_entries:
|
||||
assert hass.states.get(entity_entry.entity_id).state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"entity_registry_enabled_by_default", "ble_device", "mock_pynecil"
|
||||
)
|
||||
async def test_tip_detection(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_pynecil: AsyncMock,
|
||||
ble_device: MagicMock,
|
||||
) -> None:
|
||||
"""Test sensor state is unknown when tip is disconnected."""
|
||||
|
||||
mock_pynecil.get_live_data.return_value = LiveDataResponse(
|
||||
live_temp=479,
|
||||
max_tip_temp_ability=460,
|
||||
)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
entities = {
|
||||
"sensor.pinecil_tip_temperature",
|
||||
"sensor.pinecil_max_tip_temperature",
|
||||
"sensor.pinecil_raw_tip_voltage",
|
||||
"sensor.pinecil_tip_resistance",
|
||||
}
|
||||
for entity_id in entities:
|
||||
assert hass.states.get(entity_id).state == STATE_UNKNOWN
|
||||
|
Loading…
x
Reference in New Issue
Block a user