Skip to content

Fix PLT-1 entity discovery, blank notify device, sleeping-device aborts, and Solid LED effect - #20

Open
bharvey88 wants to merge 4 commits into
mainfrom
fix/plt1-entity-discovery-and-led
Open

bharvey88 wants to merge 4 commits into
mainfrom
fix/plt1-entity-discovery-and-led

Conversation

@bharvey88

@bharvey88 bharvey88 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Fixes five customer-visible bugs in the PLT-1 blueprint, four of them pre-existing. Found while investigating a customer report that the new RGB LED never turned on.

1. Five sensor alert groups have never worked

Air Temperature, Air Humidity, Light Intensity, UV Index and Battery alerts could not fire for anyone. The entity lookups searched for ESPHome id: values, but Home Assistant builds entity_ids from the slugified entity name:.

Firmware name Real entity_id fragment Blueprint searched for
Air Temperature air_temperature aht_temperature
Air Humidity air_humidity aht_humidity
LTR390 Light ltr390_light ltr390light
LTR390 UV Index ltr390_uv_index ltr390uvindex
Battery level battery_level batt_pct

Every lookup returned nothing, and each guard is entity != '', so the groups stayed silent even when enabled. All five are disabled by default, which is why this went unreported.

Soil moisture and soil temperature were unaffected because their id and slugified name happen to be identical. The existing wet_voltage lookup already carried a number\..*100_water.* alternative, which is the same bug patched at one site: that entity's id is wet_voltage but its name is 100% Water Voltage.

Verified by running every firmware entity name through Home Assistant's own slugify, then asserting each corrected fragment matches exactly one entity after the ^sensor\. prefilter, and each old fragment matches none. The Air Temperature Offset and Air Humidity Offset entities are in the firmware's number: block, so the prefilter excludes them and there is no collision.

2. Leaving Notify Device blank broke the whole automation

