Skip to content

Add MODEL_ALIASES for devices reporting different PRODUCT_CODE#21

Merged
lmaertin merged 1 commit intolmaertin:mainfrom
ronaldvdmeer:fix/model-alias-PDPR1H1HAW102
Mar 13, 2026
Merged

Add MODEL_ALIASES for devices reporting different PRODUCT_CODE#21
lmaertin merged 1 commit intolmaertin:mainfrom
ronaldvdmeer:fix/model-alias-PDPR1H1HAW102

Conversation

@ronaldvdmeer
Copy link
Contributor

Problem

Some devices report a PRODUCT_CODE that differs from the model ID used in their data keys and mapping files. The POOLDOSE pH+ORP CF Group Wi-Fi device reports PDPR1H1HAW102 as its PRODUCT_CODE, but the actual data keys and labels use PDPR1H1HAW100.

This causes MappingInfo.load() to fail because it looks for model_PDPR1H1HAW102_FW539187.json which does not exist. The data prefix also mismatches, breaking instant_values().

Error in Home Assistant:

Failed setup, will retry: API returned status: RequestStatus.UNKNOWN_ERROR 

2026-03-08 16:23:33.943 WARNING (MainThread) [pooldose.mappings.mapping_info] Error loading model mapping: 
[Errno 2] No such file or directory: '/usr/local/lib/python3.13/site-packages/pooldose/mappings/model_PDPR1H1HAW102_FW539187.json'

Solution

Introduce a centralized MODEL_ALIASES dictionary in constants.py that maps reported PRODUCT_CODE values to the actual model ID used in data keys and mappings.

This replaces the existing hardcoded if model_id == 'PDHC1H1HAR1V1' workaround in instant_values() with a cleaner, extensible approach that also covers the mapping-loading step in _load_device_info().

Changes

  • constants.py: Add MODEL_ALIASES dict with entries for PDHC1H1HAR1V1 (existing) and PDPR1H1HAW102 (new)
  • client.py: Use MODEL_ALIASES in both _load_device_info() and instant_values() instead of the hardcoded if-statement
  • conftest.py: Add test fixtures for aliased device scenarios
  • test_client.py: Add 3 tests verifying alias resolution during connect and data fetching

Device info

{
  "MODEL": "POOLDOSE pH+ORP CF Group Wi-Fi",
  "MODEL_ID": "PDPR1H1HAW102",
  "FW_VERSION": "1.7",
  "SW_VERSION": "3.00",
  "FW_CODE": "539187"
}

All 126 tests pass.

Tested on real device

Verified with a POOLDOSE pH+ORP CF Group Wi-Fi device (PRODUCT_CODE: PDPR1H1HAW102, FW code: 539187).

Before fix:

Error loading model mapping: [Errno 2] No such file or directory:
.../pooldose/mappings/model_PDPR1H1HAW102_FW539187.json

After fix:

Python PoolDose Client
========================================
Connecting to PoolDose device at 192.168.3.158
Using HTTP on port 80
Failed to fetch MAC address via getmac library for IP: 192.168.3.158
Connected successfully!

Device Information:
  Name: XXXXXXXXXXX
  Serial: XXXXXXXXXXX
  Model: POOLDOSE pH+ORP CF Group Wi-Fi
  Firmware: 1.7
  IP: 192.168.3.158
  MAC: None

Sensor:
  temperature: 17.5 °C
  ph: 7.4
  orp: 722 mV
  cl: 0 ppm
  ph_type_dosing: acid
  peristaltic_ph_dosing: proportional
  orp_type_dosing: low
  peristaltic_orp_dosing: proportional
  ph_calibration_type: 2_points
  ph_calibration_offset: 0 mV
  ph_calibration_slope: 59.16 mV
  orp_calibration_type: 1_point
  orp_calibration_offset: 0 mV
  orp_calibration_slope: 1 mV
  ofa_ph_time: 0 min
  ofa_orp_time: 0 min

Binary_Sensor:
  pump_alarm: False
  ph_level_alarm: False
  orp_level_alarm: False
  flow_rate_alarm: False
  relay_alarm: False
  relay_aux1: False
  relay_aux2: False
  alarm_ofa_ph: False
  alarm_ofa_orp: False

