A lightweight background service that detects when your CPAP mask is removed during sleep and alerts you immediately via Pushover. Runs on macOS, Windows, Linux, or a Raspberry Pi Zero 2W.
This tool is not a medical device. It is not intended to diagnose, treat, cure, or monitor any medical condition.
Use of this software is entirely at your own risk. The authors and contributors accept no liability whatsoever for any harm, injury, loss of sleep quality, missed alerts, false alerts, or any other outcome β direct or indirect β arising from the use or misuse of this software.
- Do not rely on this tool as your sole method of CPAP compliance monitoring
- Always consult your physician or sleep specialist regarding your CPAP therapy
- This tool is a personal convenience project shared freely with the community
- By using this software, you agree that you are solely responsible for any consequences
Severe obstructive sleep apnea (OSA) requires consistent CPAP therapy throughout the night. However, many users β especially those with high pressure requirements β tend to unconsciously remove their mask during REM or deep sleep when discomfort peaks. When this happens, therapy stops completely, OSA events return, and sleep quality suffers β often without the user even remembering it happened.
ResMed's MyAir app shows a morning summary, but by then the damage is done. There was no way to get a real-time alert the moment the mask came off.
This project solves that.
A smart plug with energy monitoring sits between your CPAP machine and the wall socket. The monitor polls the plug every 2 minutes (configurable) and reads the current power consumption in watts:
- Mask on, therapy running: varies by machine and pressure setting
- Mask off, machine idle/stopped: low standby wattage
When the monitor detects a sustained low watt reading across multiple consecutive readings, it starts a timer. If the mask stays off long enough, it fires a Pushover push notification to your phone β with priority 2 (emergency), which bypasses Silent mode and Do Not Disturb.
CPAP Machine β Smart Plug β Local WiFi β Monitor (Mac/PC/Pi) β Pushover β Phone/Watch
- CPAP machine β tested with ResMed AirSense 10 and AirSense 11. Should work with any CPAP/APAP/BiPAP machine.
- Smart plug with energy monitoring β see Supported Devices
- A device to run the monitor β Mac, Windows PC, Linux machine, or Raspberry Pi Zero 2W (or any Pi)
- Python 3.10 or later (3.11+ recommended)
- Pushover account β one-time $5 purchase per platform (iOS or Android). Free 30-day trial available.
- Smart plug account (Tapo, Kasa, or Tuya)
- Monitor device and smart plug must be on the same local WiFi network
- Internet connection required for Pushover alerts
| Item | Cost |
|---|---|
| This software | Free |
| Tapo P110 smart plug | ~βΉ1,500β2,000 / ~$18β25 |
| Pushover app (iOS or Android) | $5 one-time |
| Raspberry Pi Zero 2W (optional) | ~βΉ1,500 / ~$15 |
git clone https://github.com/discocracker02/cpap-mask-monitor.git
cd cpap-mask-monitor
bash install.shThe installer will:
- Check Python version
- Install required libraries
- Create
config.jsonfrom the example and open it for you to fill in - Register a background service that starts automatically on boot
- Start the monitor
git clone https://github.com/discocracker02/cpap-mask-monitor.git
cd cpap-mask-monitor
pip install tapo requests
copy config.example.json config.jsonEdit config.json with your credentials, then run:
python cpap_monitor.pyTo run automatically on startup, add a shortcut to cpap_monitor.py in your Windows Startup folder (Win + R β shell:startup).
1. Clone the repo:
git clone https://github.com/discocracker02/cpap-mask-monitor.git
cd cpap-mask-monitor2. Install dependencies:
pip3 install tapo requests3. Create config:
cp config.example.json config.json # Mac/Linux
copy config.example.json config.json # WindowsEdit config.json with your credentials (see Configuration).
4. Test it works:
python3 cpap_monitor.pyYou should see power readings appearing every 2 minutes.
Running the monitor on a Pi Zero 2W is the best long-term setup. It runs silently 24/7, uses almost no power (~0.5W), and doesn't tie up your laptop or Mac. Any Raspberry Pi model will work.
- Raspberry Pi Zero 2W (or any Pi model)
- MicroSD card (16GB or larger)
- Micro USB power adapter (Pi Zero 2W) or USB-C (Pi 4/5)
- Micro USB OTG adapter (for Pi Zero 2W, to connect peripherals)
- A computer to set it up from
- Download and install Raspberry Pi Imager
- Insert your microSD card
- Open Raspberry Pi Imager
- Choose Raspberry Pi Zero 2W as the device
- Choose Raspberry Pi OS Lite (64-bit) β no desktop needed
- Click the settings gear icon and configure:
- Set hostname:
snoozesense(or any name you like) - Enable SSH
- Set username:
pi(or your preferred name) - Set password
- Configure WiFi with your network name and password
- Set hostname:
- Flash the card and insert it into the Pi
Power on the Pi. Wait 2 minutes, then SSH in from your computer:
ssh pi@snoozesense.localIf that doesn't work, find the Pi's IP in your router's connected devices list and use:
ssh pi@192.168.x.xOpen your router admin page and find the DHCP reservation section. Reserve a fixed IP for your Pi using its MAC address. This ensures the Pi always gets the same IP address so nothing breaks after a reboot.
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-venv gitCreate a virtual environment:
cd ~
python3 -m venv cpap-env
source cpap-env/bin/activateClone the repo and install:
git clone https://github.com/discocracker02/cpap-mask-monitor.git
cd cpap-mask-monitor
pip install tapo requestscp config.example.json config.json
nano config.jsonFill in your Tapo credentials, plug IP, and Pushover keys. See Configuration for all options.
Save with Ctrl+X β Y β Enter.
python3 cpap_monitor.pyYou should see power readings every 2 minutes. Press Ctrl+C to stop.
Create the service file:
sudo nano /etc/systemd/system/cpap-monitor.servicePaste this (replace pi with your username if different):
[Unit]
Description=CPAP Mask Monitor
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/cpap-mask-monitor
ExecStart=/home/pi/cpap-env/bin/python3 /home/pi/cpap-mask-monitor/cpap_monitor.py
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
Save with Ctrl+X β Y β Enter.
Enable and start:
sudo systemctl enable cpap-monitor
sudo systemctl start cpap-monitor
sudo systemctl status cpap-monitorYou should see Active: active (running). The monitor will now start automatically every time the Pi boots.
Check the log:
cat ~/cpap-mask-monitor/cpap_monitor.logYou should see timestamped power readings. You're done β the Pi will run silently overnight, every night.
# Check service status
sudo systemctl status cpap-monitor
# View live log
tail -f ~/cpap-mask-monitor/cpap_monitor.log
# Restart service
sudo systemctl restart cpap-monitor
# Safely shut down Pi before unplugging
sudo shutdown nowCopy config.example.json to config.json and fill in your details:
{
"plug_type": "tapo",
"tapo": {
"email": "your_tapo_account_email@example.com",
"password": "your_tapo_account_password",
"ip": "192.168.x.x"
},
"pushover": {
"user_key": "your_pushover_user_key",
"app_token": "your_pushover_app_token"
},
"monitor": {
"poll_seconds": 120,
"idle_minutes": 15,
"watt_threshold": 3,
"confirm_count": 5,
"sleep_start_hour": 22,
"sleep_end_hour": 9,
"timezone_offset_hours": 5.5
}
}| Key | Recommended | Description |
|---|---|---|
tapo.email |
β | Your TP-Link/Tapo account email |
tapo.password |
β | Your TP-Link/Tapo account password |
tapo.ip |
β | Local IP of your Tapo plug |
pushover.user_key |
β | Your Pushover user key |
pushover.app_token |
β | Your Pushover app token |
poll_seconds |
120 | How often to poll the plug (seconds) |
idle_minutes |
15 | How long mask must be off before alert fires |
watt_threshold |
3 | Watts below which mask is considered off |
confirm_count |
5 | Consecutive low readings before timer starts |
sleep_start_hour |
22 | Hour monitoring starts (24h format) |
sleep_end_hour |
9 | Hour monitoring stops (24h format) |
timezone_offset_hours |
5.5 | Your UTC offset (India = 5.5, US EST = -5) |
Important: The recommended values above (
watt_threshold: 3,confirm_count: 5) are based on testing with a ResMed AirSense 11 AutoSet. Your machine may draw different power levels depending on the model, pressure settings, and humidifier use. Always calibrate for your own machine.
How to calibrate:
- Run
python3 cpap_monitor.pyand watch the log - Note the watts while wearing the mask with therapy running β this is your active range
- Note the watts when the machine is on standby (mask off, machine idle) β this is your idle value
- Set
watt_thresholdto a value between idle and the lowest active reading - Set
confirm_counthigher if you get false alerts β this requires more consecutive low readings before confirming mask-off
Example β ResMed AirSense 11 AutoSet:
- Active therapy: 3β7W (varies with pressure)
- Standby / idle: 2W
- Recommended threshold: 3W
- Recommended confirm_count: 5 (10 minutes of polling before confirming)
Example β ResMed AirSense 10 AutoSet:
- Active therapy: 6β15W
- Standby / idle: 2β3W
- Recommended threshold: 5W
- Recommended confirm_count: 2β3
Tip: Use your smart plug's own app (Tapo app, Kasa app etc.) to watch live power readings for 10β15 minutes while using your CPAP. This gives you a clear picture of your machine's power profile before setting any thresholds.
Open the Tapo app β tap your plug β tap the settings icon β Device Info β IP Address.
Tip: Reserve a static IP for your plug in your router's DHCP settings so the IP never changes.
Pushover is the notification service that sends alerts to your phone and Apple Watch or Wear OS watch. It costs $5 once and works on both iOS and Android.
- Go to pushover.net and create a free account
- On your dashboard, note your User Key β you'll need this for
config.json - Click Create an Application/API Token
- Give it a name (e.g. "CPAP Monitor") and click Create
- Note the API Token β you'll need this too
- Download Pushover from the App Store
- Log in with your pushover.net account
- Open Settings inside the Pushover app
- Tap Notification Settings
- Enable Critical Alerts β this is essential. It bypasses Silent mode and all Focus modes including Sleep Focus
- If you have an Apple Watch, the Pushover app installs automatically on the watch via the Watch app
- On your Apple Watch, open the Watch app on iPhone β scroll to Pushover β enable Show App on Apple Watch
- Critical alerts will now appear on both your iPhone and Apple Watch even when your phone is silenced
- Download Pushover from the Google Play Store
- Log in with your pushover.net account
- Open the app β Settings β Notification Channels
- Enable the Emergency channel β this allows alerts to bypass Do Not Disturb
- If you have a Wear OS watch (Galaxy Watch, Pixel Watch etc.), notifications mirror automatically from your phone
- To ensure watch alerts work: on your phone go to Settings β Notifications β find Pushover β enable "Allow notification mirroring" or similar (varies by Android version)
Run this command from your terminal (replace with your actual keys):
curl -s \
--form-string "token=YOUR_APP_TOKEN" \
--form-string "user=YOUR_USER_KEY" \
--form-string "title=CPAP Monitor Test" \
--form-string "message=Test alert β your setup is working!" \
--form-string "priority=2" \
--form-string "retry=60" \
--form-string "expire=300" \
https://api.pushover.net/1/messages.jsonYou should receive a loud alert on your phone and watch within a few seconds.
| Device | Status |
|---|---|
| TP-Link Tapo P110 | β Tested |
| ResMed AirSense 10 AutoSet | β Tested |
| ResMed AirSense 11 AutoSet | β Tested |
| Device | Notes |
|---|---|
| Tapo P110M | Same chip, energy monitoring |
| Tapo P115 | Energy monitoring variant |
| Tapo P105 | No energy monitoring β will not work |
| Tapo P100 | No energy monitoring β will not work |
| Kasa KP115 | Energy monitoring β should work |
| Kasa EP25 | Energy monitoring β should work |
| Tuya-compatible plugs | Requires local key setup via tinytuya |
| Any CPAP/APAP/BiPAP machine | Watt threshold calibration required |
Important: Only smart plugs with energy monitoring (live power reading in watts) are compatible. Always check your plug's spec sheet before buying.
Your watt_threshold may be too high or confirm_count too low. This is the most common issue with AutoSet machines that dynamically adjust pressure β power draw fluctuates even with the mask on.
Fix:
- Watch the log for a full night and note minimum watts during therapy
- Lower
watt_thresholdto just above standby level - Raise
confirm_countto 4 or 5 to require more consecutive low readings - Raise
idle_minutesto 15 to require longer sustained low power before alerting
- Make sure Pushover Critical Alerts is enabled (see Pushover Setup above)
- Verify your User Key and App Token in config.json
- Check internet connection on the monitor device
This is a known issue with newer Tapo firmware. Fixes to try:
- Power cycle the plug
- Restart the monitor service
- Factory reset the plug and re-add to Tapo app
- Verify the IP in config.json matches your plug's current IP
- Make sure monitor device and plug are on the same WiFi network
- Reserve a static IP for the plug in your router
- Check if IP changed β look in your router's connected devices list
- Use
ssh pi@snoozesense.localas a fallback (uses hostname instead of IP) - Reserve a static IP in your router to prevent this permanently
| Limitation | Details |
|---|---|
| Same network required | Monitor device and smart plug must be on the same local WiFi. Does not work while travelling unless you bring the plug and connect to a shared hotspot |
| Internet required for alerts | Pushover needs internet. If internet is down, the monitor logs the event locally but cannot send push notifications |
| Smart plug required | No smart plug = no monitoring. The plug is the only required hardware addition |
| 2-minute detection delay | At default settings: up to 10 minutes to confirm mask off + 15 minute idle timer = up to 25 minutes before first alert. Reduce poll_seconds to detect faster at the cost of more network traffic |
- macOS support
- Linux / Raspberry Pi support (systemd service)
- Windows auto-start service
- Additional smart plugs β Sonoff S31, additional Kasa models
- Additional alert providers β Telegram bot, ntfy.sh, Home Assistant webhook
- Web dashboard β nightly summary, mask-off history, trend graphs
- Multi-machine support β for households with multiple CPAP users
- Auto-calibration β automatically determine watt threshold from first-night data
Community contributions welcome. If you've tested with a device not listed above, please open an issue or PR.
Pull requests are welcome. For major changes, please open an issue first to discuss what you'd like to change.
MIT β free to use, modify, and distribute. See LICENSE for details.
See README_PLUGS.md for the full list of supported plugs including Tapo, Kasa, Wipro, and Havells.