Skip to content

Add leak sensor and temperature/humidity sensor support - #342

Open
crazytimmy wants to merge 4 commits into
SecKatie:mainfrom
crazytimmy:main
Open

crazytimmy wants to merge 4 commits into
SecKatie:mainfrom
crazytimmy:main

Conversation

@crazytimmy

@crazytimmy crazytimmy commented Sep 7, 2026

Copy link
Copy Markdown

Adds support for two Wyze Sense v2 device types that were previously either dropped (LeakSensor) or unrecognized (TemperatureHumidity, mapped to UNKNOWN):

DeviceTypes.LEAK_SENSOR was already a defined enum value and included in get_object_list()'s typing, but SensorService.get_sensors() never included it in its filter, so leak sensors were silently excluded.
DeviceTypes.TEMPERATURE_HUMIDITY didn't exist at all — TH3U devices were falling through to DeviceTypes.UNKNOWN.

Both new types read their state directly from device_params (ws_detect_state for leak, th_sensor_temperature/th_sensor_humidity for temp/humidity) rather than the P-code property_list endpoint, since that's where Wyze's API actually returns this data for these device types — confirmed against live API responses.

Also fixes a pre-existing test fixture bug: test_sensor_service.py's Sensor fixtures used a "device_type" key instead of "product_type", which the Device.type property actually reads. This went unnoticed because no prior test path called .type on these fixtures — my new update() branches are the first to do so.

Tested against a physical Wyze Sense Hub with real WS3U and TH3U devices, plus new unit tests covering both device types.

A companion PR to ha-wyzeapi (adding the corresponding HA entities) depends on this: SecKatie/ha-wyzeapi#929
This could also be linked to SecKatie/ha-wyzeapi#899 as it helps solve this issue.

Note: I had help from Claude in writing this, so up to you if you want to accept it or not.

@crazytimmy
crazytimmy requested a review from SecKatie as a code owner September 7, 2026 22:39
@CLAassistant

CLAassistant commented Sep 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54d9ac78a5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +34 to +35
sensor.detected = sensor.device_params.get("ws_detect_state") == 1
return sensor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh leak state before returning it

When a leak starts or stops within 1,200 seconds of the previous object-list refresh, get_updated_params() returns the cached BaseService._devices parameters, and this new early return skips the live property request. Consequently, normal polls and callbacks can report the previous wet/dry state for up to 20 minutes; temperature and humidity values have the same staleness problem. These parameter-only sensors need a suitably fresh API source or explicit cache invalidation.

Useful? React with 👍 / 👎.

Comment on lines +37 to +38
if sensor.type is DeviceTypes.TEMPERATURE_HUMIDITY:
return sensor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Throttle parameter-only sensor update callbacks

When a leak or temperature/humidity sensor is registered through register_for_updates(), after the initial refresh this branch completes from the cache without network latency. Since update_worker() runs while True with no delay, it then schedules updates and invokes the subscriber callback continuously, potentially consuming a CPU core and flooding the event loop until the sensor is deregistered. Add explicit polling throttling for these early-return paths.

Useful? React with 👍 / 👎.

…orker pacing

CameraService.update() was calling _get_event_list() independently for
every camera on every update cycle, even though the event list is
account-wide, not camera-specific. With N cameras, this meant N
identical API calls per pass instead of one. update() now accepts an
optional pre-fetched event list, and update_worker fetches it once per
pass and shares it across all cameras.

update_worker also had no pacing between full passes when subscribers
were present (only when the subscriber list was empty). Adds the same
sleep-based pacing used in SensorService as a backstop.

This is the second instance of the same rate-limiting pattern found
while testing SecKatie#342 - see the discussion there for the sensor-side fix
and the 429s this was causing account-wide.
The 1200-second cache interval was overly conservative once combined
with the rate-limit fixes in sensor_service.py and camera_service.py.
60 seconds gives much better responsiveness for door/leak/motion
sensors while still meaningfully reducing API call volume versus the
original per-device polling this replaced.

Note: this constant is shared by every BaseService subclass, not just
SensorService and CameraService. Only those two have been verified to
have safe update_worker pacing as of this PR - other services
(LockService, ThermostatService, etc.) haven't been audited for
similar unpaced polling loops, so this value is intentionally
conservative rather than tuned purely for sensor responsiveness.
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