├── images ├── NodeMCU.jpg ├── Wiring.jpg ├── ESPMount.jpg ├── Schematic.png └── ScreenshotHomeAssistant.jpg ├── kicad ├── Z97 Extreme4 - USB 2.0 Header.png ├── HomeAssistantNASPowerHomeAssistant.png ├── Z97 Extreme4 - System Panel Header.png └── ESP8266-ESPHome-PC-Power-HomeAssistant.pro ├── .gitignore ├── secrets.yaml ├── LICENSE ├── pc-power.yaml ├── scripts └── nas-power.py └── README.md /images/NodeMCU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/images/NodeMCU.jpg -------------------------------------------------------------------------------- /images/Wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/images/Wiring.jpg -------------------------------------------------------------------------------- /images/ESPMount.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/images/ESPMount.jpg -------------------------------------------------------------------------------- /images/Schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/images/Schematic.png -------------------------------------------------------------------------------- /images/ScreenshotHomeAssistant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/images/ScreenshotHomeAssistant.jpg -------------------------------------------------------------------------------- /kicad/Z97 Extreme4 - USB 2.0 Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/kicad/Z97 Extreme4 - USB 2.0 Header.png -------------------------------------------------------------------------------- /kicad/HomeAssistantNASPowerHomeAssistant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/kicad/HomeAssistantNASPowerHomeAssistant.png -------------------------------------------------------------------------------- /kicad/Z97 Extreme4 - System Panel Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Erriez/ESPHomePCPowerControlHomeAssistant/HEAD/kicad/Z97 Extreme4 - System Panel Header.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore settings for ESPHome 2 | # This is an example and may include too much for your use-case. 3 | # You can modify this file to suit your needs. 4 | /.esphome/ 5 | /secrets.yaml 6 | -------------------------------------------------------------------------------- /secrets.yaml: -------------------------------------------------------------------------------- 1 | esphome_api_password: "changeme" 2 | esphome_ota_password: "changeme" 3 | 4 | esphome_ap_password: "changeme" 5 | 6 | esphome_wifi_ssid: "ssid" 7 | esphome_wifi_password: "password" 8 | 9 | -------------------------------------------------------------------------------- /kicad/ESP8266-ESPHome-PC-Power-HomeAssistant.pro: -------------------------------------------------------------------------------- 1 | update=22/05/2015 07:44:53 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2025 Erriez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pc-power.yaml: -------------------------------------------------------------------------------- 1 | esphome: 2 | name: pc-power 3 | 4 | # ESP8266 board 5 | esp8266: 6 | board: nodemcuv2 7 | 8 | # Or: ESP32 board (comment out esp8266) 9 | # esp32: 10 | # lolin_d32 11 | 12 | # WiFi connection to base station 13 | wifi: 14 | ssid: !secret esphome_wifi_ssid 15 | password: !secret esphome_wifi_password 16 | 17 | # Enable fallback hotspot (captive portal) in case wifi connection fails 18 | ap: 19 | ssid: "PC Power Fallback Hotspot" 20 | password: !secret esphome_ap_password 21 | 22 | # captive_portal: 23 | 24 | # Enable logging 25 | logger: 26 | 27 | # Enable Home Assistant API 28 | api: 29 | password: !secret esphome_api_password 30 | 31 | # Enable OTA update via WiFi 32 | ota: 33 | - platform: esphome 34 | password: !secret esphome_ota_password 35 | 36 | switch: 37 | - platform: gpio 38 | id: power_short_press 39 | name: "PC Power Toggle" 40 | icon: "mdi:electric-switch" 41 | pin: 42 | number: D2 # Power button output pin 43 | inverted: false 44 | allow_other_uses: true 45 | mode: 46 | output: true 47 | on_turn_on: 48 | - delay: 150ms 49 | - switch.turn_off: power_short_press 50 | - platform: gpio 51 | id: power_long_press 52 | name: "PC HARD POWER OFF" 53 | icon: "mdi:electric-switch" 54 | pin: 55 | number: D2 # Power button output pin 56 | inverted: false 57 | allow_other_uses: true 58 | mode: 59 | output: true 60 | on_turn_on: 61 | - delay: 3500ms 62 | - switch.turn_off: power_long_press 63 | 64 | binary_sensor: 65 | - platform: gpio 66 | name: "PC Power State" 67 | device_class: power 68 | pin: 69 | number: D1 # Power detect input pin (readback from Reset button) 70 | inverted: false 71 | mode: 72 | input: true 73 | -------------------------------------------------------------------------------- /scripts/nas-power.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2020-2025 Erriez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | """ 26 | Example Python script to send a button press or read power status. 27 | """ 28 | 29 | import json 30 | import requests 31 | import time 32 | 33 | 34 | # https://developers.home-assistant.io/docs/api/rest/ 35 | HA_SERVER = 'http://yourserver:8123' 36 | HA_TOKEN = 'YOUR_TOKEN' 37 | VERBOSE = True 38 | 39 | 40 | def print_json(json_obj): 41 | print(json.dumps(json_obj, indent=2)) 42 | 43 | 44 | def ha_get(endpoint): 45 | url = '{}/{}'.format(HA_SERVER, endpoint) 46 | headers = { 47 | 'Authorization': 'Bearer {}'.format(HA_TOKEN), 48 | 'content-type': 'application/json', 49 | } 50 | 51 | if VERBOSE: 52 | print('GET: {}'.format(url)) 53 | print(headers) 54 | 55 | return requests.get(url, headers=headers) 56 | 57 | 58 | def ha_post(endpoint, data): 59 | url = '{}/{}'.format(HA_SERVER, endpoint) 60 | headers = { 61 | 'Authorization': 'Bearer {}'.format(HA_TOKEN), 62 | 'content-type': 'application/json', 63 | } 64 | 65 | if VERBOSE: 66 | print('POST: {}'.format(url)) 67 | print(headers) 68 | print(data) 69 | 70 | return requests.post(url, headers=headers, data=data) 71 | 72 | 73 | # ------------------------------------------------------------------------------------ 74 | def ha_get_config(): 75 | return json.loads(ha_get('api/config').text) 76 | 77 | 78 | def ha_get_state(entity_id): 79 | r = ha_get('api/states/{}'.format(entity_id)) 80 | r_text = json.loads(r.text) 81 | if VERBOSE: 82 | print('Received code: {}'.format(r.status_code)) 83 | print_json(r_text) 84 | return r_text.get('state') 85 | 86 | 87 | def ha_post_state(entity_id, state): 88 | return ha_post('api/states/{}'.format(entity_id), '{ "state": "%s" }' % state) 89 | 90 | 91 | def ha_post_switch(entity_id): 92 | return ha_post('api/services/switch/turn_on', '{ "entity_id": "%s" }' % entity_id) 93 | 94 | 95 | # ------------------------------------------------------------------------------------ 96 | def post_nas_power_button_press(): 97 | return ha_post_switch('switch.nas_power_button') 98 | 99 | 100 | def get_nas_power_state(): 101 | return ha_get_state('binary_sensor.nas_power_sense') 102 | 103 | 104 | # ------------------------------------------------------------------------------------ 105 | def post_bell_button_press(): 106 | return ha_post_switch('switch.bell') 107 | 108 | 109 | def get_bell_state(): 110 | return ha_post_switch('switch.bell') 111 | 112 | 113 | # ------------------------------------------------------------------------------------ 114 | if __name__ == '__main__': 115 | print_json(ha_get_config()) 116 | 117 | #print(post_bell_button_press()) 118 | 119 | print(post_nas_power_button_press()) 120 | time.sleep(1) 121 | print('NAS power state: {}'.format(get_nas_power_state())) 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPHome PC Power Control via Home Assistant 2 | 3 | This project contains remote PC power control via HomeAssistant and ESPHome ESP8266/ESP32 boards. 4 | 5 | I use this project to power my PC based NAS remotely on and off via Home Assistant as alternative to Wake-On-Lan (WOL) which has known limitations. 6 | 7 | ## Features 8 | 9 | * Control physical PC power button (front panel) connected to the motherboard: 10 | * Turn PC power on from any power state such as `power-off/suspended/hybernate` (Behavior short press power button). 11 | * Graceful `shutdown/suspend/hybernate` of the operating system (Behavior short press power button). 12 | * Force power-off (Behavior long press power-button). 13 | * Read PC power on/off status from motherboard. 14 | * No operating system dependency. 15 | * Power/reset buttons front panel remains full functional. 16 | * User authorization via Homeassistant. 17 | * Use with any (micro/mini)-ATX computer motherboard. 18 | 19 | Youtube video: 20 | 21 | [![Youtube video](https://img.youtube.com/vi/rAcvqaPf830/0.jpg)](https://www.youtube.com/watch?v=rAcvqaPf830) 22 | 23 | Homeassistant screenshot: 24 | 25 | ![Screenshot Home Assistant](images/ScreenshotHomeAssistant.jpg) 26 | 27 | ## Differences Wake-On-Lan (WOL) magic packet 28 | 29 | [Wake-On-Lan (WOL)](https://en.wikipedia.org/wiki/Wake-on-LAN) is intended to turn computer power on remotely via Ethernet by sending a so called `magic packet`. This has several known limitations which are not applicable with this ESP project: 30 | 31 | * Wake-On-Lan (WOL) has **no authorization**! The WOL protocol has no functionality to provide a password or allow a specific computer to wake from. Any device on the same LAN can wake a PC by sending a magic packet. The ESP is password protected via Homeassistant. 32 | * Wake-On-Lan (WOL) can only `wake` a PC from suspend or power-off state. There is no shutdown/suspend functionality. 33 | * To put a Linux machine in `sleep` state, a SSH connection to the remote PC can be made for example by executing `pm-suspend` command (Install via `sudo apt install pm-utils`), reference [Ubuntu pm-action documentation](https://manpages.ubuntu.com/manpages/jammy/man8/pm-action.8.html). Disadvantages: 34 | * Requires SSH login. 35 | * Requires sudo rights. 36 | * Wake-On-Lan magic packets are ignored when connecting main power the first time to the power supply, even when WOL is activated in the BIOS. The reason is that WOL is disabled on most computers at first power on and requires activation by a running operating system before the PC responds on a magic packet to wake the computer. Reference: [Ubuntu Wake-On-Lan](https://help.ubuntu.com/community/WakeOnLan). 37 | * Running Homeassistant [Wake-On-Lan](https://www.home-assistant.io/integrations/wake_on_lan) in a Docker container cannot forward broadcast magic packets with a bridged network configuration. It requires an external Docker container as bridge to forward magic packages and is beyond the scope of Homeassistant. This is currently not included in Homeassistant documentation. 38 | 39 | ## Hardware 40 | 41 | The hardware consists of an ESP8266 or ESP32 and two IO pins, mounted on a PCI metal plate (see picture below). In my case I used an ESP8266 NodeMCU board. Theoretically any ESP8266 or ESP32 board can be used for this project. 42 | 43 | * One pin controls the power button by generating a long or short press 44 | * A second pin reads the motherboard power status. 45 | 46 | ![Hardware](images/NodeMCU.jpg) 47 | 48 | ## Schematic 49 | 50 | My NAS is based on an Intel Core I7 ASRock Z97 ATX motherboard and contains standard 2.5mm 2x5 male headers: 51 | 52 | ![Schematic](images/Schematic.png) 53 | 54 | **ATX header USB 2.0:** 55 | 56 | Header `J1` pin 1 constant +5V power on pin 1, even when the PC is power-off and is used to power the NodeMCU. The ESP8266 or ESP32 is powered via an on-board 3V3 regulator. 57 | 58 | **ATX header System panel:** 59 | 60 | Header `J2` contains the power, reset and GND pins: 61 | 62 | * Power button pin 6. 63 | * Short press: Turn PC on or generate graceful shutdown. 64 | * Long press: Generate hard power off. 65 | * Reset button pin 7. 66 | * Used to read power status via the reset button pin: 67 | * High: power-on 68 | * Low: power-off 69 | * GND pin 5. 70 | 71 | * Pin `D1` is used to read the power status from the reset pin: High is on, Low is off. Resistor `R1` is used to minimize current when the IO pin is accidentally set to output. 72 | * Pin `D2` is used to pull the power button low to generate a short or long press. Transistor `Q1` is used for secure isolation between ESP8266 and motherboard. 73 | 74 | Warning: All ATX and ESP pins must be operating at 3V3. 75 | 76 | ## Downloads 77 | 78 | * [KiCad schematic .SCH](kicad/ESP8266-ESPHome-PC-Power-HomeAssistant.pro) 79 | 80 | ## Wiring 81 | 82 | ![Wiring](images/Wiring.jpg) 83 | 84 | A DIY breakout PCB can be mounted at the system panel header to connect power button `SW1` and reset button `SW2`. 85 | 86 | ## WiFi stability 87 | 88 | There is sufficient space in an ATX computer case to mount the ESP board. However, the computer case is metal shielded, so the WiFi distance to the base station reduces. It is recommended to place the ESP outside the computer case when the WiFi connection is unstable or distance too low. An ESP32 may result in different WiFi connection stability, but overall it depends on the environment. 89 | 90 | ESP8266 PCB mounted at the back of the PC case: 91 | 92 | ![ESP PC mount](images/ESPMount.jpg) 93 | 94 | ## Software updates 95 | 96 | The ESPHome application firmware can be updated via USB connection (virtual serial port) or WiFi OTA (Over The Air update). 97 | 98 | Updating the firmware via WiFi is enabled when configuring `ota` in the .yaml file. This requires a WiFi connection between host computer and ESP. OTA update does not work when the login from ESP to WiFi base station fails due to incorrect WiFi credentials. In this case an update via serial is required or via `ap` fallback as configured in the .yaml file. 99 | 100 | ## ESPHome 101 | 102 | The ESPHome application consists of the two Yaml files. One configuration file and a second `secrets.yaml` to store passwords. 103 | 104 | Documentation: 105 | 106 | * [ESPHome GPIO Switch](https://esphome.io/components/switch/gpio.html) 107 | * [ESPHome Binary Sensor](https://esphome.io/components/binary_sensor/gpio.html) 108 | 109 | ### Project configuration 110 | 111 | Configure the following files: 112 | - [pc-power.yaml](https://github.com/Erriez/ESPHomePCPowerControlHomeAssistant/blob/master/pc-power.yaml): Configure `platform` and `board`. 113 | - [secrets.yaml](https://github.com/Erriez/ESPHomePCPowerControlHomeAssistant/blob/master/secrets.yaml): Configure WiFi SSID and passwords 114 | 115 | Please refer to [ESPHome documentation](https://esphome.io/components/esphome.html) for more information about ESPHome YAML configuration. 116 | 117 | 118 | ### Program ESP8266 or ESP32 119 | 120 | Connect USB cable to ESP8266 or ESP32 board and enter the following commands. (Examples are tested on Ubuntu). For more information, refer to [ESPHome.io](https://esphome.io/guides/getting_started_command_line.html). 121 | 122 | ```bash 123 | # Clone this repository 124 | $ git clone git@github.com:Erriez/ESPHomePCPowerControlHomeAssistant.git 125 | 126 | # Install Python3 virtualenv 127 | $ sudo apt install python3-virtualenv 128 | 129 | # Create virtualenv 130 | $ virtualenv venv 131 | 132 | # Activate virtualenv 133 | $ source venv/bin/activate 134 | 135 | # Install ESPHome 136 | $ pip install esphome 137 | 138 | # Optional: Install platformio updates 139 | $ platformio platform update 140 | $ platformio upgrade 141 | 142 | # Optional: Add user permission serial port 143 | $ sudo usermod -a -G dialout 144 | $ sudo reboot now 145 | 146 | # Check ESPHome installation 147 | $ esphome --help 148 | 149 | # Optional: Compile program without upload 150 | $ esphome compile pc-power.yaml 151 | 152 | # Upload program to ESP8266 or ESP32 153 | $ esphome run pc-power.yaml 154 | 155 | # Select serial port or WiFi to upload application 156 | 157 | # Check logs 158 | $ esphome logs pc-power.yaml 159 | ``` 160 | 161 | ## Home Assistant configuration 162 | 163 | This section describes Home Assistant configuration. 164 | 165 | ### Register ESP device 166 | 167 | * `Configuration | Integrations: Add Integration: ESPHome` 168 | * Select hostname or IP address of the ESP device. 169 | * Enter password as configured in `secrets.yml` | `esphome_api_password`. 170 | 171 | ### Homeassistant | Edit Dashboard | RAW Configuration Editor 172 | 173 | Add PC power integration to a dashboard via raw edit: 174 | 175 | ```yaml 176 | title: Home 177 | views: 178 | - title: PC 179 | path: pc 180 | badges: [] 181 | cards: 182 | - type: button 183 | entity: switch.pc_power_toggle 184 | show_name: true 185 | - type: button 186 | tap_action: 187 | action: none 188 | entity: binary_sensor.pc_power_state 189 | hold_action: 190 | action: none 191 | - type: button 192 | tap_action: 193 | action: toggle 194 | entity: switch.pc_hard_power_off 195 | icon_height: 40px 196 | show_state: false 197 | show_name: true 198 | show_icon: true 199 | ``` 200 | 201 | ## Version history 202 | 203 | Restart Home Assistant and ready to go! 204 | 205 | ### Update 11 October 2024 206 | 207 | The ESPHome YAML file format changed with [ESPHome version 2024.6.0](https://esphome.io/changelog/2024.6.0.html#). The old format generates errors like: 208 | 209 | ``` 210 | $ esphome compile ESPHomePCPowerControlHomeAssistant/pc-power.yaml 211 | INFO ESPHome 2024.9.2 212 | INFO Reading configuration ESPHomePCPowerControlHomeAssistant/pc-power.yaml... 213 | Failed config 214 | 215 | ota.unknown: [source ESPHomePCPowerControlHomeAssistant/pc-power.yaml:27] 216 | 217 | 'ota' requires a 'platform' key but it was not specified. 218 | 219 | and: 220 | 221 | Failed config 222 | 223 | switch.gpio: [source ESPHomePCPowerControlHomeAssistant/pc-power.yaml:30] 224 | 225 | Pin 4 is used in multiple places. 226 | ``` 227 | 228 | In this case, please update to the new file format in this project. 229 | 230 | ### Update 13 February 2022 231 | 232 | * The button pin timing controlled via Homeassistant scripts was not stable. The power pin control has been moved to the ESP firmware which generates a much more accurate timing. 233 | * Renamed the following names: 234 | * Rename `switch.pc_power_button` to `switch.pc_power_toggle`. 235 | * Rename `switch.pc_power_button_long_press` to `switch.pc_hard_power_off`. 236 | * Rename `switch.pc_power_sense` to `switch.pc_power_state`. 237 | --------------------------------------------------------------------------------