Number:
  ph_target: 7.3
  orp_target: 710 mV
  cl_target: 1 ppm

Switch:
  pause_dosing: False
  pump_monitoring: False
  frequency_input: False

Select:
  water_meter_unit: L

Connection completed successfully!

@ronaldvdmeer
Copy link
Contributor Author

While investigating this issue, I also noticed that set_value(), get_device_language() and reboot_device() in request_handler.py create their own aiohttp.ClientSession instead of using _get_session(). This bypasses any externally provided session (e.g. from Home Assistant). See #23

@ronaldvdmeer
Copy link
Contributor Author

@lmaertin a friendly reminder in case you've missed recent activity in the repository

@lmaertin
Copy link
Owner

Hi @ronaldvdmeer, thanks for the reminder. Actually I have missed the notification. I like you proposal and will review the PR over weekend. Thanks for contributing!

@ronaldvdmeer
Copy link
Contributor Author

Hi @ronaldvdmeer, thanks for the reminder. Actually I have missed the notification. I like you proposal and will review the PR over weekend. Thanks for contributing!

Great! Also did some other PR's and made a proposal to fix an open issue. If you need some clarification let me know.

@lmaertin lmaertin self-assigned this Mar 13, 2026
@lmaertin lmaertin merged commit 8852437 into lmaertin:main Mar 13, 2026
@ronaldvdmeer ronaldvdmeer deleted the fix/model-alias-PDPR1H1HAW102 branch March 14, 2026 12:37
@lmaertin
Copy link
Owner

lmaertin commented Mar 14, 2026

Hi @ronaldvdmeer, thanks for the reminder. Actually I have missed the notification. I like you proposal and will review the PR over weekend. Thanks for contributing!

Great! Also did some other PR's and made a proposal to fix an open issue. If you need some clarification let me know.

Hi @ronaldvdmeer, HA Core PR home-assistant/core#165507 got merged, may you want to use this script to switch from core component to a custom component based on the Dev Branch for pooldose from HA's terminal:

cd /config
curl -o- -L https://gist.githubusercontent.com/bdraco/43f8043cb04b9838383fd71353e99b18/raw/core_integration_pr | bash /dev/stdin -d pooldose -p 165507

Kindly let me know if all works for you.

@ronaldvdmeer
Copy link
Contributor Author

Hi @lmaertin, I tested the custom component and the integration works correctly with my device.

I also verified that the environment is now using python-pooldose 0.8.5, and the CLI (pooldose --analyze) works against my PoolDose. So the library side appears to be in place.

As mentioned in my original PR, the additional values still need the corresponding EntityDescription mappings in the Home Assistant integration for them to appear as entities.

Do you prefer to add those mappings on the HA side yourself, or would you like me to open a follow-up core PR for that?

@lmaertin
Copy link
Owner

Do you have experiences with HA integration development? May you start and I can review.

@ronaldvdmeer
Copy link
Contributor Author

I don't have experience with Home Assistant integration development yet.

If it's relatively straightforward, with a clear structure and well documented guidelines from the HA side, I'm happy to have a look and give it a try. Otherwise it might be better if you handle that part.

@lmaertin
Copy link
Owner

May we can share work. Which items do you want to propagate? Monitoring, Diagnostics, Control?

Shall some of those be deactivated by default in HA?

@ronaldvdmeer
Copy link
Contributor Author

Let me review the new values from my device and propose a small set that I think would make sense to expose in Home Assistant (monitoring vs diagnostics vs control).

I'll come back with a suggestion for which entities should be included and which ones might be better disabled by default.

Do you agree with that approach?

@lmaertin
Copy link
Owner

Yes, this makes sense, as I cannot test with my device. Please share example values as well, I need those for the test fixtures.

@ronaldvdmeer
Copy link
Contributor Author

Hi @lmaertin, here's my proposal for the new entities, including categorization, default enabled/disabled state, and example values from my live device.

Let me know if you agree with this categorization, or if you'd like to adjust anything. Once aligned, I'd like to open a draft PR on HA core (dev branch) so you can review it before it goes live. Sound good?

Binary sensors — Monitoring (11 new)

