From f7662fe73dfa4b7ca04f874ba6b0f8a31406f13e Mon Sep 17 00:00:00 2001 From: Ronald van der Meer Date: Wed, 11 Mar 2026 16:15:30 +0100 Subject: [PATCH] feat: extend PDPR1H1HAW100_FW539187 mapping with 22 new entities Add 22 new entities discovered via live device analysis: - 11 binary_sensors: OFA2 alarms, temperature/concentration alarms, system standby - 6 sensors with conversions: circulation pump status, peristaltic CL dosing, device config, temperature unit, power-on/flow delay status - 5 numbers: dosing off-timers (pH/ORP/CL), power-on delay timer, flow delay timer Total mapped entities: 32 -> 54 Skipped: alarm-page duplicates, read-only timer displays, opaque device status, redundant flowrate unit display Add 8 tests for the extended mapping covering entity counts, alarm binary sensors, sensor conversions, and number entities. --- .../model_PDPR1H1HAW100_FW539187.json | 115 ++++++++++++++++++ tests/test_mapping_info.py | 87 +++++++++++++ 2 files changed, 202 insertions(+) diff --git a/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json b/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json index 539b57f..0c0ca78 100644 --- a/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json +++ b/src/pooldose/mappings/model_PDPR1H1HAW100_FW539187.json @@ -165,5 +165,120 @@ "PDPR1H1HAW100_FW539187_COMBO_w_1eklinki6_M_": "m3", "PDPR1H1HAW100_FW539187_COMBO_w_1eklinki6_LITER": "L" } + }, + "alarm_ofa2_ph": { + "key": "w_1eklfb8ob", + "type": "binary_sensor" + }, + "alarm_ofa2_orp": { + "key": "w_1eo1paejq", + "type": "binary_sensor" + }, + "alarm_ofa2_cl": { + "key": "w_1eo1pk0kl", + "type": "binary_sensor" + }, + "alarm_water_too_cold": { + "key": "w_1fahufq7q", + "type": "binary_sensor" + }, + "alarm_water_too_hot": { + "key": "w_1fahufrqe", + "type": "binary_sensor" + }, + "alarm_ph_too_low": { + "key": "w_1fahuft7o", + "type": "binary_sensor" + }, + "alarm_ph_too_high": { + "key": "w_1fahufum0", + "type": "binary_sensor" + }, + "alarm_cl_too_low_orp": { + "key": "w_1fahug0lo", + "type": "binary_sensor" + }, + "alarm_cl_too_high_orp": { + "key": "w_1fahug414", + "type": "binary_sensor" + }, + "alarm_cl_too_high": { + "key": "w_1fahvms2n", + "type": "binary_sensor" + }, + "alarm_system_standby": { + "key": "w_1fai1n09b", + "type": "binary_sensor" + }, + "circulation_pump_status": { + "key": "w_1eo2fpohe", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_DISABLED_|": "disabled", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo2fpohe_ENABLED|": "enabled" + } + }, + "peristaltic_cl_dosing": { + "key": "w_1eo1sf1m6", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo1sf1m6_OFF|": "off", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo1sf1m6_PROPORTIONAL|": "proportional", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo1sf1m6_ON_OFF|": "on / off", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo1sf1m6_TIMED|": "timed", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eo1sf1m6_CYCLE|": "cycle" + } + }, + "device_config": { + "key": "w_1eu60qapu", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1eu60qapu_PH_ORP|": "ph_orp", + "|PDPR1H1HAW100_FW539187_LABEL_w_1eu60qapu_PH_ORP_CHLORINE|": "ph_orp_chlorine" + } + }, + "temperature_unit": { + "key": "w_1fkpc4jak", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1fkpc4jak__C|": "celsius", + "|PDPR1H1HAW100_FW539187_LABEL_w_1fkpc4jak__F|": "fahrenheit" + } + }, + "power_on_delay_status": { + "key": "w_1fhb0hqu2", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_DISABLE|": "disabled", + "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0hqu2_ENABLE|": "enabled" + } + }, + "flow_delay_status": { + "key": "w_1fhb0ht7t", + "type": "sensor", + "conversion": { + "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_DISABLE|": "disabled", + "|PDPR1H1HAW100_FW539187_LABEL_w_1fhb0ht7t_ENABLE|": "enabled" + } + }, + "time_off_ph_dosing": { + "key": "w_1eklj30b7", + "type": "number" + }, + "time_off_orp_dosing": { + "key": "w_1eo1v30n4", + "type": "number" + }, + "time_off_cl_dosing": { + "key": "w_1eo20ibtm", + "type": "number" + }, + "power_on_delay_timer": { + "key": "w_1ffng5qhk", + "type": "number" + }, + "flow_delay_timer": { + "key": "w_1ffng5spg", + "type": "number" } } \ No newline at end of file diff --git a/tests/test_mapping_info.py b/tests/test_mapping_info.py index 017c69b..1f98424 100644 --- a/tests/test_mapping_info.py +++ b/tests/test_mapping_info.py @@ -59,3 +59,90 @@ def test_available_selects_missing_fields(): mapping_info = MappingInfo(mapping=fake_mapping, status=RequestStatus.SUCCESS) with pytest.raises(KeyError): _ = mapping_info.available_selects() + + +class TestPDPR1H1HAW100FW539187Mapping: + """Tests for the PDPR1H1HAW100 FW539187 mapping file.""" + + MODEL_ID = "PDPR1H1HAW100" + FW_CODE = "539187" + + @pytest.fixture + async def mapping(self): + """Load the mapping file.""" + info = await MappingInfo.load(self.MODEL_ID, self.FW_CODE) + assert info.status == RequestStatus.SUCCESS + assert info.mapping is not None + return info + + async def test_load_success(self, mapping): + """Test mapping file loads successfully.""" + assert mapping.status == RequestStatus.SUCCESS + + async def test_total_entity_count(self, mapping): + """Test total number of mapped entities.""" + assert len(mapping.mapping) == 54 + + async def test_entity_type_counts(self, mapping): + """Test entity count per type.""" + types = mapping.available_types() + assert len(types.get("sensor", [])) == 22 + assert len(types.get("binary_sensor", [])) == 20 + assert len(types.get("number", [])) == 8 + assert len(types.get("switch", [])) == 3 + assert len(types.get("select", [])) == 1 + + async def test_new_alarm_binary_sensors(self, mapping): + """Test new alarm binary sensors are present.""" + types = mapping.available_types() + expected_alarms = [ + "alarm_ofa2_ph", + "alarm_ofa2_orp", + "alarm_ofa2_cl", + "alarm_water_too_cold", + "alarm_water_too_hot", + "alarm_ph_too_low", + "alarm_ph_too_high", + "alarm_cl_too_low_orp", + "alarm_cl_too_high_orp", + "alarm_cl_too_high", + "alarm_system_standby", + ] + for alarm in expected_alarms: + assert alarm in types["binary_sensor"], f"Missing binary_sensor: {alarm}" + + async def test_new_sensors_with_conversions(self, mapping): + """Test new sensors with conversion dictionaries.""" + sensors = mapping.available_sensors() + sensors_with_conversion = [ + "circulation_pump_status", + "peristaltic_cl_dosing", + "device_config", + "temperature_unit", + "power_on_delay_status", + "flow_delay_status", + ] + for name in sensors_with_conversion: + assert name in sensors, f"Missing sensor: {name}" + assert sensors[name].conversion is not None, f"Missing conversion for {name}" + assert len(sensors[name].conversion) >= 2, f"Conversion too small for {name}" + + async def test_new_number_entities(self, mapping): + """Test new number entities are present.""" + types = mapping.available_types() + expected_numbers = [ + "time_off_ph_dosing", + "time_off_orp_dosing", + "time_off_cl_dosing", + "power_on_delay_timer", + "flow_delay_timer", + ] + for number in expected_numbers: + assert number in types["number"], f"Missing number: {number}" + + async def test_select_entity(self, mapping): + """Test select entity with options and conversion.""" + selects = mapping.available_selects() + assert "water_meter_unit" in selects + assert len(selects["water_meter_unit"].options) == 2 + assert len(selects["water_meter_unit"].conversion) == 2