I was not confident that the little Zigbee wireless contact sensors were consistent enough for alerting me of perimeter doors opening and closing, so I bought a NodeMCU and some Normally Closed contact sensors, and added some simple pin monitoring to alert Home Assistant via MQTT that either my gate was opened or my garage door was opened.
It really can’t be simpler. The code also could be cleaned up, but it works. I know it’s not the best form, but I’m not concerned so long as it works.
Update 2022-05-11: My original sketch didn’t use authentication, because when I wrote it, Home Assistant’s MQTT broker didn’t require it. Since then, the MQTT broker update “broke” my connections, because now username/password is required. I fixed it with the three lines in RED below.
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "MyWifiSSID";
const char* password = "myWifiPassword";
const char* mqtt_server = "homeassistant.local";
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
#define MQTT_USER "yourmqttuser"
#define MQTT_PASSWORD "yourmqttpass"
char msg[MSG_BUFFER_SIZE];
int value = 0;
String hostname = "Nodemcu1";
void setup_wifi() {
delay(5);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
//WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
WiFi.setHostname(hostname.c_str());
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
/*
* This is what receives MQTT.
* Fetch the 433 code payload and send it using the transmitter
*/
void callback(char* topic, byte* payload, unsigned int length) {
String strFromHa;
int intFromHa;
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
//comes through as an array
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
strFromHa += (char)payload[i];
}
//convert to INT
//intFromHa = strFromHa.toInt();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
//if (client.connect(clientId.c_str())) {
if (client.connect(clientId.c_str(), MQTT_USER, MQTT_PASSWORD)) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopicContact", "hello world");
// ... and resubscribe
client.subscribe("inTopicContact");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(D5, INPUT_PULLUP);
pinMode(D6, INPUT_PULLUP);
//mqtt
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
//mqtt
void sendMqtt(int intSensorNum) {
//snprintf (msg, MSG_BUFFER_SIZE, "D5 #%ld", intSensorNum);
//I think the %ld = end of line
snprintf (msg, MSG_BUFFER_SIZE, "#%ld", intSensorNum);
Serial.print("Publish message: ");
Serial.println(msg);
client.publish("outTopicContact", msg);
}
int intSecsOpen = 0;
int intSecsOpen5 = 0;
int intSecsOpen6 = 0;
void loop() {
Serial.println("-----Waiting for Input----- " + String(intSecsOpen));
//D5 - Blue Wire - garage door
boolean btnPressed5 = !digitalRead(D5);
if(btnPressed5 == false) {
Serial.println("Door is open on D5.");
intSecsOpen5++;
//Only send alert when first detected.
if (intSecsOpen5 == 1) {
sendMqtt(50);
}
}
//If it's been open for more than a second, monitor for it closing
if (intSecsOpen5 > 0) {
if(btnPressed5 == true) {
//Once it's closed, reset the seconds open timer.
//Or if it's been open for an hour - not sure if I'll add this.
intSecsOpen5 = 0;
sendMqtt(51);
Serial.println("-----Door is closed on D5.-----");
}
}
//D6 - Orange Wire - back gate
boolean btnPressed6 = !digitalRead(D6);
if(btnPressed6 == false) {
Serial.println("Door is open on D6.");
intSecsOpen6++;
//Only send alert when first detected.
if (intSecsOpen6 == 1) {
sendMqtt(60);
}
}
//If it's been open for more than a second, monitor for it closing
if (intSecsOpen6 > 0) {
if(btnPressed6 == true) {
//Once it's closed, reset the seconds open timer.
//Or if it's been open for an hour - not sure if I'll add this.
intSecsOpen6 = 0;
sendMqtt(61);
Serial.println("-----Door is closed on D6.-----");
}
}
//mqtt
if (!client.connected()) {
reconnect();
}
client.loop();
client.publish("X", "online"); //adding this to keep it occupied.
delay(1000);
//send a keep alive message every 5 minutes also helps with troubleshooting
if (intSecsOpen == 300) {
sendMqtt(99);
intSecsOpen = 0;
}
intSecsOpen++;
}
Note: I had to add the client.publish line and the “keep alive” lines, because the unit kept losing the MQTT connection.
Upload the INO, wire it up, then go to Home Assistant > Configuration > Devices > MQTT Configuration > then listen to topic: outTopicContact
Then set up an Automation for Open and Closed, using the trigger MQTT and the payload to distinguish between the sensors. Payload of #50 = garage open; #51 = garage closed.
I feel that it’s necessary to document the simplest of setups, because it’s not clear anywhere else. Here’s a list of little nuggets of info to make setup easier.
It’s pretty fun to be able to control the Google Home Mini with voice, but I’d rather not sacrifice privacy for convenience, so I switched off the microphone. Now the Minis are just speakers I can use with Home Assistant – still pretty cool.
I don’t think there’s a way around installing the Google Home app in order to set these up. So first, set up your Google Home Mini in the Google Home app.
Then in Home Assistant, go to Configuration > Devices & Services > ADD INTEGRATION, and add the Google Cast integration. It will find your device(s) automatically.
I had two Minis, and I set up a Group that still doesn’t work called ggg.
I wanted to add a button to my dashboard to play Radio Paradise, so I created a Helper button. Then I created an Automation for when the button is pressed and added a Call Service that allows me to add the Entity and the URL for Radio Paradise.
Additionally, I wanted a Text to Speech notification when my front door motion sensor went off, so I added the TTS action to my Front Door Motion trigger:
Typical of the Home Assistant documentation, there’s no information on what the Cache field does. One presumes that it aligns with cache: “Allow TTS to cache voice file to local storage”, where the default is true. So what happens is that the first time it’s run, Home Assistant generates an MP3 file of a voice reading the text that’s stored in the config/tts directory – in this case, it’s Google’s voice.
The out of the box media player “card” doesn’t have a volume slider – fun, right? Apparently, there’s a Universal Media Player that you can configure to include a volume control, but I was unable to figure out how to do it [time spent: 1hr].
Another option is adding this slider (slider-entity-row), which also requires HACS. I didn’t try this one.
I thought it was a HACS integration, but as it turns out, it is not; it’s a Frontend. So the steps are:
Install HACS (Oh, and if you don’t have Terminal installed, you need to do that first.).
2. Reboot Home Assistant.
3. Open HACS, and click Frontend – it is not an Integration.
4. Search for Mini Media Player, and install it.
5. Try to add it.
6. Ponder why this error displays. This isn’t my first time installing HACS, so I wasn’t surprised.
Note: I later found that you can fix this error by adding the following lines to your configuration.yaml file:
7. Download it.
I expected to find this new card in the Edit Dashboard > ADD CARD, but it wasn’t there. I rebooted Home Assistant. It’s still not there. I can see it’s installed in HACS > Frontend. I’m now trying to find out what to do next. The instructions are no help at all – it shows how to manually install it, but nothing about HACS. Do I manually install it now?
8. Looking at the Simple Install instructions, I guess I have to copy the mini-media-player-bundle.js to /config/www then go to Configuration > Dashboards > ADD DASHBOARD. The Simple Install instructions do not work.
9. OK, there’s the YAML Mode installation instructions. Let’s try that! I added the following to configuration.yaml:
10. And rebooted Home Assistant. And it’s still not there.
11. Go to browser settings > Delete Cache.
Simple but poorly documented.
So after about 2 hours of fumbling in the dark, I finally was able to add a volume control to the media player. I also inadvertently deleted not just my cache but all my saved passwords as well.
So the moral of the story: don’t install it via HACS. Instead, download the JS (follow the Simple Install instructions) and put it in your config/www directory.
How to Take an Automation Snapshot with Your Camera
Apparently, this doesn’t actually send the image; instead, it sends the URL to the image, so it’s only visible if you’re on your network.
Create a Notifier Group
Instead of selecting each individual device to notify, using the Home Assistant app, you can create a notification group. I found it difficult to find the device name to use, and eventually found it by editing an Automation.
Note that you need to prefix “mobile_app_” to each of the names in your Notify Group.
Also note that some Apple updates can rename your phone to something arbitrary like iPhone (44), and you’ll need to rename it back to the original name on the phone in Settings > General > About > Name.
Save, check configuration, and restart. Now you have the all_devices notification group to choose when you set up future Automations.
There’s a lot more you can do with notifications. I have yet been able to send an image in the notification, such as a snapshot from a camera where motion was detected.
Add System Monitoring
Watched this Youtube that recommended adding System Monitoring, so I did it. It requires you to add lines to your configuration.yaml.
I can’t get the processor_temperature to work – maybe my Dell Wyse doesn’t provide it.
Install Sage by Hughes Zigbee Doorbell Sensor
My doorbell terminals were reversed from most of what I see on the Internet. I did not turn off power to the house, because there’s no power going to the doorbell until the door bell is pressed – and it’s only 20VAC anyway, so it’s not terribly dangerous to work with. (Answers the question: Do I need to turn off power to install the Sage by Hughes Zigbee door bell sensor? – Not necessary, no. But if you are a nervous Nelly, sure, why not?)
After wiring it up, I removed the battery blocker to enable the Sage, and there was no button to press for pairing. I went to Configuration > ZHA > ADD DEVICE, and it paired automatically.
The battery shows as zero percent in Home Assistant, although I checked the battery and it’s fully charged. Also, the device is activated when the “Secondary” bell is pressed – that took some trial and error.
Overall, this was really easy to install and set up. The lithium battery it comes with should last for years.
Resume Playing Media After TTS Announcement Using Variables
I’m not sure why there are integrations for variables, such as hass-variables and home-assistant-variables. I mention them here, because after reading through how to set variables for media stream and volume, I saw two different posts saying that Home Assistant doesn’t natively have variables. However, when I added variables to my automation, they worked just fine. So the following steps allowed me to add the stream URL and volume variables to and automation, which I used to resume a media stream and reset the volume.
I’m using a Google Home device to stream Internet radio stations. Below is an example of my media player attributes (found in Developer Tools > media_player.mydevicename).
The attributes I’m interested in are media_content_id and maybe the volume_level. I want an automation to play the Text to Speech notification, then resume playing what it was playing before.
Up to this point, I’ve mostly avoided having to directly edit the automations.yaml, but this automation requires it. In pseudo-code:
Trigger: When doorbell is pressed.
Actions:
Get the currently playing stream and volume of the Google Home device and make them variables
Set the volume of the Google Home to 50%
Wait a second (because it takes a second to set volume)
Play the TTS message: Someone rang the front door bell
Wait 3 seconds
Set the volume of the Google home back to what it was before
Continue streaming
This took a few trial and errors, but I was able to get it running. Below is the YAML:
Now that I have some weeks of configuring automations in the UI, I can see how the YAML reflects those fields, and I now have a better understanding of how to write the YAML. I use the File Editor to check my syntax, and I watch for errors when reloading the automations. So it took about 6 weeks of significant time investment to get to a point where YAML is not quite as intimidating as it was before.
I still have yet to figure out how this YAML works. Maybe it’s a script? I tried it and HA didn’t like sequence.
Like most things with Home Assistant, much of the documentation is there, while key details are missing or presumed knowledge. While I was able to get this to work, I’m not confident that it works as well as a natively compatible camera. The stream is acceptable, but my weaker cameras tend to pause – probably due to a poor connection. I was able to get a live stream on the dashboard after I began editing my own dashboards – and not relying on Home Assistant to do it for me.
Install the docker-wyze-bridge add-on: go to Github, scoll down to and click ADD REPOSITORY.
Open the repository in your Home Assistant, and add it.
Don’t Start it yet!
Click the Configuration tab, and enter your Wyze email and password. Leave everything else as default; I had to set the RTSP_READTIMEOUT when setting up the config. From what I can understand, the RTSP_READTIMEOUT is the time allowed for the cameras to drop offline before the integration considers them unavailable. I had a camera with a weak connection, so I set this to 10 seconds to avoid losing the feed (actually I lose the feed, but at least the image remains, giving me the illusion that it’s still running).
Click the Info tab and Start.
Now go to your Overview page, and you will see the video stills. Your stream isn’t yet set up.
Go back to the Docker Wyze Bridge add on, and click the Log tab to find your camera stream names. You should see something like this:
2022/03/07 11:35:07 [RTSP][FRONTDOOR] ? '/frontdoor' stream is UP! (3/3)
2022/03/07 11:35:08 [RTSP][BACKDOOR] ? '/backdoor' stream is UP! (3/3)
2022/03/07 11:35:08 [RTSP][GARAGE] ? '/garage' stream is UP! (3/3)
If you don’t see your cameras, you may need to Restart the add on.
Note your camera names here – they are not quite the same as what you named them in your Wyze app, nor are the the same as displayed in your router. This detail alone took many trial and errors before I found that I could just look it up in the add on the docker-wyze-bridge add in Log tab
Check the Video Stream with VLC
To check to see if they’re working, install VLC Media Player, then open it > Media > Open Network Stream, enter the rtsp stream address, and click Play. Note the IP address is the address of your Home Assistant, and not the IP of the camera.
You should see your video stream there. If not, troubleshoot the stream before trying to add it to your Home Assistant.
Add Stream to Configuration.YAML
Once you have a functioning stream, use the HA File Editor to edit your configuration.yaml, and add the following:
After restarting Home Assistant, you may need to restart the docker-wyze-bridge add-on too. You should now see your streaming cameras on your dashboard.
Make it Live on Dashboard
Edit your dashboard, and click EDIT on the camera card.
Select live for the Camera View.
I can see that the cameras with the worst connection tend to time out, so I may need to buy a WiFi repeater.
Next: Which Camera Platform Works Best
This post discusses the different platforms and what worked best for him/her.
I tried it with FFMPEG, but the stream kept freezing and going Unavailable. To try FFMPEG, add this to the configuration.yaml: