How to check if someone is in bath?

I think what you are after is a hygrostat. There are 2 in HACS.


1 Like

Looks like https://github.com/basschipper/homeassistant-generic-hygrostat is due to be merged into Home Assistant:

https://github.com/home-assistant/core/pull/36759

Meaning a simple condition in your automation wont work?
Add a condition on the “lights off after x time” automation that the humidity value should be <60?

If during summer humidity goes over 60, maybe you could use something like this to create an average daily humidity sensor

sensor:
  - platform: average
    name: 'Average humidity'
    duration:
      days: 1
    entities:
      - sensor.whatever_your_humidity_sensor

Then have a condition on your lights off automation to only fire when:
current humidity < (average humidity + 10%* average humidity)

That’s what i can think of,
Let us know if i understood correctly

1 Like

Or hardware solution … attach a small temp sensor to the outside of the bath… if significantly above ambient temp the bath tub is being used ? Could also attach a strain (weight) sensor under a leg of the bath.

Or you could use this:

You can set up a “rate of change” value and turn on that binary sensor if the humidity goes up too fast to be from environmental sources.

1 Like

I run our master bathroom fan based on this. Have an Aeotec sensor in there and use this AVERAGE sensor to calculate the average humidity in the room:

It’s available via HACS as ‘Average Sensor’ [GitHub - Limych/ha-average: Average Sensor for Home Assistant]

Personally I then use automations to turn on the fan when the current humidity is 9% greater than average and off when it’s back below that threshold.

Hope it helps!

A lot of options over there
Will try out trend and average first, will see how good they are

Thanks!

Here’s a quick 60 hour chart of our master bath. Gray is average, blue is actual.

image

2 Likes

The hygrostat is, believe me, the easiest.

You cannot use a fixed level to trigger at because humidity is relative. “Background” humidity will be higher on some days and you only want to measure the increase or change.

Trend should work, but hygrostat is designed for this.

1 Like

Got it. Testing trend first
By the way, should not derivatives also solve this problems?
It would show how fast humidity is changing, if I am not mistaken

P.S. I don’t really want to use custom component if default once can handle this task. That’s why I’m starting with trend

For humidity, look at the rate-of-rise or slope of the line ( derivative of FFT of data points) to get an accurate detection.

If I were to design something to check bath usage, I would use 3 temp probes and some logic. The probes would be located:

1 in the drain, 1 at the source (handle), 1 at the top of the exit (before shower nozzle)

That would allow detection if you are showering or using the bath (if you have a shower), thought it would be a lot of work since temp probes need to be placed on copper pipes

steady high temp at the top and drain = shower
constant high temp at source, then drop in drain, then drop at source = bath filled
then…
increase temp in drain then dropoff = bath drained

You could also use a microphone to detect the sound of a bath filling or shower running, they have different acoustic profiles, volume levels in specific frequency bands are pretty obvious. My bath is adjacent to another room, an an acoustic probe placed on that rooms wall would probably work, keeping the hardware out of the bathroom and away from humidity.

Temp + acoustic + humidity triggers would make a bulletproof detection system

True, I think when I first looked at this HA did not have a derivative sensor.

If the generic hygrostats do not meet your requirements. Just use the generic_thermostat. I’ve used that for ages for my hygrostats, since there was not really any alternatives. Only you have to live with pretending your humidity is temperature.

Here is how I handle the light in bathroom using humidity and motion


id: '1596963203067'
  alias: Bathroom light ON [ON MOTION]
  trigger:
  - platform: state
    entity_id: binary_sensor.motion_2, binary_sensor.presence_1
    to: 'on'
  - platform: device
    type: turned_on
    device_id: d2296767263e4c6f84f7538991381d85
    entity_id: light.bathroom
    domain: light
  action:
  - service: light.turn_on
    entity_id: light.bathroom
  mode: single
- id: '1596963203063'
  alias: Bathroom light OFF [MOTION 10 min]
  trigger:
  - entity_id: binary_sensor.motion_2, binary_sensor.presence_1
    for: 0:05:00
    platform: state
    to: 'off'
  condition:
  - condition: device
    type: is_on
    device_id: d2296767263e4c6f84f7538991381d85
    entity_id: light.bathroom
    domain: light
    for:
      hours: 0
      minutes: 0
      seconds: 0
  action:
  - wait_template: '{{float(states(''sensor.th_7_humidity'')) < float(80)}}'
    continue_on_timeout: false
  - type: turn_off
    device_id: d2296767263e4c6f84f7538991381d85
    entity_id: light.bathroom
    domain: light
  mode: single

I did a little differently.
The fan in my bathroom is connected via Sonoff TH10 with AM2301 (temperature + humidity) sensor. ESPHome-based firmware is uploaded inside it.
The system can autonomously maintain the set indoor humidity (default: 75%). In this case, the states of the sensors are broadcast to HA and through MQTT you can reconfigure the fan response threshold.
You can also temporary change the threshold for a specified time. At the end of which, the system will return to the previous state.
The second option: by pressing a button on the TH10, the fan is forced to start for 5 minutes (also works without the participation of a smart home).

The general logic is simple: remove as much primitive work from the smart home as possible. At the same time, leaving it in full control of the situation.

Regarding the main topic …
IMHO, to accurately determine the presence of people, you will need two sensors: a door opening sensor + a PIR sensor.
Let us take as X time how long the PIR sensor waits before canceling the “in motion” state.
The logic is this:

  • The door is open - the light is always on;
  • If the door is closed more than X …
    • if the PIR sensor is ON (indicates the presence of movement), then there are people in the room. We leave this state unchanged until the door is opened.
    • if the PIR sensor is OFF (indicates there is no movement), then there are no people in the room and you can turn off the light.

I already use motion + door sensor
And I have that logic
But here is the case:
You leave bathroom and immediately next person gets in. Motion is not longer detected but person is in bath, Home Assistant thinks that person left (first person did) and turns off the light while you are in bath
that’s a rear case, that’s why I am ok for more than a year now with that logic, but it can be better

If there are failures, then you have some kind of violation of logic.

Or the PIR sensor is not scanning the entire room. Or the state of the PIR sensor is not checked after the X timeout after the door is closed.

If a person left and closed the door, after time X the PIR sensor will turn off. If a new person came next, he would move at least for a few seconds. This means that in time X after the door is closed, the PIR sensor will still be turned on.

Motion sensor just can not see a person in bath

Hi, If its of any use and you don’t mind using node red I created a function for detecting the rate of rise of temperature but you could adapt it for your use. The function just really compares two values it receives in succession and calculates the difference. So for the actual rate of rise you would need to send the temp/humidity values at an interval of 1 min or more.

Node Red Flow
Screenshot 2020-11-21 at 22.17.01

[{"id":"e6994f2d.a8f94","type":"function","z":"4b74def4.25059","name":"compare","func":"// See Setup TAB for where the variables are initialised.\n//  Function to compare 2 values to send a payload based on either\n// rate of rise trigger or Max Temperature reached\n\n\n\nvar previoustemp = context.get('previoustemp'); //previous temperature value\nvar currenttemp =  context.get('currenttemp'); // current temperature value\nvar payloadtemp = msg.payload; // current temperature value\n\n// Change the RoR value for the Rate of Rise trigger \nvar RoR = 7.5;\n// Change the MAX Temperature Value \nvar maxtemp = 60;\n\n\ncontext.set('currenttemp',payloadtemp); //set the current temperature to temp2 variable\ncurrenttemp=context.get('currenttemp');\n\n//Temperature difference between 2 values\ntempdif=currenttemp-previoustemp; \n\n// set the message payload based on conditions\nif ((previoustemp == -300) && (maxtemp > payloadtemp)) {msg.payload=\"Previous temp is null can't give rate of rise yet until i receive another value\"}\nelse if (maxtemp < payloadtemp) {msg.payload={\"FireDetected\":\"true\", \"MaxTemp\":payloadtemp}}\nelse if (tempdif > RoR) {msg.payload={\"FireDetected\":\"true\", \"RateofRise\":tempdif };}\nelse if (previoustemp !== -300) {msg.payload={\"FireDetected\":\"false\", \"RateofRise\":tempdif};}\n\n// set previous temperature to the value of current temperature\ncontext.set('previoustemp',currenttemp);\n\n// Return the new message\nreturn msg;\n\n\n\n\n\n\n\n\n// if (temp3>6.7) {\n//  msg.payload= \"on \" + count + temp1 +temp2;\n//  return msg.payload;\n//}  else {\n//  return null\n//}\n","outputs":1,"noerr":0,"initialize":"//Initialise the variables \nvar previoustemp = context.get('previoustemp') || -300; //previous temperature value\nvar currenttemp =  context.get('currenttemp') || -300; // current temperature value\nvar tempdif =null;\ncontext.set('previoustemp',-300);","finalize":"","x":300,"y":640,"wires":[["f2b59f04.059708"]]},{"id":"d3f359ba.7105e8","type":"inject","z":"4b74def4.25059","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":130,"y":620,"wires":[["e6994f2d.a8f94"]]},{"id":"7f6a7349.ff7714","type":"inject","z":"4b74def4.25059","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":130,"y":660,"wires":[["e6994f2d.a8f94"]]},{"id":"ed8d44b7.85f87","type":"inject","z":"4b74def4.25059","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"20","payloadType":"num","x":130,"y":700,"wires":[["e6994f2d.a8f94"]]},{"id":"f2b59f04.059708","type":"debug","z":"4b74def4.25059","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":480,"y":640,"wires":[]},{"id":"e13159fc.392a88","type":"inject","z":"4b74def4.25059","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":130,"y":580,"wires":[["e6994f2d.a8f94"]]},{"id":"5cc2a0c5.2b13","type":"inject","z":"4b74def4.25059","name":"Max Temp 61","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"61","payloadType":"num","x":150,"y":740,"wires":[["e6994f2d.a8f94"]]}]