The input defaults to "" and fed twelve device_id: !input notify_device mobile_app device actions. Device actions validate at setup: device_automation/helpers.py raises InvalidDeviceAutomationConfig("Unknown device ''), which is a HomeAssistantError, and automation/config.py catches it and returns a FAILED_ACTIONS stub for the entire automation. So the documented "leave blank to use only the Custom Action" path produced an automation that never ran at all.

Replaced with notify.mobile_app_{{ device_attr(notify_device, 'name') | slugify }}, the standard blueprint pattern. Both the device registry name and the notify target come from the same ATTR_DEVICE_NAME, so the service name reconstructs correctly.

3. A sleeping device aborted the entire run

The two number.set_value calibration writes run first in every execution and had no continue_on_error. ESPHome raises HomeAssistantError when the device is disconnected, and the entity still reports as available, so no condition avoided it. Any window where the device is not connected killed the whole run: no notifications, no LED, no custom action. Confirmed on a customer's PLT-1B, where both writes failed with Authenticated connection not ready yet ... ConnectionState.HOST_RESOLVED.

4. LED Effect "Solid" broke the LED completely

The Solid option's value is the string "None". Service data renders with parse_result=True, so literal_eval("None") produced Python None, and light.turn_on rejected it with string value is None at 'effect'. Rendering the whole data dict as a single template keeps it a string. Both Solid and Slow Pulse now validate against the real light.turn_on schema.

5. min_version was wrong

Declared 2024.6.0, but the file uses triggers:, conditions: and the trigger: item key, which arrived in 2024.10. On 2024.6 through 2024.9 the automation fails schema validation after import. Bumped to 2024.10.0.

Also

Configuring a threshold while the reading was already past it never fired that template trigger, because template triggers arm only if false at setup. Added homeassistant.start and automation_reloaded triggers under a led_sync id that only the LED branches accept, so notification behavior is unchanged. Sync relights only in Stay On Until Watered; it never starts a flash, since automation_reloaded fires on every automation save system-wide and would otherwise blink every PLT-1 whenever the user edited an unrelated automation.

README: removed the Alert Delay (minutes) row and the Never (disable repeat) line, which described inputs removed in a7b4306, documented the Calibration section, and corrected the LED relight claim.

Validation

YAML parses, all 138 templates compile, every !input resolves with none unused, every referenced trigger id exists, line endings unchanged. Entity matching and the light.turn_on payload proven by execution against Home Assistant's own code.

Not yet run on hardware.

🤖 Generated with Claude Code

- Fix five sensor lookups that never matched: HA builds entity ids from
  the slugified entity name, not the ESPHome id, so aht_temperature,
  aht_humidity, ltr390light, ltr390uvindex and batt_pct found nothing
  and those alert groups were dead. Now air_temperature, air_humidity,
  ltr390_light, ltr390_uv_index, battery_level.
- Render the LED turn_on data as one template so the Solid effect's
  'None' stays a string. Rendered per key it became Python None and
  light.turn_on rejected the call with "string value is None".
- Mark the calibration number.set_value writes continue_on_error. A
  deep-sleeping device raises HomeAssistantError on them, which killed
  every repeat run at step one on battery models.
- Replace mobile_app device actions with templated notify service
  calls. A blank Notify Device failed device-action validation at
  setup ("Unknown device ''") and left the whole automation an
  errored stub, breaking the documented custom-action-only path.
- Add led_sync triggers (homeassistant start + automation_reloaded)
  that restore the LED after a restart or reload. Template triggers
  only fire on a false-to-true transition, so a threshold already
  breached at setup never lit the LED. Calibration writes are gated
  off for these runs, and a flash interrupted by a reload is cleared.
- Bump min_version to 2024.10.0; the triggers:/conditions: keys did
  not exist in 2024.6.
- README: add the Calibration section, drop the removed Alert Delay
  and Never-disable-repeat inputs, document Disable Repeat Alerts,
  and correct the LED deep sleep claims.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
automation_reloaded fires on every automation save system-wide, so the
sync branch as written re-flashed the LED whenever the user saved any
unrelated automation, and on every Home Assistant restart.

A flash marks the moment an alert fires, so there is no state for a sync
to restore. Sync now relights only in Stay On Until Watered mode. In
Flash On Alert mode it just clears an LED left on by a flash that a
reload interrupted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a217455c-2112-4ee5-86dd-fc4655f92a4a


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

On a manual run Home Assistant sets trigger to {'platform': None}, so
trigger.id is undefined and every alert branch gated on
trigger.id in [...] evaluated false. Clicking Run could never fire an
alert or light the LED, only write the calibration values. That is how
customers naturally test the blueprint, so it read as the blueprint
being broken.

Each gate now falls back to 'manual', which the alert branches, the LED
clear branch and the LED sync branch accept. A manual run is treated as
a full re-evaluation of current state, and Disable Repeat Alerts no
longer suppresses it. The calibration and repeat gates were already
behaving correctly by accident, through comparisons against an
undefined value; they are explicit now.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@bharvey88

Copy link
Copy Markdown
Contributor Author

Hardware confirmation from a customer trace on a PLT-1B running this branch.

The entity discovery fix works. All five previously dead lookups resolved on a real device:

air_temp_entity:     sensor.apollo_plt_1b_3e480c_air_temperature
air_humidity_entity: sensor.apollo_plt_1b_3e480c_air_humidity
light_entity:        sensor.apollo_plt_1b_3e480c_ltr390_light
uv_entity:           sensor.apollo_plt_1b_3e480c_ltr390_uv_index
battery_entity:      sensor.apollo_plt_1b_3e480c_battery_level

The sleeping-device fix works. Both calibration writes failed with Authenticated connection not ready yet ... ConnectionState.HOST_RESOLVED and continue_on_error let the run continue instead of aborting at step one.

The trace also exposed one more bug, fixed in 9571cd0. It was a manual Run, where Home Assistant sets trigger to {'platform': None}. Every alert branch gated on trigger.id in [...] evaluated false, so a manual run could only write calibration values and never fire an alert or light the LED. Clicking Run is how people test a blueprint, so it looked broken. Gates now fall back to 'manual', which the alert, LED clear and LED sync branches accept.

Note for anyone testing on a battery PLT-1B: the LED can only be lit while the device is awake, so Stay On Until Watered is not usable there. That is the documented behavior, not a regression.

The LED mode guidance keyed off wired versus battery, on the assumption
that a PLT-1B sleeps out of the box. It does not. The prevent_sleep
switch is RESTORE_DEFAULT_ON in Core.yaml, which every variant
including the battery models includes, so a stock PLT-1B stays awake
and Stay On Until Watered works on it.

The rule keys off Prevent Sleep now, which is the switch that actually
decides whether the LED can stay lit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@bharvey88

Copy link
Copy Markdown
Contributor Author

Correction to my note above: I claimed a PLT-1B sleeps out of the box and that Stay On Until Watered is unusable on it. That is wrong. prevent_sleep is RESTORE_DEFAULT_ON in the shared Core.yaml, so both models stay awake unless the owner turns it off, and hold mode works on either.

591f335 rewrites the LED mode guidance in the blueprint description and the README to key off Prevent Sleep instead of the model. The docs page carried the same error and is corrected separately.

The connection error in that trace therefore does not prove the device was sleeping, only that it was not connected at that moment, which a reconnect after a restart or reload explains equally well. The continue_on_error fix is unaffected either way, since it covers any disconnected state.

@bharvey88

Copy link
Copy Markdown
Contributor Author

Hardware validated end to end on a PLT-1B.

Fix Evidence
Five dead sensor lookups Light Intensity, UV Index and Battery alerts all delivered notifications. These groups could not fire for any customer before this PR
Blank/failed notify path rewrite Notifications reach the phone, so `device_attr(notify_device, 'name')
Manual Run gates Run now fires every breached branch. Previously it could only write calibration values
LED Effect Solid LED lights instead of failing cv.string validation
led_sync on reload Saving the automation relights the LED in hold mode, and correctly sends no notifications
Calibration continue_on_error Customer trace: both writes failed with ConnectionState.HOST_RESOLVED and the run continued

Still unverified: blank Notify Device with only a Custom Action.

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.

1 participant