Integration for IronOS (Pinecil V2) soldering irons (#120802)
* Add Pinecil integration * Refactor with new library * Add tests for config flow, remove unused code * requested changes * update requirements * Move some sensor values to diagnostics, add tests for sensors * User service uuid in discovery * fix manufacturer name * Bump pynecil to version 0.2.0 * Rename integration to IronOS * Recreate snapshot * Update strings * type checking * Update snapshot * Add async_setup to coordinator * Show device id with serial number * Added missing boost to operation mode states * remove super call * Refactor * testspull/122765/head
parent
06ee8fdd47
commit
70df4ca461
|
@ -710,6 +710,8 @@ build.json @home-assistant/supervisor
|
||||||
/homeassistant/components/iqvia/ @bachya
|
/homeassistant/components/iqvia/ @bachya
|
||||||
/tests/components/iqvia/ @bachya
|
/tests/components/iqvia/ @bachya
|
||||||
/homeassistant/components/irish_rail_transport/ @ttroy50
|
/homeassistant/components/irish_rail_transport/ @ttroy50
|
||||||
|
/homeassistant/components/iron_os/ @tr4nt0r
|
||||||
|
/tests/components/iron_os/ @tr4nt0r
|
||||||
/homeassistant/components/isal/ @bdraco
|
/homeassistant/components/isal/ @bdraco
|
||||||
/tests/components/isal/ @bdraco
|
/tests/components/isal/ @bdraco
|
||||||
/homeassistant/components/islamic_prayer_times/ @engrbm87 @cpfair
|
/homeassistant/components/islamic_prayer_times/ @engrbm87 @cpfair
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
"""The IronOS integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from pynecil import Pynecil
|
||||||
|
|
||||||
|
from homeassistant.components import bluetooth
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_NAME, Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .coordinator import IronOSCoordinator
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||||
|
|
||||||
|
type IronOSConfigEntry = ConfigEntry[IronOSCoordinator]
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: IronOSConfigEntry) -> bool:
|
||||||
|
"""Set up IronOS from a config entry."""
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert entry.unique_id
|
||||||
|
ble_device = bluetooth.async_ble_device_from_address(
|
||||||
|
hass, entry.unique_id, connectable=True
|
||||||
|
)
|
||||||
|
if not ble_device:
|
||||||
|
raise ConfigEntryNotReady(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="setup_device_unavailable_exception",
|
||||||
|
translation_placeholders={CONF_NAME: entry.title},
|
||||||
|
)
|
||||||
|
|
||||||
|
device = Pynecil(ble_device)
|
||||||
|
|
||||||
|
coordinator = IronOSCoordinator(hass, device)
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
entry.runtime_data = coordinator
|
||||||
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: IronOSConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
|
@ -0,0 +1,83 @@
|
||||||
|
"""Config flow for IronOS integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from habluetooth import BluetoothServiceInfoBleak
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.bluetooth.api import async_discovered_service_info
|
||||||
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
|
from homeassistant.const import CONF_ADDRESS
|
||||||
|
|
||||||
|
from .const import DISCOVERY_SVC_UUID, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
class IronOSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for IronOS."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialize the config flow."""
|
||||||
|
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||||
|
self._discovered_devices: dict[str, str] = {}
|
||||||
|
|
||||||
|
async def async_step_bluetooth(
|
||||||
|
self, discovery_info: BluetoothServiceInfoBleak
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle bluetooth discovery step."""
|
||||||
|
await self.async_set_unique_id(discovery_info.address)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
self._discovery_info = discovery_info
|
||||||
|
|
||||||
|
return await self.async_step_bluetooth_confirm()
|
||||||
|
|
||||||
|
async def async_step_bluetooth_confirm(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Confirm discovery."""
|
||||||
|
assert self._discovery_info is not None
|
||||||
|
discovery_info = self._discovery_info
|
||||||
|
title = discovery_info.name
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
return self.async_create_entry(title=title, data={})
|
||||||
|
|
||||||
|
self._set_confirm_only()
|
||||||
|
placeholders = {"name": title}
|
||||||
|
self.context["title_placeholders"] = placeholders
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="bluetooth_confirm", description_placeholders=placeholders
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle the user step to pick discovered device."""
|
||||||
|
if user_input is not None:
|
||||||
|
address = user_input[CONF_ADDRESS]
|
||||||
|
title = self._discovered_devices[address]
|
||||||
|
await self.async_set_unique_id(address, raise_on_progress=False)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
return self.async_create_entry(title=title, data={})
|
||||||
|
|
||||||
|
current_addresses = self._async_current_ids()
|
||||||
|
for discovery_info in async_discovered_service_info(self.hass, True):
|
||||||
|
address = discovery_info.address
|
||||||
|
if (
|
||||||
|
DISCOVERY_SVC_UUID not in discovery_info.service_uuids
|
||||||
|
or address in current_addresses
|
||||||
|
or address in self._discovered_devices
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
self._discovered_devices[address] = discovery_info.name
|
||||||
|
|
||||||
|
if not self._discovered_devices:
|
||||||
|
return self.async_abort(reason="no_devices_found")
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{vol.Required(CONF_ADDRESS): vol.In(self._discovered_devices)}
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Constants for the IronOS integration."""
|
||||||
|
|
||||||
|
DOMAIN = "iron_os"
|
||||||
|
|
||||||
|
MANUFACTURER = "PINE64"
|
||||||
|
MODEL = "Pinecil V2"
|
||||||
|
|
||||||
|
OHM = "Ω"
|
||||||
|
|
||||||
|
DISCOVERY_SVC_UUID = "9eae1000-9d0d-48c5-aa55-33e27f9bc533"
|
|
@ -0,0 +1,49 @@
|
||||||
|
"""Update coordinator for IronOS Integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pynecil import CommunicationError, DeviceInfoResponse, LiveDataResponse, Pynecil
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
|
||||||
|
|
||||||
|
class IronOSCoordinator(DataUpdateCoordinator[LiveDataResponse]):
|
||||||
|
"""IronOS coordinator."""
|
||||||
|
|
||||||
|
device_info: DeviceInfoResponse
|
||||||
|
config_entry: ConfigEntry
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, device: Pynecil) -> None:
|
||||||
|
"""Initialize IronOS coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name=DOMAIN,
|
||||||
|
update_interval=SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> LiveDataResponse:
|
||||||
|
"""Fetch data from Device."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await self.device.get_live_data()
|
||||||
|
|
||||||
|
except CommunicationError as e:
|
||||||
|
raise UpdateFailed("Cannot connect to device") from e
|
||||||
|
|
||||||
|
async def _async_setup(self) -> None:
|
||||||
|
"""Set up the coordinator."""
|
||||||
|
|
||||||
|
self.device_info = await self.device.get_device_info()
|
|
@ -0,0 +1,41 @@
|
||||||
|
"""Base entity for IronOS integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
|
||||||
|
from homeassistant.helpers.entity import EntityDescription
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import MANUFACTURER, MODEL
|
||||||
|
from .coordinator import IronOSCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
class IronOSBaseEntity(CoordinatorEntity[IronOSCoordinator]):
|
||||||
|
"""Base IronOS entity."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: IronOSCoordinator,
|
||||||
|
entity_description: EntityDescription,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
self.entity_description = entity_description
|
||||||
|
self._attr_unique_id = (
|
||||||
|
f"{coordinator.config_entry.unique_id}_{entity_description.key}"
|
||||||
|
)
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert coordinator.config_entry.unique_id
|
||||||
|
self.device_info = DeviceInfo(
|
||||||
|
connections={(CONNECTION_BLUETOOTH, coordinator.config_entry.unique_id)},
|
||||||
|
manufacturer=MANUFACTURER,
|
||||||
|
model=MODEL,
|
||||||
|
name="Pinecil",
|
||||||
|
sw_version=coordinator.device_info.build,
|
||||||
|
serial_number=f"{coordinator.device_info.device_sn} (ID:{coordinator.device_info.device_id})",
|
||||||
|
)
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"live_temperature": {
|
||||||
|
"default": "mdi:soldering-iron"
|
||||||
|
},
|
||||||
|
"setpoint_temperature": {
|
||||||
|
"default": "mdi:thermostat"
|
||||||
|
},
|
||||||
|
"voltage": {
|
||||||
|
"default": "mdi:current-dc"
|
||||||
|
},
|
||||||
|
"handle_temperature": {
|
||||||
|
"default": "mdi:grease-pencil"
|
||||||
|
},
|
||||||
|
"power_pwm_level": {
|
||||||
|
"default": "mdi:square-wave"
|
||||||
|
},
|
||||||
|
"power_source": {
|
||||||
|
"default": "mdi:power-plug",
|
||||||
|
"state": {
|
||||||
|
"dc": "mdi:record-circle-outline",
|
||||||
|
"qc": "mdi:usb-port",
|
||||||
|
"pd_vbus": "mdi:usb-c-port",
|
||||||
|
"pd": "mdi:usb-c-port"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tip_resistance": {
|
||||||
|
"default": "mdi:omega"
|
||||||
|
},
|
||||||
|
"hall_sensor": {
|
||||||
|
"default": "mdi:leak"
|
||||||
|
},
|
||||||
|
"movement_time": {
|
||||||
|
"default": "mdi:clock-fast"
|
||||||
|
},
|
||||||
|
"max_tip_temp_ability": {
|
||||||
|
"default": "mdi:thermometer-chevron-up"
|
||||||
|
},
|
||||||
|
"uptime": {
|
||||||
|
"default": "mdi:progress-clock"
|
||||||
|
},
|
||||||
|
"tip_voltage": {
|
||||||
|
"default": "mdi:sine-wave"
|
||||||
|
},
|
||||||
|
"operating_mode": {
|
||||||
|
"default": "mdi:format-list-bulleted",
|
||||||
|
"state": {
|
||||||
|
"boost": "mdi:rocket-launch",
|
||||||
|
"soldering": "mdi:soldering-iron",
|
||||||
|
"sleeping": "mdi:sleep",
|
||||||
|
"settings": "mdi:menu-open",
|
||||||
|
"debug": "mdi:bug-play"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"estimated_power": {
|
||||||
|
"default": "mdi:flash"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"domain": "iron_os",
|
||||||
|
"name": "IronOS",
|
||||||
|
"bluetooth": [
|
||||||
|
{
|
||||||
|
"service_uuid": "9eae1000-9d0d-48c5-aa55-33e27f9bc533",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codeowners": ["@tr4nt0r"],
|
||||||
|
"config_flow": true,
|
||||||
|
"dependencies": ["bluetooth_adapters"],
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/iron_os",
|
||||||
|
"iot_class": "local_polling",
|
||||||
|
"loggers": ["pynecil"],
|
||||||
|
"requirements": ["pynecil==0.2.0"]
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
"""Sensor platform for IronOS integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import StrEnum
|
||||||
|
|
||||||
|
from pynecil import LiveDataResponse, OperatingMode, PowerSource
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
EntityCategory,
|
||||||
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
PERCENTAGE,
|
||||||
|
UnitOfElectricPotential,
|
||||||
|
UnitOfPower,
|
||||||
|
UnitOfTemperature,
|
||||||
|
UnitOfTime,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
|
from . import IronOSConfigEntry
|
||||||
|
from .const import OHM
|
||||||
|
from .entity import IronOSBaseEntity
|
||||||
|
|
||||||
|
|
||||||
|
class PinecilSensor(StrEnum):
|
||||||
|
"""Pinecil Sensors."""
|
||||||
|
|
||||||
|
LIVE_TEMP = "live_temperature"
|
||||||
|
SETPOINT_TEMP = "setpoint_temperature"
|
||||||
|
DC_VOLTAGE = "voltage"
|
||||||
|
HANDLETEMP = "handle_temperature"
|
||||||
|
PWMLEVEL = "power_pwm_level"
|
||||||
|
POWER_SRC = "power_source"
|
||||||
|
TIP_RESISTANCE = "tip_resistance"
|
||||||
|
UPTIME = "uptime"
|
||||||
|
MOVEMENT_TIME = "movement_time"
|
||||||
|
MAX_TIP_TEMP_ABILITY = "max_tip_temp_ability"
|
||||||
|
TIP_VOLTAGE = "tip_voltage"
|
||||||
|
HALL_SENSOR = "hall_sensor"
|
||||||
|
OPERATING_MODE = "operating_mode"
|
||||||
|
ESTIMATED_POWER = "estimated_power"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class IronOSSensorEntityDescription(SensorEntityDescription):
|
||||||
|
"""IronOS sensor entity descriptions."""
|
||||||
|
|
||||||
|
value_fn: Callable[[LiveDataResponse], StateType]
|
||||||
|
|
||||||
|
|
||||||
|
PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.LIVE_TEMP,
|
||||||
|
translation_key=PinecilSensor.LIVE_TEMP,
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.live_temp,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.DC_VOLTAGE,
|
||||||
|
translation_key=PinecilSensor.DC_VOLTAGE,
|
||||||
|
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||||
|
device_class=SensorDeviceClass.VOLTAGE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.dc_voltage,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.HANDLETEMP,
|
||||||
|
translation_key=PinecilSensor.HANDLETEMP,
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.handle_temp,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.PWMLEVEL,
|
||||||
|
translation_key=PinecilSensor.PWMLEVEL,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
suggested_display_precision=0,
|
||||||
|
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.pwm_level,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.POWER_SRC,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.UPTIME,
|
||||||
|
translation_key=PinecilSensor.UPTIME,
|
||||||
|
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||||
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
value_fn=lambda data: data.uptime,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.MOVEMENT_TIME,
|
||||||
|
translation_key=PinecilSensor.MOVEMENT_TIME,
|
||||||
|
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||||
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.movement_time,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.MAX_TIP_TEMP_ABILITY,
|
||||||
|
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,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.TIP_VOLTAGE,
|
||||||
|
translation_key=PinecilSensor.TIP_VOLTAGE,
|
||||||
|
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
|
||||||
|
device_class=SensorDeviceClass.VOLTAGE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=3,
|
||||||
|
value_fn=lambda data: data.tip_voltage,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.HALL_SENSOR,
|
||||||
|
translation_key=PinecilSensor.HALL_SENSOR,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
value_fn=lambda data: data.hall_sensor,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.OPERATING_MODE,
|
||||||
|
translation_key=PinecilSensor.OPERATING_MODE,
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
options=[item.name.lower() for item in OperatingMode],
|
||||||
|
value_fn=(
|
||||||
|
lambda data: data.operating_mode.name.lower()
|
||||||
|
if data.operating_mode
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IronOSSensorEntityDescription(
|
||||||
|
key=PinecilSensor.ESTIMATED_POWER,
|
||||||
|
translation_key=PinecilSensor.ESTIMATED_POWER,
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda data: data.estimated_power,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: IronOSConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up sensors from a config entry."""
|
||||||
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
IronOSSensorEntity(coordinator, description)
|
||||||
|
for description in PINECIL_SENSOR_DESCRIPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class IronOSSensorEntity(IronOSBaseEntity, SensorEntity):
|
||||||
|
"""Representation of a IronOS sensor entity."""
|
||||||
|
|
||||||
|
entity_description: IronOSSensorEntityDescription
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> StateType:
|
||||||
|
"""Return sensor state."""
|
||||||
|
return self.entity_description.value_fn(self.coordinator.data)
|
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||||
|
"data": {
|
||||||
|
"address": "[%key:common::config_flow::data::device%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bluetooth_confirm": {
|
||||||
|
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"live_temperature": {
|
||||||
|
"name": "Tip temperature"
|
||||||
|
},
|
||||||
|
"voltage": {
|
||||||
|
"name": "DC input voltage"
|
||||||
|
},
|
||||||
|
"handle_temperature": {
|
||||||
|
"name": "Handle temperature"
|
||||||
|
},
|
||||||
|
"power_pwm_level": {
|
||||||
|
"name": "Power level"
|
||||||
|
},
|
||||||
|
"power_source": {
|
||||||
|
"name": "Power source",
|
||||||
|
"state": {
|
||||||
|
"dc": "DC input",
|
||||||
|
"qc": "USB Quick Charge",
|
||||||
|
"pd_vbus": "USB PD VBUS",
|
||||||
|
"pd": "USB Power Delivery"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tip_resistance": {
|
||||||
|
"name": "Tip resistance"
|
||||||
|
},
|
||||||
|
"uptime": {
|
||||||
|
"name": "Uptime"
|
||||||
|
},
|
||||||
|
"movement_time": {
|
||||||
|
"name": "Last movement time"
|
||||||
|
},
|
||||||
|
"max_tip_temp_ability": {
|
||||||
|
"name": "Max tip temperature"
|
||||||
|
},
|
||||||
|
"tip_voltage": {
|
||||||
|
"name": "Raw tip voltage"
|
||||||
|
},
|
||||||
|
"hall_sensor": {
|
||||||
|
"name": "Hall effect strength"
|
||||||
|
},
|
||||||
|
"operating_mode": {
|
||||||
|
"name": "Operating mode",
|
||||||
|
"state": {
|
||||||
|
"idle": "[%key:common::state::idle%]",
|
||||||
|
"soldering": "Soldering",
|
||||||
|
"sleeping": "Sleeping",
|
||||||
|
"settings": "Settings",
|
||||||
|
"debug": "Debug",
|
||||||
|
"boost": "Boost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"estimated_power": {
|
||||||
|
"name": "Estimated power"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"setup_device_unavailable_exception": {
|
||||||
|
"message": "Device {name} is not reachable"
|
||||||
|
},
|
||||||
|
"setup_device_connection_error_exception": {
|
||||||
|
"message": "Connection to device {name} failed, try again later"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -321,6 +321,11 @@ BLUETOOTH: Final[list[dict[str, bool | str | int | list[int]]]] = [
|
||||||
"domain": "inkbird",
|
"domain": "inkbird",
|
||||||
"local_name": "tps",
|
"local_name": "tps",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"connectable": True,
|
||||||
|
"domain": "iron_os",
|
||||||
|
"service_uuid": "9eae1000-9d0d-48c5-aa55-33e27f9bc533",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"connectable": False,
|
"connectable": False,
|
||||||
"domain": "kegtron",
|
"domain": "kegtron",
|
||||||
|
|
|
@ -276,6 +276,7 @@ FLOWS = {
|
||||||
"ipma",
|
"ipma",
|
||||||
"ipp",
|
"ipp",
|
||||||
"iqvia",
|
"iqvia",
|
||||||
|
"iron_os",
|
||||||
"islamic_prayer_times",
|
"islamic_prayer_times",
|
||||||
"israel_rail",
|
"israel_rail",
|
||||||
"iss",
|
"iss",
|
||||||
|
|
|
@ -2899,6 +2899,12 @@
|
||||||
"config_flow": false,
|
"config_flow": false,
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
},
|
},
|
||||||
|
"iron_os": {
|
||||||
|
"name": "IronOS",
|
||||||
|
"integration_type": "hub",
|
||||||
|
"config_flow": true,
|
||||||
|
"iot_class": "local_polling"
|
||||||
|
},
|
||||||
"islamic_prayer_times": {
|
"islamic_prayer_times": {
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
|
|
@ -2025,6 +2025,9 @@ pymsteams==0.1.12
|
||||||
# homeassistant.components.mysensors
|
# homeassistant.components.mysensors
|
||||||
pymysensors==0.24.0
|
pymysensors==0.24.0
|
||||||
|
|
||||||
|
# homeassistant.components.iron_os
|
||||||
|
pynecil==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.netgear
|
# homeassistant.components.netgear
|
||||||
pynetgear==0.10.10
|
pynetgear==0.10.10
|
||||||
|
|
||||||
|
|
|
@ -1615,6 +1615,9 @@ pymonoprice==0.4
|
||||||
# homeassistant.components.mysensors
|
# homeassistant.components.mysensors
|
||||||
pymysensors==0.24.0
|
pymysensors==0.24.0
|
||||||
|
|
||||||
|
# homeassistant.components.iron_os
|
||||||
|
pynecil==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.netgear
|
# homeassistant.components.netgear
|
||||||
pynetgear==0.10.10
|
pynetgear==0.10.10
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
"""Tests for the Pinecil integration."""
|
|
@ -0,0 +1,141 @@
|
||||||
|
"""Fixtures for Pinecil tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
from bleak.backends.device import BLEDevice
|
||||||
|
from habluetooth import BluetoothServiceInfoBleak
|
||||||
|
from pynecil import DeviceInfoResponse, LiveDataResponse, OperatingMode, PowerSource
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.iron_os import DOMAIN
|
||||||
|
from homeassistant.const import CONF_ADDRESS
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import generate_advertisement_data, generate_ble_device
|
||||||
|
|
||||||
|
USER_INPUT = {CONF_ADDRESS: "c0:ff:ee:c0:ff:ee"}
|
||||||
|
DEFAULT_NAME = "Pinecil-C0FFEEE"
|
||||||
|
PINECIL_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="Pinecil-C0FFEEE",
|
||||||
|
address="c0:ff:ee:c0:ff:ee",
|
||||||
|
device=generate_ble_device(
|
||||||
|
address="c0:ff:ee:c0:ff:ee",
|
||||||
|
name="Pinecil-C0FFEEE",
|
||||||
|
),
|
||||||
|
rssi=-61,
|
||||||
|
manufacturer_data={},
|
||||||
|
service_data={},
|
||||||
|
service_uuids=["9eae1000-9d0d-48c5-aa55-33e27f9bc533"],
|
||||||
|
source="local",
|
||||||
|
advertisement=generate_advertisement_data(
|
||||||
|
manufacturer_data={},
|
||||||
|
service_uuids=["9eae1000-9d0d-48c5-aa55-33e27f9bc533"],
|
||||||
|
),
|
||||||
|
connectable=True,
|
||||||
|
time=0,
|
||||||
|
tx_power=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
UNKNOWN_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="",
|
||||||
|
address="c0:ff:ee:c0:ff:ee",
|
||||||
|
device=generate_ble_device(
|
||||||
|
address="c0:ff:ee:c0:ff:ee",
|
||||||
|
name="",
|
||||||
|
),
|
||||||
|
rssi=-61,
|
||||||
|
manufacturer_data={},
|
||||||
|
service_data={},
|
||||||
|
service_uuids=[],
|
||||||
|
source="local",
|
||||||
|
advertisement=generate_advertisement_data(
|
||||||
|
manufacturer_data={},
|
||||||
|
service_uuids=[],
|
||||||
|
),
|
||||||
|
connectable=True,
|
||||||
|
time=0,
|
||||||
|
tx_power=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_bluetooth(enable_bluetooth: None) -> None:
|
||||||
|
"""Auto mock bluetooth."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||||
|
"""Override async_setup_entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.iron_os.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
yield mock_setup_entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="discovery")
|
||||||
|
def mock_async_discovered_service_info() -> Generator[MagicMock]:
|
||||||
|
"""Mock service discovery."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.iron_os.config_flow.async_discovered_service_info",
|
||||||
|
return_value=[PINECIL_SERVICE_INFO, UNKNOWN_SERVICE_INFO],
|
||||||
|
) as discovery:
|
||||||
|
yield discovery
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="config_entry")
|
||||||
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
|
"""Mock Pinecil configuration entry."""
|
||||||
|
return MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title=DEFAULT_NAME,
|
||||||
|
data={},
|
||||||
|
unique_id="c0:ff:ee:c0:ff:ee",
|
||||||
|
entry_id="1234567890",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="ble_device")
|
||||||
|
def mock_ble_device() -> Generator[MagicMock]:
|
||||||
|
"""Mock BLEDevice."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.async_ble_device_from_address",
|
||||||
|
return_value=BLEDevice(
|
||||||
|
address="c0:ff:ee:c0:ff:ee", name=DEFAULT_NAME, rssi=-50, details={}
|
||||||
|
),
|
||||||
|
) as ble_device:
|
||||||
|
yield ble_device
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_pynecil() -> Generator[AsyncMock, None, None]:
|
||||||
|
"""Mock Pynecil library."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.iron_os.Pynecil", autospec=True
|
||||||
|
) as mock_client:
|
||||||
|
client = mock_client.return_value
|
||||||
|
|
||||||
|
client.get_device_info.return_value = DeviceInfoResponse(
|
||||||
|
build="v2.22",
|
||||||
|
device_id="c0ffeeC0",
|
||||||
|
address="c0:ff:ee:c0:ff:ee",
|
||||||
|
device_sn="0000c0ffeec0ffee",
|
||||||
|
name=DEFAULT_NAME,
|
||||||
|
)
|
||||||
|
client.get_live_data.return_value = LiveDataResponse(
|
||||||
|
live_temp=298,
|
||||||
|
setpoint_temp=300,
|
||||||
|
dc_voltage=20.6,
|
||||||
|
handle_temp=36.3,
|
||||||
|
pwm_level=41,
|
||||||
|
power_src=PowerSource.PD,
|
||||||
|
tip_resistance=6.2,
|
||||||
|
uptime=1671,
|
||||||
|
movement_time=10000,
|
||||||
|
max_tip_temp_ability=460,
|
||||||
|
tip_voltage=2212,
|
||||||
|
hall_sensor=0,
|
||||||
|
operating_mode=OperatingMode.SOLDERING,
|
||||||
|
estimated_power=24.8,
|
||||||
|
)
|
||||||
|
yield client
|
|
@ -0,0 +1,683 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_sensors[sensor.pinecil_dc_input_voltage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_dc_input_voltage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'DC input voltage',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.DC_VOLTAGE: 'voltage'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_voltage',
|
||||||
|
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_dc_input_voltage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'voltage',
|
||||||
|
'friendly_name': 'Pinecil DC input voltage',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_dc_input_voltage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '20.6',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_estimated_power-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.pinecil_estimated_power',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Estimated power',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.ESTIMATED_POWER: 'estimated_power'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_estimated_power',
|
||||||
|
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_estimated_power-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'power',
|
||||||
|
'friendly_name': 'Pinecil Estimated power',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_estimated_power',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '24.8',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_hall_effect_strength-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_hall_effect_strength',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Hall effect strength',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.HALL_SENSOR: 'hall_sensor'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_hall_sensor',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_hall_effect_strength-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Pinecil Hall effect strength',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_hall_effect_strength',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_handle_temperature-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.pinecil_handle_temperature',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Handle temperature',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.HANDLETEMP: 'handle_temperature'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_handle_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_handle_temperature-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'Pinecil Handle temperature',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_handle_temperature',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '36.3',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_last_movement_time-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_last_movement_time',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Last movement time',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.MOVEMENT_TIME: 'movement_time'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_movement_time',
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_last_movement_time-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'Pinecil Last movement time',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_last_movement_time',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '10000',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_max_tip_temperature-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_max_tip_temperature',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Max tip temperature',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.MAX_TIP_TEMP_ABILITY: 'max_tip_temp_ability'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_max_tip_temp_ability',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_max_tip_temperature-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'Pinecil Max tip temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_max_tip_temperature',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '460',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_operating_mode-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'options': list([
|
||||||
|
'idle',
|
||||||
|
'soldering',
|
||||||
|
'boost',
|
||||||
|
'sleeping',
|
||||||
|
'settings',
|
||||||
|
'debug',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.pinecil_operating_mode',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Operating mode',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.OPERATING_MODE: 'operating_mode'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_operating_mode',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_operating_mode-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'enum',
|
||||||
|
'friendly_name': 'Pinecil Operating mode',
|
||||||
|
'options': list([
|
||||||
|
'idle',
|
||||||
|
'soldering',
|
||||||
|
'boost',
|
||||||
|
'sleeping',
|
||||||
|
'settings',
|
||||||
|
'debug',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_operating_mode',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'soldering',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_power_level-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_power_level',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 0,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.POWER_FACTOR: 'power_factor'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Power level',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.PWMLEVEL: 'power_pwm_level'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_power_pwm_level',
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_power_level-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'power_factor',
|
||||||
|
'friendly_name': 'Pinecil Power level',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_power_level',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '41',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_power_source-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'options': list([
|
||||||
|
'dc',
|
||||||
|
'qc',
|
||||||
|
'pd_vbus',
|
||||||
|
'pd',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_power_source',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Power source',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.POWER_SRC: 'power_source'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_power_source',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_power_source-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'enum',
|
||||||
|
'friendly_name': 'Pinecil Power source',
|
||||||
|
'options': list([
|
||||||
|
'dc',
|
||||||
|
'qc',
|
||||||
|
'pd_vbus',
|
||||||
|
'pd',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_power_source',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'pd',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_raw_tip_voltage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_raw_tip_voltage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 3,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Raw tip voltage',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.TIP_VOLTAGE: 'tip_voltage'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_tip_voltage',
|
||||||
|
'unit_of_measurement': <UnitOfElectricPotential.MILLIVOLT: 'mV'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_raw_tip_voltage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'voltage',
|
||||||
|
'friendly_name': 'Pinecil Raw tip voltage',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfElectricPotential.MILLIVOLT: 'mV'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_raw_tip_voltage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '2212',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_tip_resistance-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_tip_resistance',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Tip resistance',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.TIP_RESISTANCE: 'tip_resistance'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_tip_resistance',
|
||||||
|
'unit_of_measurement': 'Ω',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_tip_resistance-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Pinecil Tip resistance',
|
||||||
|
'unit_of_measurement': 'Ω',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_tip_resistance',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '6.2',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_tip_temperature-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.pinecil_tip_temperature',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Tip temperature',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.LIVE_TEMP: 'live_temperature'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_live_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_tip_temperature-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'Pinecil Tip temperature',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_tip_temperature',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '298',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_uptime-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.pinecil_uptime',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Uptime',
|
||||||
|
'platform': 'iron_os',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': <PinecilSensor.UPTIME: 'uptime'>,
|
||||||
|
'unique_id': 'c0:ff:ee:c0:ff:ee_uptime',
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.pinecil_uptime-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'Pinecil Uptime',
|
||||||
|
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.pinecil_uptime',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '1671',
|
||||||
|
})
|
||||||
|
# ---
|
|
@ -0,0 +1,66 @@
|
||||||
|
"""Tests for the Pinecil config flow."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
|
from homeassistant.components.iron_os import DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_USER
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from .conftest import DEFAULT_NAME, PINECIL_SERVICE_INFO, USER_INPUT
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form(
|
||||||
|
hass: HomeAssistant, mock_setup_entry: AsyncMock, discovery: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Test the user config flow."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
USER_INPUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == DEFAULT_NAME
|
||||||
|
assert result["data"] == {}
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_no_device_discovered(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
discovery: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test setup with no device discoveries."""
|
||||||
|
discovery.return_value = []
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "no_devices_found"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_step_bluetooth(hass: HomeAssistant) -> None:
|
||||||
|
"""Test discovery via bluetooth.."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_BLUETOOTH},
|
||||||
|
data=PINECIL_SERVICE_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "bluetooth_confirm"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == DEFAULT_NAME
|
||||||
|
assert result["data"] == {}
|
||||||
|
assert result["result"].unique_id == "c0:ff:ee:c0:ff:ee"
|
|
@ -0,0 +1,73 @@
|
||||||
|
"""Tests for the Pinecil Sensors."""
|
||||||
|
|
||||||
|
from collections.abc import AsyncGenerator
|
||||||
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from pynecil import CommunicationError
|
||||||
|
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.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
async def sensor_only() -> AsyncGenerator[None, None]:
|
||||||
|
"""Enable only the sensor platform."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.iron_os.PLATFORMS",
|
||||||
|
[Platform.SENSOR],
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_pynecil: AsyncMock,
|
||||||
|
ble_device: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the Pinecil sensor platform."""
|
||||||
|
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
|
||||||
|
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_sensors_unavailable(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_pynecil: AsyncMock,
|
||||||
|
ble_device: MagicMock,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test the sensors when device disconnects."""
|
||||||
|
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
|
||||||
|
|
||||||
|
mock_pynecil.get_live_data.side_effect = CommunicationError
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
|
||||||
|
entity_entries = er.async_entries_for_config_entry(
|
||||||
|
entity_registry, config_entry.entry_id
|
||||||
|
)
|
||||||
|
for entity_entry in entity_entries:
|
||||||
|
assert hass.states.get(entity_entry.entity_id).state == STATE_UNAVAILABLE
|
Loading…
Reference in New Issue