All use device_class: PROBLEM and entity_category: DIAGNOSTIC.

Entity key Name Default Example value Notes
alarm_ofa2_ph pH overfeed 2 Enabled false Secondary overfeed alarm
alarm_ofa2_orp ORP overfeed 2 Enabled false Secondary overfeed alarm
alarm_ofa2_cl Chlorine overfeed 2 Enabled false Secondary overfeed alarm
alarm_water_too_cold Water too cold Enabled true Temperature alarm
alarm_water_too_hot Water too hot Enabled false Temperature alarm
alarm_ph_too_low pH too low Enabled false Concentration alarm
alarm_ph_too_high pH too high Enabled false Concentration alarm
alarm_cl_too_low_orp Chlorine too low (ORP) Enabled false Concentration alarm
alarm_cl_too_high_orp Chlorine too high (ORP) Enabled true Concentration alarm
alarm_cl_too_high Chlorine too high Enabled false Concentration alarm
alarm_system_standby System standby Enabled true System status

Rationale: All alarms should be enabled by default — they are safety-relevant indicators that users typically want to monitor and automate on.

Sensors — Diagnostics (6 new)

All use entity_category: DIAGNOSTIC and device_class: ENUM.

Entity key Name Default Example value Options Notes
circulation_pump_status Circulation pump Disabled "disabled" disabled, enabled Config readback
peristaltic_cl_dosing (existing) "off" off, proportional, on_off, timed, cycle Add cycle as 5th option
device_config Device configuration Disabled "ph_orp" ph_orp, ph_orp_chlorine Rarely changes
temperature_unit Temperature unit Disabled "celsius" celsius, fahrenheit Rarely changes
power_on_delay_status Power-on delay Disabled "disabled" disabled, enabled Config readback
flow_delay_status Flow delay Disabled "disabled" disabled, enabled Config readback

Rationale: These are configuration readbacks that rarely change. Disabled by default to keep the entity list clean, but available for advanced users.

Numbers — Config / Control (5 new)

All use entity_category: CONFIG, native_unit_of_measurement: SECONDS.

Entity key Name Default Example value Min Max Step Notes
time_off_ph_dosing pH dosing off-time Disabled 60 1 360 1 Pause between pH dosing cycles
time_off_orp_dosing ORP dosing off-time Disabled 60 1 360 1 Pause between ORP dosing cycles
time_off_cl_dosing Chlorine dosing off-time Disabled 60 1 360 1 Pause between CL dosing cycles
power_on_delay_timer Power-on delay timer Disabled 0 0 5400 1 Delay after power-on before dosing
flow_delay_timer Flow delay timer Disabled 0 0 3600 1 Delay after flow detected before dosing

Rationale: These are advanced configuration parameters. Disabled by default to prevent accidental changes. Experienced users can enable them when needed.

Example fixture data (additions to instantvalues.json)

These values come from a live PDPR1H1HAW100 device (FW539187). They should be merged into the existing fixture at the appropriate nesting level per platform type.

Add to "binary_sensor" section:

    "alarm_ofa2_ph": {
      "value": false
    },
    "alarm_ofa2_orp": {
      "value": false
    },
    "alarm_ofa2_cl": {
      "value": false
    },
    "alarm_water_too_cold": {
      "value": true
    },
    "alarm_water_too_hot": {
      "value": false
    },
    "alarm_ph_too_low": {
      "value": false
    },
    "alarm_ph_too_high": {
      "value": false
    },
    "alarm_cl_too_low_orp": {
      "value": false
    },
    "alarm_cl_too_high_orp": {
      "value": true
    },
    "alarm_cl_too_high": {
      "value": false
    },
    "alarm_system_standby": {
      "value": true
    }

Add to "sensor" section:

    "circulation_pump_status": {
      "value": "disabled",
      "unit": null
    },
    "device_config": {
      "value": "ph_orp",
      "unit": null
    },
    "temperature_unit": {
      "value": "celsius",
      "unit": null
    },
    "power_on_delay_status": {
      "value": "disabled",
      "unit": null
    },
    "flow_delay_status": {
      "value": "disabled",
      "unit": null
    }

