parent
4f255439eb
commit
f2b07ea886
|
@ -8,6 +8,7 @@ from enum import Enum
|
|||
import logging
|
||||
from typing import cast
|
||||
|
||||
from awesomeversion import AwesomeVersion
|
||||
from pynecil import (
|
||||
CharSetting,
|
||||
CommunicationError,
|
||||
|
@ -34,6 +35,8 @@ SCAN_INTERVAL = timedelta(seconds=5)
|
|||
SCAN_INTERVAL_GITHUB = timedelta(hours=3)
|
||||
SCAN_INTERVAL_SETTINGS = timedelta(seconds=60)
|
||||
|
||||
V223 = AwesomeVersion("v2.23")
|
||||
|
||||
|
||||
@dataclass
|
||||
class IronOSCoordinators:
|
||||
|
@ -72,6 +75,7 @@ class IronOSBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
|||
),
|
||||
)
|
||||
self.device = device
|
||||
self.v223_features = False
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Set up the coordinator."""
|
||||
|
@ -81,6 +85,8 @@ class IronOSBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
|||
except CommunicationError as e:
|
||||
raise UpdateFailed("Cannot connect to device") from e
|
||||
|
||||
self.v223_features = AwesomeVersion(self.device_info.build) >= V223
|
||||
|
||||
|
||||
class IronOSLiveDataCoordinator(IronOSBaseCoordinator[LiveDataResponse]):
|
||||
"""IronOS coordinator."""
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
},
|
||||
"power_limit": {
|
||||
"default": "mdi:flash-alert"
|
||||
},
|
||||
"hall_effect_sleep_time": {
|
||||
"default": "mdi:timer-sand"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
|
@ -105,6 +108,9 @@
|
|||
},
|
||||
"usb_pd_mode": {
|
||||
"default": "mdi:meter-electric-outline"
|
||||
},
|
||||
"tip_type": {
|
||||
"default": "mdi:pencil-outline"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
@ -154,7 +160,16 @@
|
|||
"soldering": "mdi:soldering-iron",
|
||||
"sleeping": "mdi:sleep",
|
||||
"settings": "mdi:menu-open",
|
||||
"debug": "mdi:bug-play"
|
||||
"debug": "mdi:bug-play",
|
||||
"soldering_profile": "mdi:chart-box-outline",
|
||||
"temperature_adjust": "mdi:thermostat-box",
|
||||
"usb_pd_debug": "mdi:bug-play",
|
||||
"thermal_runaway": "mdi:fire-alert",
|
||||
"startup_logo": "mdi:dots-circle",
|
||||
"cjc_calibration": "mdi:tune-vertical",
|
||||
"startup_warnings": "mdi:alert",
|
||||
"initialisation_done": "mdi:check-circle",
|
||||
"hibernating": "mdi:sleep"
|
||||
}
|
||||
},
|
||||
"estimated_power": {
|
||||
|
|
|
@ -65,6 +65,7 @@ class PinecilNumber(StrEnum):
|
|||
VOLTAGE_DIV = "voltage_div"
|
||||
TEMP_INCREMENT_SHORT = "temp_increment_short"
|
||||
TEMP_INCREMENT_LONG = "temp_increment_long"
|
||||
HALL_EFFECT_SLEEP_TIME = "hall_effect_sleep_time"
|
||||
|
||||
|
||||
def multiply(value: float | None, multiplier: float) -> float | None:
|
||||
|
@ -323,6 +324,23 @@ PINECIL_NUMBER_DESCRIPTIONS: tuple[IronOSNumberEntityDescription, ...] = (
|
|||
),
|
||||
)
|
||||
|
||||
PINECIL_NUMBER_DESCRIPTIONS_V223: tuple[IronOSNumberEntityDescription, ...] = (
|
||||
IronOSNumberEntityDescription(
|
||||
key=PinecilNumber.HALL_EFFECT_SLEEP_TIME,
|
||||
translation_key=PinecilNumber.HALL_EFFECT_SLEEP_TIME,
|
||||
value_fn=(lambda _, settings: settings.get("hall_sleep_time")),
|
||||
characteristic=CharSetting.HALL_SLEEP_TIME,
|
||||
raw_value_fn=lambda value: value,
|
||||
mode=NumberMode.BOX,
|
||||
native_min_value=0,
|
||||
native_max_value=60,
|
||||
native_step=5,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
|
@ -331,10 +349,13 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up number entities from a config entry."""
|
||||
coordinators = entry.runtime_data
|
||||
descriptions = PINECIL_NUMBER_DESCRIPTIONS
|
||||
|
||||
if coordinators.live_data.v223_features:
|
||||
descriptions += PINECIL_NUMBER_DESCRIPTIONS_V223
|
||||
|
||||
async_add_entities(
|
||||
IronOSNumberEntity(coordinators, description)
|
||||
for description in PINECIL_NUMBER_DESCRIPTIONS
|
||||
IronOSNumberEntity(coordinators, description) for description in descriptions
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from pynecil import (
|
|||
ScrollSpeed,
|
||||
SettingsDataResponse,
|
||||
TempUnit,
|
||||
TipType,
|
||||
USBPDMode,
|
||||
)
|
||||
|
||||
|
@ -53,6 +54,7 @@ class PinecilSelect(StrEnum):
|
|||
LOCKING_MODE = "locking_mode"
|
||||
LOGO_DURATION = "logo_duration"
|
||||
USB_PD_MODE = "usb_pd_mode"
|
||||
TIP_TYPE = "tip_type"
|
||||
|
||||
|
||||
def enum_to_str(enum: Enum | None) -> str | None:
|
||||
|
@ -138,6 +140,8 @@ PINECIL_SELECT_DESCRIPTIONS: tuple[IronOSSelectEntityDescription, ...] = (
|
|||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
)
|
||||
PINECIL_SELECT_DESCRIPTIONS_V222: tuple[IronOSSelectEntityDescription, ...] = (
|
||||
IronOSSelectEntityDescription(
|
||||
key=PinecilSelect.USB_PD_MODE,
|
||||
translation_key=PinecilSelect.USB_PD_MODE,
|
||||
|
@ -149,6 +153,27 @@ PINECIL_SELECT_DESCRIPTIONS: tuple[IronOSSelectEntityDescription, ...] = (
|
|||
entity_registry_enabled_default=False,
|
||||
),
|
||||
)
|
||||
PINECIL_SELECT_DESCRIPTIONS_V223: tuple[IronOSSelectEntityDescription, ...] = (
|
||||
IronOSSelectEntityDescription(
|
||||
key=PinecilSelect.USB_PD_MODE,
|
||||
translation_key=PinecilSelect.USB_PD_MODE,
|
||||
characteristic=CharSetting.USB_PD_MODE,
|
||||
value_fn=lambda x: enum_to_str(x.get("usb_pd_mode")),
|
||||
raw_value_fn=lambda value: USBPDMode[value.upper()],
|
||||
options=[x.name.lower() for x in USBPDMode],
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
IronOSSelectEntityDescription(
|
||||
key=PinecilSelect.TIP_TYPE,
|
||||
translation_key=PinecilSelect.TIP_TYPE,
|
||||
characteristic=CharSetting.TIP_TYPE,
|
||||
value_fn=lambda x: enum_to_str(x.get("tip_type")),
|
||||
raw_value_fn=lambda value: TipType[value.upper()],
|
||||
options=[x.name.lower() for x in TipType],
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -157,11 +182,17 @@ async def async_setup_entry(
|
|||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up select entities from a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinators = entry.runtime_data
|
||||
descriptions = PINECIL_SELECT_DESCRIPTIONS
|
||||
|
||||
descriptions += (
|
||||
PINECIL_SELECT_DESCRIPTIONS_V223
|
||||
if coordinators.live_data.v223_features
|
||||
else PINECIL_SELECT_DESCRIPTIONS_V222
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
IronOSSelectEntity(coordinator, description)
|
||||
for description in PINECIL_SELECT_DESCRIPTIONS
|
||||
IronOSSelectEntity(coordinators, description) for description in descriptions
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@
|
|||
},
|
||||
"temp_increment_long": {
|
||||
"name": "Long-press temperature step"
|
||||
},
|
||||
"hall_effect_sleep_time": {
|
||||
"name": "Hall sensor sleep timeout"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
|
@ -173,6 +176,15 @@
|
|||
"off": "[%key:common::state::off%]",
|
||||
"on": "[%key:common::state::on%]"
|
||||
}
|
||||
},
|
||||
"tip_type": {
|
||||
"name": "Soldering tip type",
|
||||
"state": {
|
||||
"auto": "Auto sense",
|
||||
"ts100_long": "TS100 long/Hakko T12 tip",
|
||||
"pine_short": "Pinecil short tip",
|
||||
"pts200": "PTS200 short tip"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
@ -223,7 +235,16 @@
|
|||
"sleeping": "Sleeping",
|
||||
"settings": "Settings",
|
||||
"debug": "Debug",
|
||||
"boost": "Boost"
|
||||
"boost": "Boost",
|
||||
"soldering_profile": "Soldering profile",
|
||||
"temperature_adjust": "Temperature adjust",
|
||||
"usb_pd_debug": "USB PD debug",
|
||||
"thermal_runaway": "Thermal runaway",
|
||||
"startup_logo": "Booting",
|
||||
"cjc_calibration": "CJC calibration",
|
||||
"startup_warnings": "Startup warnings",
|
||||
"initialisation_done": "Initialisation done",
|
||||
"hibernating": "Hibernating"
|
||||
}
|
||||
},
|
||||
"estimated_power": {
|
||||
|
|
|
@ -20,6 +20,7 @@ from pynecil import (
|
|||
ScrollSpeed,
|
||||
SettingsDataResponse,
|
||||
TempUnit,
|
||||
TipType,
|
||||
)
|
||||
import pytest
|
||||
|
||||
|
@ -164,7 +165,7 @@ def mock_pynecil() -> Generator[AsyncMock]:
|
|||
client = mock_client.return_value
|
||||
|
||||
client.get_device_info.return_value = DeviceInfoResponse(
|
||||
build="v2.22",
|
||||
build="v2.23",
|
||||
device_id="c0ffeeC0",
|
||||
address="c0:ff:ee:c0:ff:ee",
|
||||
device_sn="0000c0ffeec0ffee",
|
||||
|
@ -205,6 +206,8 @@ def mock_pynecil() -> Generator[AsyncMock]:
|
|||
display_invert=True,
|
||||
calibrate_cjc=True,
|
||||
usb_pd_mode=True,
|
||||
hall_sleep_time=5,
|
||||
tip_type=TipType.PINE_SHORT,
|
||||
)
|
||||
client.get_live_data.return_value = LiveDataResponse(
|
||||
live_temp=298,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
}),
|
||||
'device_info': dict({
|
||||
'__type': "<class 'pynecil.types.DeviceInfoResponse'>",
|
||||
'repr': "DeviceInfoResponse(build='v2.22', device_id='c0ffeeC0', address='c0:ff:ee:c0:ff:ee', device_sn='0000c0ffeec0ffee', name='Pinecil-C0FFEEE', is_synced=False)",
|
||||
'repr': "DeviceInfoResponse(build='v2.23', device_id='c0ffeeC0', address='c0:ff:ee:c0:ff:ee', device_sn='0000c0ffeec0ffee', name='Pinecil-C0FFEEE', is_synced=False)",
|
||||
}),
|
||||
'live_data': dict({
|
||||
'__type': "<class 'pynecil.types.LiveDataResponse'>",
|
||||
|
|
|
@ -226,6 +226,63 @@
|
|||
'state': '7',
|
||||
})
|
||||
# ---
|
||||
# name: test_state[number.pinecil_hall_sensor_sleep_timeout-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 60,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 5,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.pinecil_hall_sensor_sleep_timeout',
|
||||
'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 sensor sleep timeout',
|
||||
'platform': 'iron_os',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': <PinecilNumber.HALL_EFFECT_SLEEP_TIME: 'hall_effect_sleep_time'>,
|
||||
'unique_id': 'c0:ff:ee:c0:ff:ee_hall_effect_sleep_time',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_state[number.pinecil_hall_sensor_sleep_timeout-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Pinecil Hall sensor sleep timeout',
|
||||
'max': 60,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 5,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.pinecil_hall_sensor_sleep_timeout',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '5',
|
||||
})
|
||||
# ---
|
||||
# name: test_state[number.pinecil_keep_awake_pulse_delay-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
'options': list([
|
||||
'off',
|
||||
'on',
|
||||
'safe',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
|
@ -287,6 +288,7 @@
|
|||
'options': list([
|
||||
'off',
|
||||
'on',
|
||||
'safe',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
|
@ -415,6 +417,66 @@
|
|||
'state': 'fast',
|
||||
})
|
||||
# ---
|
||||
# name: test_state[select.pinecil_soldering_tip_type-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'auto',
|
||||
'ts100_long',
|
||||
'pine_short',
|
||||
'pts200',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.pinecil_soldering_tip_type',
|
||||
'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': 'Soldering tip type',
|
||||
'platform': 'iron_os',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': <PinecilSelect.TIP_TYPE: 'tip_type'>,
|
||||
'unique_id': 'c0:ff:ee:c0:ff:ee_tip_type',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_state[select.pinecil_soldering_tip_type-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Pinecil Soldering tip type',
|
||||
'options': list([
|
||||
'auto',
|
||||
'ts100_long',
|
||||
'pine_short',
|
||||
'pts200',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.pinecil_soldering_tip_type',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'pine_short',
|
||||
})
|
||||
# ---
|
||||
# name: test_state[select.pinecil_start_up_behavior-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
'entity_picture': 'https://brands.home-assistant.io/_/iron_os/icon.png',
|
||||
'friendly_name': 'Pinecil Firmware',
|
||||
'in_progress': False,
|
||||
'installed_version': 'v2.22',
|
||||
'installed_version': 'v2.23',
|
||||
'latest_version': 'v2.22',
|
||||
'release_summary': None,
|
||||
'release_url': 'https://github.com/Ralim/IronOS/releases/tag/v2.22',
|
||||
|
|
|
@ -4,13 +4,15 @@ from datetime import timedelta
|
|||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pynecil import CommunicationError
|
||||
from pynecil import CommunicationError, DeviceInfoResponse
|
||||
import pytest
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import DEFAULT_NAME
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
|
@ -89,3 +91,35 @@ async def test_settings_exception(
|
|||
|
||||
assert (state := hass.states.get("number.pinecil_boost_temperature"))
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"entity_registry_enabled_by_default", "mock_pynecil", "ble_device"
|
||||
)
|
||||
async def test_v223_entities_not_loaded(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_pynecil: AsyncMock,
|
||||
) -> None:
|
||||
"""Test the new entities in IronOS v2.23 are not loaded on smaller versions."""
|
||||
|
||||
mock_pynecil.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,
|
||||
)
|
||||
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
|
||||
|
||||
assert hass.states.get("number.pinecil_hall_sensor_sleep_timeout") is None
|
||||
assert hass.states.get("select.pinecil_soldering_tip_type") is None
|
||||
assert (
|
||||
state := hass.states.get("select.pinecil_power_delivery_3_1_epr")
|
||||
) is not None
|
||||
|
||||
assert len(state.attributes["options"]) == 2
|
||||
|
|
|
@ -138,6 +138,12 @@ async def test_state(
|
|||
("number.pinecil_sleep_temperature", CharSetting.SLEEP_TEMP, 150, 150),
|
||||
("number.pinecil_sleep_timeout", CharSetting.SLEEP_TIMEOUT, 5, 5),
|
||||
("number.pinecil_voltage_divider", CharSetting.VOLTAGE_DIV, 600, 600),
|
||||
(
|
||||
"number.pinecil_hall_sensor_sleep_timeout",
|
||||
CharSetting.HALL_SLEEP_TIME,
|
||||
60,
|
||||
60,
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "ble_device")
|
||||
|
|
|
@ -16,6 +16,7 @@ from pynecil import (
|
|||
ScreenOrientationMode,
|
||||
ScrollSpeed,
|
||||
TempUnit,
|
||||
TipType,
|
||||
USBPDMode,
|
||||
)
|
||||
import pytest
|
||||
|
@ -111,6 +112,11 @@ async def test_state(
|
|||
"on",
|
||||
(CharSetting.USB_PD_MODE, USBPDMode.ON),
|
||||
),
|
||||
(
|
||||
"select.pinecil_soldering_tip_type",
|
||||
"auto",
|
||||
(CharSetting.TIP_TYPE, TipType.AUTO),
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "ble_device")
|
||||
|
|
Loading…
Reference in New Issue