├── LICENSE ├── GarHAge ├── config.h └── GarHAge.ino └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 marthoc 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 | -------------------------------------------------------------------------------- /GarHAge/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GarHAge 3 | * a Home-Automation-friendly ESP8266-based MQTT Garage Door Controller 4 | * Licensed under the MIT License, Copyright (c) 2017 marthoc 5 | * 6 | * User-configurable Parameters 7 | */ 8 | 9 | // Wifi Parameters 10 | 11 | #define WIFI_SSID "your-ssid-name" 12 | #define WIFI_PASSWORD "your-ssid-password" 13 | 14 | // Static IP Parameters 15 | 16 | #define STATIC_IP false 17 | #define IP 192,168,1,100 18 | #define GATEWAY 192,168,1,1 19 | #define SUBNET 255,255,255,0 20 | 21 | // MQTT Parameters 22 | 23 | #define MQTT_BROKER "w.x.y.z" 24 | #define MQTT_CLIENTID "GarHAge" 25 | #define MQTT_USERNAME "your-mqtt-username" 26 | #define MQTT_PASSWORD "your-mqtt-password" 27 | 28 | // Relay Parameters 29 | 30 | #define ACTIVE_HIGH_RELAY true 31 | 32 | // Door 1 Parameters 33 | 34 | #define DOOR1_ALIAS "Door 1" 35 | #define MQTT_DOOR1_ACTION_TOPIC "garage/door/1/action" 36 | #define MQTT_DOOR1_STATUS_TOPIC "garage/door/1/status" 37 | #define DOOR1_OPEN_PIN D2 38 | #define DOOR1_CLOSE_PIN D2 39 | #define DOOR1_STATUS_PIN D5 40 | #define DOOR1_STATUS_SWITCH_LOGIC "NO" 41 | 42 | // Door 2 Parameters 43 | 44 | #define DOOR2_ENABLED false 45 | #define DOOR2_ALIAS "Door 2" 46 | #define MQTT_DOOR2_ACTION_TOPIC "garage/door/2/action" 47 | #define MQTT_DOOR2_STATUS_TOPIC "garage/door/2/status" 48 | #define DOOR2_OPEN_PIN D1 49 | #define DOOR2_CLOSE_PIN D1 50 | #define DOOR2_STATUS_PIN D6 51 | #define DOOR2_STATUS_SWITCH_LOGIC "NO" 52 | 53 | -------------------------------------------------------------------------------- /GarHAge/GarHAge.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * GarHAge 3 | * a Home-Automation-friendly ESP8266-based MQTT Garage Door Controller 4 | * Licensed under the MIT License, Copyright (c) 2017 marthoc 5 | */ 6 | 7 | #include 8 | #include 9 | #include "config.h" 10 | 11 | // Mapping NodeMCU Ports to Arduino GPIO Pins 12 | // Allows use of NodeMCU Port nomenclature in config.h 13 | #define D0 16 14 | #define D1 5 15 | #define D2 4 16 | #define D3 0 17 | #define D4 2 18 | #define D5 14 19 | #define D6 12 20 | #define D7 13 21 | #define D8 15 22 | 23 | const char* ssid = WIFI_SSID; 24 | const char* password = WIFI_PASSWORD; 25 | 26 | const boolean static_ip = STATIC_IP; 27 | IPAddress ip(IP); 28 | IPAddress gateway(GATEWAY); 29 | IPAddress subnet(SUBNET); 30 | 31 | const char* mqtt_broker = MQTT_BROKER; 32 | const char* mqtt_clientId = MQTT_CLIENTID; 33 | const char* mqtt_username = MQTT_USERNAME; 34 | const char* mqtt_password = MQTT_PASSWORD; 35 | 36 | const boolean activeHighRelay = ACTIVE_HIGH_RELAY; 37 | 38 | const char* door1_alias = DOOR1_ALIAS; 39 | const char* mqtt_door1_action_topic = MQTT_DOOR1_ACTION_TOPIC; 40 | const char* mqtt_door1_status_topic = MQTT_DOOR1_STATUS_TOPIC; 41 | const int door1_openPin = DOOR1_OPEN_PIN; 42 | const int door1_closePin = DOOR1_CLOSE_PIN; 43 | const int door1_statusPin = DOOR1_STATUS_PIN; 44 | const char* door1_statusSwitchLogic = DOOR1_STATUS_SWITCH_LOGIC; 45 | 46 | const boolean door2_enabled = DOOR2_ENABLED; 47 | const char* door2_alias = DOOR2_ALIAS; 48 | const char* mqtt_door2_action_topic = MQTT_DOOR2_ACTION_TOPIC; 49 | const char* mqtt_door2_status_topic = MQTT_DOOR2_STATUS_TOPIC; 50 | const int door2_openPin = DOOR2_OPEN_PIN; 51 | const int door2_closePin = DOOR2_CLOSE_PIN; 52 | const int door2_statusPin = DOOR2_STATUS_PIN; 53 | const char* door2_statusSwitchLogic = DOOR2_STATUS_SWITCH_LOGIC; 54 | 55 | const int relayActiveTime = 500; 56 | int door1_lastStatusValue = 2; 57 | int door2_lastStatusValue = 2; 58 | unsigned long door1_lastSwitchTime = 0; 59 | unsigned long door2_lastSwitchTime = 0; 60 | int debounceTime = 2000; 61 | 62 | String availabilityBase = MQTT_CLIENTID; 63 | String availabilitySuffix = "/availability"; 64 | String availabilityTopicStr = availabilityBase + availabilitySuffix; 65 | const char* availabilityTopic = availabilityTopicStr.c_str(); 66 | const char* birthMessage = "online"; 67 | const char* lwtMessage = "offline"; 68 | 69 | WiFiClient espClient; 70 | PubSubClient client(espClient); 71 | 72 | // Wifi setup function 73 | 74 | void setup_wifi() { 75 | 76 | delay(10); 77 | Serial.println(); 78 | Serial.print("Connecting to "); 79 | Serial.print(ssid); 80 | 81 | WiFi.mode(WIFI_STA); 82 | WiFi.begin(ssid, password); 83 | 84 | if (static_ip) { 85 | WiFi.config(ip, gateway, subnet); 86 | } 87 | 88 | while (WiFi.status() != WL_CONNECTED) { 89 | delay(500); 90 | Serial.print("."); 91 | } 92 | 93 | Serial.print(" WiFi connected - IP address: "); 94 | Serial.println(WiFi.localIP()); 95 | } 96 | 97 | // Callback when MQTT message is received; calls triggerDoorAction(), passing topic and payload as parameters 98 | 99 | void callback(char* topic, byte* payload, unsigned int length) { 100 | Serial.print("Message arrived ["); 101 | Serial.print(topic); 102 | Serial.print("] "); 103 | 104 | for (int i = 0; i < length; i++) { 105 | Serial.print((char)payload[i]); 106 | } 107 | 108 | Serial.println(); 109 | 110 | String topicToProcess = topic; 111 | payload[length] = '\0'; 112 | String payloadToProcess = (char*)payload; 113 | triggerDoorAction(topicToProcess, payloadToProcess); 114 | } 115 | 116 | // Functions that check door status and publish an update when called 117 | 118 | void publish_door1_status() { 119 | if (digitalRead(door1_statusPin) == LOW) { 120 | if (door1_statusSwitchLogic == "NO") { 121 | Serial.print(door1_alias); 122 | Serial.print(" closed! Publishing to "); 123 | Serial.print(mqtt_door1_status_topic); 124 | Serial.println("..."); 125 | client.publish(mqtt_door1_status_topic, "closed", true); 126 | } 127 | else if (door1_statusSwitchLogic == "NC") { 128 | Serial.print(door1_alias); 129 | Serial.print(" open! Publishing to "); 130 | Serial.print(mqtt_door1_status_topic); 131 | Serial.println("..."); 132 | client.publish(mqtt_door1_status_topic, "open", true); 133 | } 134 | else { 135 | Serial.println("Error! Specify only either NO or NC for DOOR1_STATUS_SWITCH_LOGIC in config.h! Not publishing..."); 136 | } 137 | } 138 | else { 139 | if (door1_statusSwitchLogic == "NO") { 140 | Serial.print(door1_alias); 141 | Serial.print(" open! Publishing to "); 142 | Serial.print(mqtt_door1_status_topic); 143 | Serial.println("..."); 144 | client.publish(mqtt_door1_status_topic, "open", true); 145 | } 146 | else if (door1_statusSwitchLogic == "NC") { 147 | Serial.print(door1_alias); 148 | Serial.print(" closed! Publishing to "); 149 | Serial.print(mqtt_door1_status_topic); 150 | Serial.println("..."); 151 | client.publish(mqtt_door1_status_topic, "closed", true); 152 | } 153 | else { 154 | Serial.println("Error! Specify only either NO or NC for DOOR1_STATUS_SWITCH_LOGIC in config.h! Not publishing..."); 155 | } 156 | } 157 | } 158 | 159 | void publish_door2_status() { 160 | if (digitalRead(door2_statusPin) == LOW) { 161 | if (door2_statusSwitchLogic == "NO") { 162 | Serial.print(door2_alias); 163 | Serial.print(" closed! Publishing to "); 164 | Serial.print(mqtt_door2_status_topic); 165 | Serial.println("..."); 166 | client.publish(mqtt_door2_status_topic, "closed", true); 167 | } 168 | else if (door2_statusSwitchLogic == "NC") { 169 | Serial.print(door2_alias); 170 | Serial.print(" open! Publishing to "); 171 | Serial.print(mqtt_door2_status_topic); 172 | Serial.println("..."); 173 | client.publish(mqtt_door2_status_topic, "open", true); 174 | } 175 | else { 176 | Serial.println("Error! Specify only either NO or NC for DOOR2_STATUS_SWITCH_LOGIC in config.h! Not publishing..."); 177 | } 178 | } 179 | else { 180 | if (door2_statusSwitchLogic == "NO") { 181 | Serial.print(door2_alias); 182 | Serial.print(" open! Publishing to "); 183 | Serial.print(mqtt_door2_status_topic); 184 | Serial.println("..."); 185 | client.publish(mqtt_door2_status_topic, "open", true); 186 | } 187 | else if (door2_statusSwitchLogic == "NC") { 188 | Serial.print(door2_alias); 189 | Serial.print(" closed! Publishing to "); 190 | Serial.print(mqtt_door2_status_topic); 191 | Serial.println("..."); 192 | client.publish(mqtt_door2_status_topic, "closed", true); 193 | } 194 | else { 195 | Serial.println("Error! Specify only either NO or NC for DOOR2_STATUS_SWITCH_LOGIC in config.h! Not publishing..."); 196 | } 197 | } 198 | } 199 | 200 | // Functions that run in loop() to check each loop if door status (open/closed) has changed and call publish_doorX_status() to publish any change if so 201 | 202 | void check_door1_status() { 203 | int currentStatusValue = digitalRead(door1_statusPin); 204 | if (currentStatusValue != door1_lastStatusValue) { 205 | unsigned int currentTime = millis(); 206 | if (currentTime - door1_lastSwitchTime >= debounceTime) { 207 | publish_door1_status(); 208 | door1_lastStatusValue = currentStatusValue; 209 | door1_lastSwitchTime = currentTime; 210 | } 211 | } 212 | } 213 | 214 | void check_door2_status() { 215 | int currentStatusValue = digitalRead(door2_statusPin); 216 | if (currentStatusValue != door2_lastStatusValue) { 217 | unsigned int currentTime = millis(); 218 | if (currentTime - door2_lastSwitchTime >= debounceTime) { 219 | publish_door2_status(); 220 | door2_lastStatusValue = currentStatusValue; 221 | door2_lastSwitchTime = currentTime; 222 | } 223 | } 224 | } 225 | 226 | // Function that publishes birthMessage 227 | 228 | void publish_birth_message() { 229 | // Publish the birthMessage 230 | Serial.print("Publishing birth message \""); 231 | Serial.print(birthMessage); 232 | Serial.print("\" to "); 233 | Serial.print(availabilityTopic); 234 | Serial.println("..."); 235 | client.publish(availabilityTopic, birthMessage, true); 236 | } 237 | 238 | // Function that toggles the relevant relay-connected output pin 239 | 240 | void toggleRelay(int pin) { 241 | if (activeHighRelay) { 242 | digitalWrite(pin, HIGH); 243 | delay(relayActiveTime); 244 | digitalWrite(pin, LOW); 245 | } 246 | else { 247 | digitalWrite(pin, LOW); 248 | delay(relayActiveTime); 249 | digitalWrite(pin, HIGH); 250 | } 251 | } 252 | 253 | // Function called by callback() when a message is received 254 | // Passes the message topic as the "requestedDoor" parameter and the message payload as the "requestedAction" parameter 255 | 256 | void triggerDoorAction(String requestedDoor, String requestedAction) { 257 | if (requestedDoor == mqtt_door1_action_topic && requestedAction == "OPEN") { 258 | Serial.print("Triggering "); 259 | Serial.print(door1_alias); 260 | Serial.println(" OPEN relay!"); 261 | toggleRelay(door1_openPin); 262 | } 263 | else if (requestedDoor == mqtt_door1_action_topic && requestedAction == "CLOSE") { 264 | Serial.print("Triggering "); 265 | Serial.print(door1_alias); 266 | Serial.println(" CLOSE relay!"); 267 | toggleRelay(door1_closePin); 268 | } 269 | else if (requestedDoor == mqtt_door1_action_topic && requestedAction == "STATE") { 270 | Serial.print("Publishing on-demand status update for "); 271 | Serial.print(door1_alias); 272 | Serial.println("!"); 273 | publish_birth_message(); 274 | publish_door1_status(); 275 | } 276 | else if (requestedDoor == mqtt_door2_action_topic && requestedAction == "OPEN") { 277 | Serial.print("Triggering "); 278 | Serial.print(door2_alias); 279 | Serial.println(" OPEN relay!"); 280 | toggleRelay(door2_openPin); 281 | } 282 | else if (requestedDoor == mqtt_door2_action_topic && requestedAction == "CLOSE") { 283 | Serial.print("Triggering "); 284 | Serial.print(door2_alias); 285 | Serial.println(" CLOSE relay!"); 286 | toggleRelay(door2_closePin); 287 | } 288 | else if (requestedDoor == mqtt_door2_action_topic && requestedAction == "STATE") { 289 | Serial.print("Publishing on-demand status update for "); 290 | Serial.print(door2_alias); 291 | Serial.println("!"); 292 | publish_birth_message(); 293 | publish_door2_status(); 294 | } 295 | else { Serial.println("Unrecognized action payload... taking no action!"); 296 | } 297 | } 298 | 299 | // Function that runs in loop() to connect/reconnect to the MQTT broker, and publish the current door statuses on connect 300 | 301 | void reconnect() { 302 | // Loop until we're reconnected 303 | while (!client.connected()) { 304 | Serial.print("Attempting MQTT connection..."); 305 | // Attempt to connect 306 | if (client.connect(mqtt_clientId, mqtt_username, mqtt_password, availabilityTopic, 0, true, lwtMessage)) { 307 | Serial.println("Connected!"); 308 | 309 | // Publish the birth message on connect/reconnect 310 | publish_birth_message(); 311 | 312 | // Subscribe to the action topics to listen for action messages 313 | Serial.print("Subscribing to "); 314 | Serial.print(mqtt_door1_action_topic); 315 | Serial.println("..."); 316 | client.subscribe(mqtt_door1_action_topic); 317 | 318 | if (door2_enabled) { 319 | Serial.print("Subscribing to "); 320 | Serial.print(mqtt_door2_action_topic); 321 | Serial.println("..."); 322 | client.subscribe(mqtt_door2_action_topic); 323 | } 324 | 325 | // Publish the current door status on connect/reconnect to ensure status is synced with whatever happened while disconnected 326 | publish_door1_status(); 327 | if (door2_enabled) { publish_door2_status(); 328 | } 329 | 330 | } 331 | else { 332 | Serial.print("failed, rc="); 333 | Serial.print(client.state()); 334 | Serial.println(" try again in 5 seconds"); 335 | // Wait 5 seconds before retrying 336 | delay(5000); 337 | } 338 | } 339 | } 340 | 341 | void setup() { 342 | // Setup the output and input pins used in the sketch 343 | // Set the lastStatusValue variables to the state of the status pins at setup time 344 | 345 | // Setup Door 1 pins 346 | pinMode(door1_openPin, OUTPUT); 347 | pinMode(door1_closePin, OUTPUT); 348 | // Set output pins LOW with an active-high relay 349 | if (activeHighRelay) { 350 | digitalWrite(door1_openPin, LOW); 351 | digitalWrite(door1_closePin, LOW); 352 | } 353 | // Set output pins HIGH with an active-low relay 354 | else { 355 | digitalWrite(door1_openPin, HIGH); 356 | digitalWrite(door1_closePin, HIGH); 357 | } 358 | // Set input pin to use internal pullup resistor 359 | pinMode(door1_statusPin, INPUT_PULLUP); 360 | // Update variable with current door state 361 | door1_lastStatusValue = digitalRead(door1_statusPin); 362 | 363 | // Setup Door 2 pins 364 | if (door2_enabled) { 365 | pinMode(door2_openPin, OUTPUT); 366 | pinMode(door2_closePin, OUTPUT); 367 | // Set output pins LOW with an active-high relay 368 | if (activeHighRelay) { 369 | digitalWrite(door2_openPin, LOW); 370 | digitalWrite(door2_closePin, LOW); 371 | } 372 | // Set output pins HIGH with an active-low relay 373 | else { 374 | digitalWrite(door2_openPin, HIGH); 375 | digitalWrite(door2_closePin, HIGH); 376 | } 377 | // Set input pin to use internal pullup resistor 378 | pinMode(door2_statusPin, INPUT_PULLUP); 379 | // Update variable with current door state 380 | door2_lastStatusValue = digitalRead(door2_statusPin); 381 | } 382 | 383 | // Setup serial output, connect to wifi, connect to MQTT broker, set MQTT message callback 384 | Serial.begin(115200); 385 | 386 | Serial.println("Starting GarHAge..."); 387 | 388 | if (activeHighRelay) { 389 | Serial.println("Relay mode: Active-High"); 390 | } 391 | else { 392 | Serial.println("Relay mode: Active-Low"); 393 | } 394 | 395 | setup_wifi(); 396 | client.setServer(mqtt_broker, 1883); 397 | client.setCallback(callback); 398 | } 399 | 400 | void loop() { 401 | // Connect/reconnect to the MQTT broker and listen for messages 402 | if (!client.connected()) { 403 | reconnect(); 404 | } 405 | client.loop(); 406 | 407 | // Check door open/closed status each loop and publish changes 408 | check_door1_status(); 409 | if (door2_enabled) { check_door2_status(); 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GarHAge 2 | ## a Home-Automation-friendly ESP8266-based MQTT Garage Door Controller 3 | 4 | GarHAge allows two "dumb" garage door openers to be controlled (open/close) and report garage door status (open/closed) via MQTT. 5 | 6 | GarHAge is almost completely compatible with Home Assistant's "MQTT Cover" platform. It responds to HASS's open and close commands and reports door status to keep HASS's GUI in sync with the door state. GarHAge does not implement HASS's "stop" command (as this functionality varies between garage door openers) or "tilt" functionality. Sample HASS configuration snippets are provided in this repository to get your garage door openers connected to HASS as quickly and painlessly as possible. 7 | 8 | GarHAge should be controllable via any home automation software that can configure an MQTT cover, rollershutter, garage door, or send commands over MQTT, including OpenHAB. 9 | 10 | GarHAge has both hardware and software components. The required hardware components include an ESP8266-based microcontroller (such as the NodeMCU), a relay module, and reed/magnetic switches. The software component is found in this repo. 11 | 12 | Best of all, if you select the proper parts, building and installing a GarHAge requires **no soldering**! 13 | 14 | ## Table of Contents 15 | 16 | * [GarHAge](#garhage) 17 | * [a Home\-Automation\-friendly ESP8266\-based MQTT Garage Door Controller](#a-home-automation-friendly-esp8266-based-mqtt-garage-door-controller) 18 | * [How GarHAge works\.\.\.](#how-garhage-works) 19 | * [Hardware](#hardware) 20 | * [Bill of Materials](#bill-of-materials) 21 | * [Detailed Bill of Materials](#detailed-bill-of-materials) 22 | * [1\. ESP8266\-based microcontroller](#1-esp8266-based-microcontroller) 23 | * [2\. Dual 5v relay module with active\-high inputs](#2-dual-5v-relay-module-with-active-high-inputs) 24 | * [3\. Reed/magnetic door switches](#3-reedmagnetic-door-switches) 25 | * [4\. 5v MicroUSB power supply](#4-5v-microusb-power-supply) 26 | * [5\. Mini solderless breadboard (170 tie\-point)](#5-mini-solderless-breadboard-170-tie-point) 27 | * [6\. \- 8\. Miscellaneous parts](#6---8-miscellaneous-parts) 28 | * [Building GarHAge](#building-garhage) 29 | * [Software](#software) 30 | * [1\. Set up the Arduino IDE](#1-set-up-the-arduino-ide) 31 | * [2\. Load the sketch in the Arduino IDE and modify the user parameters in config\.h](#2-load-the-sketch-in-the-arduino-ide-and-modify-the-user-parameters-in-configh) 32 | * [Wifi Parameters](#wifi-parameters) 33 | * [Static IP Parameters](#static-ip-parameters) 34 | * [MQTT Parameters](#mqtt-parameters) 35 | * [Door 1 Parameters](#door-1-parameters) 36 | * [Door 2 Parameters](#door-2-parameters) 37 | * [3\. Upload the sketch to your NodeMCU / microcontroller](#3-upload-the-sketch-to-your-nodemcu--microcontroller) 38 | * [4\. Check the Arduino IDE Serial Monitor](#4-check-the-arduino-ide-serial-monitor) 39 | * [Installing GarHAge](#installing-garhage) 40 | * [Configuring Home Assistant](#configuring-home-assistant) 41 | * [HASS Automation Workaround](#hass-automation-workaround) 42 | * [MQTT Cover: Basic configuration](#mqtt-cover-basic-configuration) 43 | * [MQTT Cover: Complete configuration](#mqtt-cover-complete-configuration) 44 | * [MQTT Binary Sensor](#mqtt-binary-sensor) 45 | * [HASS Example Automations](#hass-example-automations) 46 | * [Configuring OpenHAB](#configuring-openhab) 47 | * [Contributing](#contributing) 48 | * [Issues/Bug Reports/Feature Requests](#issuesbug-reportsfeature-requests) 49 | 50 | ## How GarHAge works... 51 | 52 | GarHAge subscribes to a configurable MQTT topic for each of two garage doors (by default, `garage/door/1/action` and `garage/door/2/action`). 53 | 54 | When the `OPEN` payload is received on either of these topics, GarHAge momentarily activates a relay connected to the relevant garage door opener to cause the door to open. 55 | 56 | When the `CLOSE` payload is received on either of these topics, GarHAge momentarily activates a relay connected to the relevant garage door opener to cause the door to close. By default, GarHAge is configured to activate the same relay for the `OPEN` and `CLOSE` payloads, as most (if not all) garage door openers operate manually by momentarily closing the same circuit to both open and close. 57 | 58 | When the `STATE` payload is received on either of these topics, GarHAge publishes the status (`open` or `closed`) of the relevant garage door to the configurable topic `garage/door/1/status` or `garage/door/2/status`. These messages are published with the "retain" flag set. _(Note: To address a current issue in Home Assistant that may result in MQTT platforms not showing the correct garage door status (open/closed) after a HASS restart, I recommend creating an automation in Home Assistant that publishes the `STATE` payload for each door on HASS start. An example is provided in the [Configuring Home Assistant](#configuring-home-assistant) section of this documentation.)_ 59 | 60 | When the state of a garage door changes (either because GarHAge has triggered the door to open or close, or because the door has been opened or closed via a remote, pushbutton switch, or manually), GarHAge publishes the status (`open` or `closed`) of the relevant garage door to `garage/door/1/status` or `garage/door/2/status`. These messages are published with the "retain" flag set. 61 | 62 | GarHAge also publishes a "birth" message on connection to your MQTT broker, and a "last-will-and-testament" message on disconnection from the broker, so that your home automation software can respond appropriately to GarHAge being online or offline. 63 | 64 | 65 | ## Hardware 66 | 67 | ### Bill of Materials 68 | 69 | Building GarHAge to control two garage door openers requires: 70 | 71 | | No. | Qty | Part | Link | Approx Price | 72 | | --- | --- | ---- | ---- | ------------ | 73 | | 1. | 1 | ESP8266-based microcontroller (e.g. NodeMCU) | [Link](https://www.amazon.com/ESP8266-NodeMcu-development-Internet-HONG111/dp/B06XBSV95D/ref=sr_1_6?ie=UTF8&qid=1504449670&sr=8-6&keywords=nodemcu) | $ 7.00 | 74 | | 2. | 1 | Dual-relay module | [Link](http://www.robotshop.com/en/2-channel-5v-relay-module.html) | $ 4.00 | 75 | | 3. | 2 | Reed/Magnetic door switches | [Link](http://a.co/bEomwwP) | $ 12.00 | 76 | | 4. | 1 | 5v MicroUSB power supply | [Link](http://www.robotshop.com/en/wall-adapter-power-supply-5vdc-2a.html) | $ 6.00 | 77 | | 5. | 1 | Mini solderless breadboard (170 tie-point) | [Link](http://www.robotshop.com/en/170-tie-point-mini-self-adhesive-solderless-breadboard-white.html) | $ 4.00 | 78 | | 6. | | Bell/low voltage two-conductor wire | | | 79 | | 7. | | Male-to-female breadboard jumper wires | | | 80 | | 8. | | Project box or case | | | 81 | 82 | **Approximate total cost for major components:** $ 33.00, even less if you don't mind a long lead time and source your components from AliExpress/BangGood. 83 | 84 | **Note:** If you are building GarHAge to control only one garage door opener, you will only require a single-relay module, and a single reed switch. 85 | 86 | 87 | ### Detailed Bill of Materials 88 | 89 | #### 1. ESP8266-based microcontroller 90 | 91 | I recommend the NodeMCU as GarHAge was developed and is tested on it. Its advantages are: 92 | - it comes with header pins already soldered so that it can plug directly into a mini solderless breadboard; 93 | - its VIN (or VU on the LoLin variant) port can power the 5v relay module; 94 | - it can be powered and programmed via MicroUSB; 95 | - it has Reset and Flash buttons, making programming easy. 96 | 97 | Accordingly, this guide is written with the NodeMCU in mind. 98 | 99 | But, Garhage should also work with the Adafruit HUZZAH, Wemos D1, or similar, though you may need to adjust the GPIO ports used by the sketch to match the ESP8266 ports that your microcontroller makes available. 100 | 101 | #### 2. Dual 5v relay module 102 | 103 | A dual 5v relay module (as opposed to individual 5v relays) makes setup easy: just plug jumper wires from the module's VCC, CH1, CH2, and GND pins to the NodeMCU. These relay modules generally also include LEDs to ease troubleshooting. 104 | 105 | Most importantly, because the relay module is powered by 5v, its inputs can be triggered by the NodeMCU's GPIOs. 106 | 107 | GarHAge will work with relay modules that are active-high or active-low; if using an active-low relay, be sure to set the relevant configuration parameter in config.h, described below, and test thoroughly to be sure that your garage door opener(s) are not inadvertently triggered after a momentary power-loss. 108 | 109 | #### 3. Reed/magnetic door switches 110 | 111 | GarHAge will work with both normally-open and normally-closed reed switches; if using a normally-closed switch, be sure to set the relevant configuration parameter in config.h, described below, to match the type of switch in use. Many switches with screw terminals are available, making placement and wiring easy. 112 | 113 | #### 4. 5v MicroUSB power supply 114 | 115 | Power your NodeMCU via the same type of power supply used to charge Android phones or power a RaspberryPi. Powering the NodeMCU via MicroUSB is important since the relay module can then be powered via the NodeMCU VIN (or VU on the LoLin variant) port. 116 | 117 | #### 5. Mini solderless breadboard (170 tie-point) 118 | 119 | The NodeMCU mounts to this breadboard nicely, leaving one female port next to each NodeMCU port and making it easy to use male-to-female jumper wires to make connections from the NodeMCU to the relay module. The bell wire attached to the reed switches will also plug into the breadboard ports, making for a clean and solderless installation. Finally, these mini breadboards often also have an adhesive backing, making mounting in your project box easy. 120 | 121 | #### 6. - 8. Miscellaneous parts 122 | 123 | To install GarHAge, you will also require: 124 | - Enough bell/low voltage two-conductor wire to make connections from each reed/magnetic switch at your garage doors to where GarHAge is mounted and to make connections from GarHAge to each garage door opener. 125 | - Male-to-female breadboard jumper wires to make connections from the NodeMCU to the dual-relay module (4 jumper wires required). 126 | - a project box to hold both the NodeMCU and dual-relay module. 127 | 128 | ### Building GarHAge 129 | 130 | 1. Attach your NodeMCU to the middle of the mini solderless breadboard, leaving one female port next to each NodeMCU port. 131 | 2. Mount the mini solderless breadboard in your project box. 132 | 3. Mount the dual-relay module in your project box. 133 | 4. Plug a jumper wire from VCC on the relay module to VIN / VU on the NodeMCU. 134 | 5. Plug a jumper wire from GND on the relay module to GND on the NodeMCU. 135 | 6. Plug a jumper wire from CH1 on the relay module to D2 on the NodeMCU (or Arduino/ESP8266 GPIO4). 136 | 7. Plug a jumper wire from CH2 on the relay module to D1 on the NodeMCU (or Arduino/ESP8266 GPIO5). 137 | 138 | _Done!_ 139 | 140 | 141 | ## Software 142 | 143 | ### 1. Set up the Arduino IDE 144 | 145 | You will modify the configuration parameters and upload the sketch to the NodeMCU with the Arduino IDE. 146 | 147 | 1. Download the Arduino IDE for your platform from [here](https://www.arduino.cc/en/Main/Software) and install it. 148 | 2. Add support for the ESP8266 to the Arduino IDE by following the instructions under "Installing with Boards Manager" [here](https://github.com/esp8266/arduino). 149 | 3. Add the "PubSubClient" library to the Arduino IDE: follow the instructions for using the Library Manager [here](https://www.arduino.cc/en/Guide/Libraries#toc3), and search for and install the PubSubClient library. 150 | 4. You may need to install a driver for the NodeMCU for your OS - Google for instructions for your specific microcontroller and platform, install the driver if necessary, and restart the Arduino IDE. 151 | 5. Select your board from `Tools - Boards` in the Arduino IDE (e.g. "NodeMCU 1.0 (ESP-12E Module)"). 152 | 153 | ### 2. Load the sketch in the Arduino IDE and modify the user parameters in config.h 154 | 155 | GarHAge's configuration parameters are found in config.h. After loading GarHAge.ino, select the config.h tab in the Arduino IDE. This section describes the configuration parameters and their permitted values. 156 | 157 | _IMPORTANT: No modification of the sketch code in GarHAge.ino is necessary (or advised, unless you are confident you know what you are doing and are prepared for things to break unexpectedly)._ 158 | 159 | #### Wifi Parameters 160 | 161 | `WIFI_SSID "your-wifi-ssid"` 162 | 163 | The wifi ssid GarHAge will connect to. Must be placed within quotation marks. 164 | 165 | `WIFI_PASSWORD "your-wifi-password"` 166 | 167 | The wifi ssid's password. Must be placed within quotation marks. 168 | 169 | #### Static IP Parameters 170 | 171 | `STATIC_IP false` 172 | 173 | Set to `true` to use the IP / GATEWAY / SUBNET parameters that follow. Set to `false` to use DHCP. _(Default: false)_ 174 | 175 | `IP 192,168,1,100` 176 | 177 | The static IP you want to assign to GarHAge. _(Default: 192.168.1.100)_ 178 | 179 | `GATEWAY 192,168,1,1` 180 | 181 | The gateway you want GarHAge to use. _(Default: 192.168.1.1)_ 182 | 183 | `SUBNET 255,255,255,0` 184 | 185 | The subnet mask you want GarHAge to use. _(Default: 255.255.255.0)_ 186 | 187 | _Note: There are commas (,) not periods (.) in the IP / GATEWAY / SUBNET parameters above!_ 188 | 189 | #### MQTT Parameters 190 | 191 | `MQTT_BROKER "w.x.y.z"` 192 | 193 | The IP address of your MQTT broker. Must be placed within quotation marks. 194 | 195 | `MQTT_CLIENTID "GarHAge"` 196 | 197 | The Client ID you want GarHAge to use. Should be unique among all the devices connected to your broker. Must be placed within quotation marks. _(Default: GarHAge)_ 198 | 199 | `MQTT_USERNAME "your-mqtt-username"` 200 | 201 | The username required to authenticate to your MQTT broker. Must be placed within quotation marks. Use "" (i.e. a pair of quotation marks) if your broker does not require authentication. 202 | 203 | `MQTT_PASSWORD "your-mqtt-password"` 204 | 205 | The password required to authenticate to your MQTT broker. Must be placed within quotation marks. Use "" (i.e. a pair of quotation marks) if your broker does not require authentication. 206 | 207 | #### Relay Parameters 208 | 209 | `ACTIVE_HIGH_RELAY true` 210 | 211 | Set to `false` if using an active-low relay module. Set to `true` if using an active-high relay module. _(Default: true)_ 212 | 213 | #### Door 1 Parameters 214 | 215 | `DOOR1_ALIAS "Door 1"` 216 | 217 | The alias to be used for Door 1 in serial messages. Must be placed within quotation marks. _(Default: Door 1)_ 218 | 219 | `MQTT_DOOR1_ACTION_TOPIC "garage/door/1/action"` 220 | 221 | The topic GarHAge will subscribe to for action commands for Door 1. Must be placed within quotation marks. _(Default: garage/door/1/action)_ 222 | 223 | `MQTT_DOOR1_STATUS_TOPIC "garage/door/1/status"` 224 | 225 | The topic GarHAge will publish Door 1's status to. Must be placed within quotation marks. _(Default: garage/door/1/status)_ 226 | 227 | `DOOR1_OPEN_PIN D2` 228 | 229 | The GPIO pin connected to the relay that is connected to Door 1's garage door opener's open terminals. _(Default: NodeMCU D2 / Arduino 4)_ 230 | 231 | `DOOR1_CLOSE_PIN D2` 232 | 233 | The GPIO pin connected to the relay that is connected to Door 1's garage door opener's close terminals. If your garage door opener is like most (all?), the same terminals control open and close via a momentary connection of the terminals. In this case, set DOOR1_CLOSE_PIN and DOOR1_OPEN_PIN to the same pin. _(Default: NodeMCU D2 / Arduino 4)_ 234 | 235 | `DOOR1_STATUS_PIN D5` 236 | 237 | The GPIO pin connected to the reed/magnetic switch attached to Door 1. _(Default: NodeMCU D5 / Arduino 14)_ 238 | 239 | `DOOR1_STATUS_SWITCH_LOGIC "NO"` 240 | 241 | The type of reed/magnetic switch used for Door 1. Must be placed within quotation marks. Set to `"NO` for normally-open. Set to `"NC"` for normally-closed. _(Default: NO)_ 242 | 243 | #### Door 2 Parameters 244 | 245 | `DOOR2_ENABLED false` 246 | 247 | Set to `true` to enable GarHAge to control/monitor Door 2. Set to `false` to disable Door 2. _(Default: false)_ 248 | 249 | `DOOR2_ALIAS "Door 2"` 250 | 251 | The alias to be used for Door 2 in serial messages. Must be placed within quotation marks. _(Default: Door 2)_ 252 | 253 | `MQTT_DOOR2_ACTION_TOPIC "garage/door/2/action"` 254 | 255 | The topic GarHAge will subscribe to for action commands for Door 2. Must be placed within quotation marks. _(Default: garage/door/2/action)_ 256 | 257 | `MQTT_DOOR2_STATUS_TOPIC "garage/door/2/status"` 258 | 259 | The topic GarHAge will publish Door 2's status to. Must be placed within quotation marks. _(Default: garage/door/2/status)_ 260 | 261 | `DOOR2_OPEN_PIN D1` 262 | 263 | The GPIO pin connected to the relay that is connected to Door 2's garage door opener's open terminals. _(Default: NodeMCU D1 / Arduino 5)_ 264 | 265 | `DOOR2_CLOSE_PIN D1` 266 | 267 | The GPIO pin connected to the relay that is connected to Door 2's garage door opener's close terminals. If your garage door opener is like most (all?), the same terminals control open and close via a momentary connection of the terminals. In this case, set DOOR2_CLOSE_PIN and DOOR2_OPEN_PIN to the same pin. _(Default: NodeMCU D1 / Arduino 5)_ 268 | 269 | `DOOR2_STATUS_PIN D6` 270 | 271 | The GPIO pin connected to the reed/magnetic switch attached to Door 2. _(Default: NodeMCU D6 / Arduino 12)_ 272 | 273 | `DOOR2_STATUS_SWITCH_LOGIC "NO"` 274 | 275 | The type of reed/magnetic switch used for Door 2. Must be placed within quotation marks. Set to `"NO` for normally-open. Set to `"NC"` for normally-closed. _(Default: NO)_ 276 | 277 | ### 3. Upload the sketch to your NodeMCU / microcontroller 278 | 279 | If using the NodeMCU, connect it to your computer via MicroUSB; press and hold the reset button on the NodeMCU, press and hold the Flash button on the NodeMCU, then release the Reset button. Select `Sketch - Upload` in the Arduino IDE. 280 | 281 | If using a different ESP8266 microcontroller, follow that device's instructions for putting it into flashing/programming mode. 282 | 283 | ### 4. Check the Arduino IDE Serial Monitor 284 | 285 | Open the Serial Monitor via `Tools - Serial Monitor`. Reset your microcontroller. If all is working correctly, you should see something similar to the following messages: 286 | 287 | ``` 288 | Starting GarHAge... 289 | Relay mode: Active-High 290 | 291 | Connecting to your-wifi-ssid.. WiFi connected - IP address: 192.168.1.100 292 | Attempting MQTT connection...Connected! 293 | Publishing birth message "online" to GarHAge/availability... 294 | Subscribing to garage/door/1/action... 295 | Subscribing to garage/door/2/action... 296 | Door 1 closed! Publishing to garage/door/1/status... 297 | Door 2 closed! Publishing to garage/door/2/status... 298 | ``` 299 | 300 | If you receive these (or similar) messages, all appears to be working correctly. Disconnect GarHAge from your computer and prepare to install in your garage. 301 | 302 | ## Installing GarHAge 303 | 304 | 1. Mount GarHAge in your garage. 305 | 2. Mount the reed switch for Door 1. 306 | 3. Run bell/low voltage wire from Door 1's reed switch to the NodeMCU; strip the wires and plug into D5 and GND. 307 | 4. Mount the reed switch for Door 2. 308 | 5. Run bell/low voltage wire from Door 2's reed switch to the NodeMCU; strip the wires and plug into D6 and GND. 309 | 6. Connect bell/low voltage wire to the NO and COMMON terminals of relay 1 on your relay module; run the wire to the garage door opener for Door 1 and connect to the opener's terminals (the same terminals that the pushbutton switch for your door is attached to). 310 | 7. Connect bell/low voltage wire to the NO and COMMON terminals of relay 2 on your relay module; run the wire to the garage door opener for Door 2 and connect to the opener's terminals (the same terminals that the pushbutton switch for your door is attached to). 311 | 312 | _Done!_ 313 | 314 | 315 | ## Configuring Home Assistant 316 | 317 | GarHAGE supports both Home Assistant's "MQTT Cover" and "MQTT Binary Sensor" platforms. 318 | 319 | ### HASS Automation Workaround 320 | 321 | _Testing has shown that the MQTT platform in Home Assistant sometimes does not update the status of an entity (such as the cover or binary sensor) in accordance with a retained message on the status topic on HASS start or restart. This can leave the cover entity in an "unknown" state, and the binary sensor may show "closed" even if the door is open._ 322 | 323 | _The entities will, however, update to show the correct state when the door next changes state and GarHAge publishes the state to the door's status topic._ 324 | 325 | _A temporary workaround (until this issue is resolved in HASS, or possibly in the underlying Paho MQTT library used in HASS) is to use an automation to send the "STATE" payload to the doors' action topics on Home Assistant start. This will ensure that the cover and binary sensor show the correct door state on HASS start and restart._ 326 | 327 | Place the following in your `automations.yaml` (adjusting if you have only one door controlled by GarHAge); replace `id: xyz` with the next sequential automation number according to your own `automations.yaml`. 328 | 329 | ``` 330 | - id: xyz 331 | alias: Update garage door state on startup 332 | trigger: 333 | - platform: homeassistant 334 | event: start 335 | action: 336 | - service: mqtt.publish 337 | data: 338 | topic: "garage/door/1/action" 339 | payload: "STATE" 340 | - service: mqtt.publish 341 | data: 342 | topic: "garage/door/2/action" 343 | payload: "STATE" 344 | ``` 345 | 346 | ### MQTT Cover: Basic configuration 347 | 348 | ``` 349 | cover: 350 | - platform: mqtt 351 | name: "Garage Door 1" 352 | state_topic: "garage/door/1/status" 353 | command_topic: "garage/door/1/action" 354 | availability_topic: "GarHAge/availability" 355 | 356 | - platform: mqtt 357 | name: "Garage Door 2" 358 | state_topic: "garage/door/2/status" 359 | command_topic: "garage/door/2/action" 360 | availability_topic: "GarHAge/availability" 361 | ``` 362 | 363 | _Note: GarHAge's default parameters match Home Assistant's defaults and, as such, the above minimal configuration will work._ 364 | 365 | _Note: the "availability_topic" configuration parameter will be available in Home Assistant as of version 0.55; it allows Home Assistant to display the cover as "unavailable" if GarHAge goes offline unexpectedly. When GarHAge reconnects to your broker, the cover controls will again be available in Home Assistant. GarHAge forms its availability topic by suffixing "/availability" to the MQTT_CLIENTID parameter in config.h, and publishes "online" to that topic when connecting or reconnecting to the broker and "offline" when disconnecting from the broker._ 366 | 367 | ### MQTT Cover: Complete configuration 368 | 369 | _Note: If you want to guard against your GarHAge configuration breaking if a Home Assistant update changes one of its defaults, the complete configuration below can be added._ 370 | 371 | ``` 372 | cover: 373 | - platform: mqtt 374 | name: "Garage Door 1" 375 | state_topic: "garage/door/1/status" 376 | command_topic: "garage/door/1/action" 377 | availability_topic: "GarHAge/availability" 378 | qos: 0 379 | optimistic: false 380 | retain: false 381 | payload_open: "OPEN" 382 | payload_close: "CLOSE" 383 | payload_stop: "STATE" 384 | state_open: "open" 385 | state_closed: "closed" 386 | payload_available: "online" 387 | payload_not_available: "offline" 388 | 389 | - platform: mqtt 390 | name: "Garage Door 2" 391 | state_topic: "garage/door/2/status" 392 | command_topic: "garage/door/2/action" 393 | availability_topic: "GarHAge/availability" 394 | qos: 0 395 | optimistic: false 396 | retain: false 397 | payload_open: "OPEN" 398 | payload_close: "CLOSE" 399 | payload_stop: "STATE" 400 | state_open: "open" 401 | state_closed: "closed" 402 | payload_available: "online" 403 | payload_not_available: "offline" 404 | ``` 405 | 406 | _Note: Setting_ `payload_stop` _to_ `STATE` _allows you to trigger a status update from GarHAge for a Door by pressing the stop button for that door in the HASS GUI. The stop button in the HASS GUI is otherwise unused._ 407 | 408 | ### MQTT Binary Sensor 409 | 410 | ``` 411 | binary_sensor: 412 | - platform: mqtt 413 | name: "Garage Door 1" 414 | state_topic: "garage/door/1/status" 415 | payload_on: "open" 416 | payload_off: "closed" 417 | availability_topic: "GarHAge/availability" 418 | device_class: opening 419 | qos: 0 420 | 421 | - platform: mqtt 422 | name: "Garage Door 2" 423 | state_topic: "garage/door/2/status" 424 | payload_on: "open" 425 | payload_off: "closed" 426 | availability_topic: "GarHAge/availability" 427 | device_class: opening 428 | qos: 0 429 | ``` 430 | 431 | _Note: the "availability_topic" configuration parameter will be available in Home Assistant as of version 0.54; it allows Home Assistant to display the binary sensor as "unavailable" if GarHAge goes offline unexpectedly. When GarHAge reconnects to your broker, the door state will again be displayed in Home Assistant. GarHAge forms its availability topic by suffixing "/availability" to the MQTT_CLIENTID parameter in config.h, and publishes "online" to that topic when connecting or reconnecting to the broker and "offline" when disconnecting from the broker._ 432 | 433 | ### HASS Example Automations 434 | 435 | In the examples that follow, replace `id: xyz` with the next sequential automation number according to your own `automations.yaml`. 436 | 437 | #### Close garage door if open for longer than 30 minutes 438 | 439 | Place the following in your `automations.yaml`: 440 | 441 | ``` 442 | - id: xyz 443 | alias: Close garage door if open for longer than 30 minutes 444 | trigger: 445 | - platform: state 446 | entity_id: cover.garage_door_1 447 | to: "open" 448 | for: 449 | minutes: 30 450 | action: 451 | - service: cover.close_cover 452 | entity_id: cover.garage_door_1 453 | ``` 454 | 455 | Of course, you can replace `30` with any length of time in minutes you wish. Be sure to replace `cover.garage_door_1` if the name of your garage door in Home Assistant is different. 456 | 457 | #### Notify iOS device when garage door opens or closes 458 | 459 | To use this automation, you must have the Home Assistant iOS app installed on your iPhone and must have the `ios:` and `notify:` platforms in your `configuration.yaml`. 460 | 461 | Place the following in your `automations.yaml`: 462 | 463 | ``` 464 | - id: xyz 465 | alias: Notify iOS device when garage door opens or closes 466 | trigger: 467 | - platform: state 468 | entity_id: cover.garage_door_1 469 | action: 470 | - service: notify.ios_your_device_name_here 471 | data_template: 472 | message: > 473 | Garage Door 1 just changed from {{ trigger.from_state.state }} to {{ trigger.to_state.state }}! 474 | ``` 475 | 476 | Replace `notify.ios_your_device_name_here` with the name assigned to your device by Home Assistant (for example, if Home Assistant knows your device as "steves_iphone", your notify statement would be: `notify.ios_steves_iphone`. 477 | 478 | Be sure to replace `cover.garage_door_1` if the name of your garage door in Home Assistant is different. 479 | 480 | If you wish to notify only when the garage door either opens _or_ closes, add a `to` statement to the trigger following `entity_id` and specify either "open" or "closed", e.g.: 481 | 482 | ``` 483 | - id: xyz 484 | alias: Notify iOS device when garage door opens 485 | trigger: 486 | - platform: state 487 | entity_id: cover.garage_door_1 488 | to: "open" 489 | action: 490 | - service: notify.ios_your_device_name_here 491 | data_template: 492 | message: "Garage Door 1 just opened!" 493 | ``` 494 | 495 | #### Notify iOS device when garage door is open for longer than 30 minutes, with an actionable notification 496 | 497 | An actionable notification will raise a notification on your iPhone with a "Close Garage" button allowing you to close the garage door directly from the notification (i.e. without needing to open the Home Assistant app). 498 | 499 | To use this automation, you must have the Home Assistant iOS app installed on your iPhone. You must also have the `notify:` platform in your `configuration.yaml`. 500 | 501 | To the `ios:` platform in `configuration.yaml`, add: 502 | 503 | ``` 504 | ios: 505 | push: 506 | categories: 507 | - name: Garage 508 | identifier: "GARAGE" 509 | actions: 510 | - identifier: "CLOSE_GARAGE" 511 | title: "Close Garage" 512 | activationMode: "background" 513 | authenticationRequired: no 514 | destructive: yes 515 | behavior: "default" 516 | ``` 517 | 518 | If you want to be required to authenticate (i.e. enter your passcode or unlock with TouchID) before being able to access the "Close Garage" button, change `authenticationRequired: no` to `authenticationRequired: yes`. 519 | 520 | You will also need to add the following two automations to your `automations.yaml`: 521 | 522 | ``` 523 | - id: xyz 524 | alias: Notify iOS device when garage door has been open for 30 minutes 525 | trigger: 526 | - platform: state 527 | entity_id: cover.garage_door_1 528 | to: "open" 529 | for: 530 | minutes: 30 531 | action: 532 | - service: notify.ios_your_device_name_here 533 | data: 534 | message: "Garage Door 1 has been open for 30 minutes!" 535 | data: 536 | push: 537 | category: "GARAGE" 538 | 539 | - id: xyz 540 | alias: Close garage when triggered from iOS notification 541 | trigger: 542 | - platform: event 543 | event_type: ios.notification_action_fired 544 | event_data: 545 | actionName: CLOSE_GARAGE 546 | action: 547 | - service: cover.close_cover 548 | entity_id: cover.garage_door_1 549 | ``` 550 | 551 | In the first automation, replace `notify.ios_your_device_name_here` with the name assigned to your device by Home Assistant (for example, if Home Assistant knows your device as "steves_iphone", your notify statement would be: `notify.ios_steves_iphone`. 552 | 553 | Of course, you can replace `30` with any length of time in minutes you wish. 554 | 555 | In both automations, be sure to replace `cover.garage_door_1` if the name of your garage door in Home Assistant is different. 556 | 557 | Restart Home Assistant for the configuration and automation changes to take effect. 558 | 559 | Finally: 560 | 1. Open the Home Assistant app on your iPhone. 561 | 2. Tap the gear icon in the bottom right corner of the screen. 562 | 3. Scroll down and tap "Notification Settings". 563 | 4. Tap "Update push settings". 564 | 5. You should receive the message: "Settings Imported". Tap "OK". 565 | 566 | Now, when your garage door has been open for 30 minutes, you will receive a Home Assistant notification saying "Garage Door 1 has been open for 30 minutes!", and the notification will contain a button labelled "Close Garage" which will close your garage when tapped. 567 | 568 | 569 | ## Configuring OpenHAB 570 | 571 | _Forthcoming. Please submit a pull request with a working OpenHAB configuration if you can assist!_ 572 | 573 | 574 | ## Contributing 575 | 576 | Fork this repository and submit a pull request. 577 | 578 | 579 | ## Issues/Bug Reports/Feature Requests 580 | 581 | Please open an issue in this repository and describe your issue in as much detail as possible with the steps necessary to replicate the bug. It will also be helpful to include the content of your config.h in code tags and indicate whether (and how) you modified GarHAge.ino. 582 | 583 | Please also request new features via issues! 584 | --------------------------------------------------------------------------------