Note: peristaltic_cl_dosing already exists in the fixture with value "off". No fixture change needed — only the entity description needs the extra "cycle" option added.

Add to "number" section:

    "time_off_ph_dosing": {
      "value": 60,
      "unit": "s",
      "min": 1,
      "max": 360,
      "step": 1
    },
    "time_off_orp_dosing": {
      "value": 60,
      "unit": "s",
      "min": 1,
      "max": 360,
      "step": 1
    },
    "time_off_cl_dosing": {
      "value": 60,
      "unit": "s",
      "min": 1,
      "max": 360,
      "step": 1
    },
    "power_on_delay_timer": {
      "value": 0,
      "unit": "s",
      "min": 0,
      "max": 5400,
      "step": 1
    },
    "flow_delay_timer": {
      "value": 0,
      "unit": "s",
      "min": 0,
      "max": 3600,
      "step": 1
    }

Summary

Category New entities Default enabled Default disabled
Monitoring (binary_sensor) 11 11 0
Diagnostics (sensor) 5 + 1 update 0 5
Config/Control (number) 5 0 5
Total 22 11 10

@lmaertin
Copy link
Owner

Thanks, very well done. I will have a closer look tomorrow and give feedback.

@ronaldvdmeer
Copy link
Contributor Author

ronaldvdmeer commented Mar 14, 2026

Hi @lmaertin, I've implemented the HA core changes for the 22 new entities on my fork. Could you review?

Diff: ronaldvdmeer/core@dev...add-pooldose-entities

What's included:

  • 11 binary sensor descriptions (alarm monitoring, enabled by default)
  • 5 new sensor descriptions (diagnostics, disabled by default) + cycle option added to existing peristaltic_cl_dosing
  • 5 number descriptions (config timers, disabled by default)
  • Translations in strings.json
  • Updated test fixtures and snapshots

Verified locally:

  • All 75 tests pass
  • 99% code coverage
  • Ruff formatting clean
  • hassfest validation: 0 errors

Once you're happy with this, I'll open the draft PR on home-assistant/core. Let me know if the docs page needs updating too — I don't think it lists individual entities, but you'd know better.

@lmaertin
Copy link
Owner

Thanks, I will include it now, please standy-by

