ha(H3): превью шаблона с конверсиями; climate и сущности
- config flow: шаг preview (шаблон A/B/F, диапазон/шаг уставки, линейные коэффициенты num/den/offset, текст-превью по данным trial: hvac/fan/ swing/capabilities/текущая температура) - coordinator: push-driven DataUpdateCoordinator (снимок кэша, доступность online/recovering, первичный GET-батч, один batch_commit на действие) - overrides.py: ConversionSet (ручные линейные конверсии и диапазон поверх таблиц ядра, применяются в HA) - entity.py: DeviceInfo/unique_id/доступность, capabilities-фильтры - climate: hvac/fan/swing/vertical+horizontal swing/preset ECO-BOOST, уставка 16-30 °C (шаг по шаблону), current_temperature - sensor: room_temperature, error_code, connection_state (диагностика) - binary_sensor: connectivity + флаги op_status (PROTOCOL §8.4) - switch: 8 флагов-свойств; select: положения заслонок 0..N-1 - тесты: превью/конверсии/ошибки диапазона, сущности против mock_ac (снимок, команды, capabilities-фильтр, ручная конверсия), ConversionSet - mock_ac: --set NAME=VALUE (0b/0x) для сценариев - план: demand_control отложен (семантика не зафиксирована)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Ручные конверсии/диапазоны (ConversionSet) без HA-рантайма."""
|
||||
from __future__ import annotations
|
||||
|
||||
from custom_components.fglair.overrides import ConversionSet, LinearOverride
|
||||
from pyfglair import Prop, Template
|
||||
|
||||
|
||||
def test_identity_keeps_api_units():
|
||||
conversions = ConversionSet(Template.A, {}, 5)
|
||||
assert conversions.to_display(Prop.DISPLAY_TEMPERATURE, 200) == 200
|
||||
assert conversions.to_display(Prop.ADJUST_TEMPERATURE, 220) == 220
|
||||
assert conversions.from_display(Prop.ADJUST_TEMPERATURE, 220) == 220
|
||||
assert conversions.temperature_range() == (160, 300)
|
||||
|
||||
|
||||
def test_linear_override_roundtrip_and_clamp():
|
||||
override = LinearOverride(
|
||||
num=1, den=1, offset=5, min_value=170, max_value=280
|
||||
)
|
||||
conversions = ConversionSet(
|
||||
Template.A, {Prop.ADJUST_TEMPERATURE: override}, 5
|
||||
)
|
||||
assert conversions.to_display(Prop.ADJUST_TEMPERATURE, 220) == 225
|
||||
assert conversions.from_display(Prop.ADJUST_TEMPERATURE, 225) == 220
|
||||
assert conversions.clamp(Prop.ADJUST_TEMPERATURE, 100) == 170
|
||||
assert conversions.clamp(Prop.ADJUST_TEMPERATURE, 999) == 280
|
||||
assert conversions.temperature_range() == (170, 280)
|
||||
|
||||
|
||||
def test_linear_override_scaled():
|
||||
override = LinearOverride(num=2, den=4, offset=0)
|
||||
conversions = ConversionSet(
|
||||
Template.A, {Prop.ADJUST_TEMPERATURE: override}, 5
|
||||
)
|
||||
assert conversions.to_display(Prop.ADJUST_TEMPERATURE, 200) == 100
|
||||
assert conversions.from_display(Prop.ADJUST_TEMPERATURE, 100) == 200
|
||||
|
||||
|
||||
def test_from_entry_parses_spec():
|
||||
conversions = ConversionSet.from_entry(
|
||||
{
|
||||
"template": "B",
|
||||
"temp_step": 10,
|
||||
"overrides": {
|
||||
"adjust_temperature": {
|
||||
"num": 2,
|
||||
"den": 4,
|
||||
"offset": 5,
|
||||
"min": 170,
|
||||
"max": 280,
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
assert conversions.template == Template.B
|
||||
assert conversions.step_tenths == 10
|
||||
assert conversions.temperature_range() == (170, 280)
|
||||
override = conversions.override(Prop.ADJUST_TEMPERATURE)
|
||||
assert (override.num, override.den, override.offset) == (2, 4, 5)
|
||||
assert conversions.override(Prop.FAN_SPEED).identity
|
||||
|
||||
|
||||
def test_from_entry_defaults():
|
||||
conversions = ConversionSet.from_entry({"template": "F"})
|
||||
assert conversions.step_tenths == 5
|
||||
assert conversions.temperature_range() == (160, 300)
|
||||
Reference in New Issue
Block a user