@lmaertin
Copy link
Owner

    BinarySensorEntityDescription(
        key="alarm_cl_too_low_orp",
        translation_key="alarm_cl_too_low_orp",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    BinarySensorEntityDescription(
        key="alarm_cl_too_high_orp",
        translation_key="alarm_cl_too_high_orp",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    BinarySensorEntityDescription(
        key="alarm_cl_too_high",
        translation_key="alarm_cl_too_high",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),

Is there no alarm_cl_too_low (without ORP)?

@ronaldvdmeer
Copy link
Contributor Author

No, alarm_cl_too_low (without ORP) does not exist in the device firmware. The device has three chlorine alarms:

  • alarm_cl_too_low_orp — chlorine too low, measured via ORP sensor (indirect)
  • alarm_cl_too_high_orp — chlorine too high, measured via ORP sensor (indirect)
  • alarm_cl_too_high — chlorine too high, measured via the direct CL sensor (amperometric)

The direct CL sensor only triggers an alarm when chlorine is too high, not when it's too low. This matches the device's web interface which shows exactly these three alarms.

@lmaertin
Copy link
Owner

Do you have an idea why there are secondary overfeed alarms? On my device only the secondary alarm is present.

@ronaldvdmeer
Copy link
Contributor Author

The OFA2 (secondary overfeed) alarms only exist on the PDPR1H1HAW100 model (FW539187). Looking at all four device mappings:

Model OFA alarms
PDPH1H1HAW100 (yours) alarm_ofa_ph, alarm_ofa_orp
PDPR1H1HAR1V0 alarm_ofa_ph, alarm_ofa_orp
PDHC1H1HAR1V1 alarm_ofa_ph, alarm_ofa_orp, alarm_ofa_cl
PDPR1H1HAW100 alarm_ofa_ph, alarm_ofa_orp + alarm_ofa2_ph, alarm_ofa2_orp, alarm_ofa2_cl

The PDPR1H1HAW100 firmware (FW539187) exposes two separate sets of OFA widgets, one on the Instant Values tab (OFA1/OFA2 indicators) and a duplicate set on the Alarms tab. My guess is the device has a primary and secondary overfeed threshold, but I don't know exactly why this model has both while yours only has the primary. It could be a firmware version difference, your device runs FW539176 while mine runs FW539187.

Since these entities are only created when the device's mapping actually contains them (the library only returns entities that exist in the device's widget list), alarm_ofa2_* will only appear for devices that have them. They won't show up on your device.

@lmaertin
Copy link
Owner

Thanks for your analysis, I tend to take these as "pH overfeed alternative" instead of "...2" or "secondary". In this the user is not confused when there is a "secondary" entry, but no "primary" available.

see mine:
image

@ronaldvdmeer
Copy link
Contributor Author

Done, I've renamed the OFA2 entities to use "alternative" instead of "2":

  • "pH overfeed 2" → pH overfeed alternative
  • "ORP overfeed 2" → ORP overfeed alternative
  • "Chlorine overfeed 2" → Chlorine overfeed alternative

This matches your naming convention. Updated strings, snapshots, and all 75 tests still pass. Changes are pushed to the branch.

@lmaertin
Copy link
Owner

I'm working on a PR already - merged your commit from the initial PR. you do not need to adapt yours ;)

@ronaldvdmeer
Copy link
Contributor Author

Haha great, thanks for picking it up! Happy to help with the groundwork. Let me know if you need anything else for the PR. Glad to assist where I can.

@lmaertin
Copy link
Owner

PR draft home-assistant/core#165608
I'm updating the docs now and then set the PR to be reviewed...

@lmaertin
Copy link
Owner

Ok, Core and Docs PR are on the way. Seems there is an issue in the CORE PR pipeline; It's failing, but I do not see an issue in our inputs...

I have another point: Actually I'm thinking of to move the "enabled/disabled" sensors to binary_sensors. In python-pooldose those must be mapped accordingly.

@lmaertin
Copy link
Owner

Reviewer spotted just the same ;-)

Please map to type binary_sensor and conversion to "True" for "Enabled" and "False" for "Disabled". Please share the cli output for cross-check, so we will bring this to python-pooldose 0.8.6. We can include #25 then as well in that release.

@lmaertin
Copy link
Owner

if the binary_sensor work for all previous enable/disable sensors, please create a PR so we are faster.

@ronaldvdmeer
Copy link
Contributor Author

ronaldvdmeer commented Mar 15, 2026

I'm making a new PR, give me a couple of minutes

Changing:

circulation_pump_status — Disabled/Enabled
power_on_delay_status — Disable/Enable
flow_delay_status — Disable/Enable

@ronaldvdmeer
Copy link
Contributor Author

ronaldvdmeer commented Mar 15, 2026

done, #28

@ronaldvdmeer
Copy link
Contributor Author

@lmaertin I believe I have updated everything required. Do you need anything else from me?

@lmaertin
Copy link
Owner

I don't think so, I will wrap everything up an bump the HA integration to 0.8.6, then I will go into review with the major PR...

@ronaldvdmeer
Copy link
Contributor Author

Thanks for taking the time to look into this and for maintaining the project. Really appreciate the work you put into it.

@lmaertin
Copy link
Owner

Thanks for your support as well, I like to see how community improves this API now 👍

@ronaldvdmeer
Copy link
Contributor Author

Hi @lmaertin,

Thanks for cleaning up my PR. Especially the alphabetical ordering, renaming the ofa2 keys to _alternative (no numbers in translation keys!), and fixing the alarm_ofa_cl name. I also learned I should let pytest generate the snapshot files instead of writing them by hand 😅

I'll make sure to run --snapshot-update and hassfest before submitting next time. Appreciate the patience!

@lmaertin
Copy link
Owner

Hi @ronaldvdmeer, the per-commit checks and the pipeline guides you for most of the things. As well the AI setup in PR helps.

Overall the HA dev ecosystem is brilliant.

@ronaldvdmeer
Copy link
Contributor Author

Yeah, for me this was the first time working on an integration and seeing how the proces of a PR works with Home Assistant. Learned alot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants