I updated the python daemon accordingly. It has to listen to two topics and the action/trigger topic is also different.
Hope this is helpfull for someone. I spent almost a full day on the changes and it is still not perfect. I can't get the switch or the button to use the JSON payload. I really don't know where else I can put escapes or more single or double quotes. In the end I decided to skip the JSON for commands.
Also when the daemon encounters a non-JSON command on a subscribed topic, it crashes.. The code should use some proper exception handling, but I don't wanna.
#!/usr/bin/env python# (c) 2022-04-24 S.E.Jansen.#import lybraries#import RPi.GPIO as GPIOfrom gpiozero import Button as Button
from gpiozero import DigitalInputDevice as DigitalInputDevice
from gpiozero import DigitalOutputDevice as DigitalOutputDevice
import time
import GPIO_Config
import MQTT_Config
import json
import paho.mqtt.client as paho
#setup pins
button = Button(GPIO_Config.button, pull_up=False, bounce_time=0.05,hold_time = 3, hold_repeat=False)
bel = DigitalOutputDevice(GPIO_Config.bel)
mute_state = DigitalInputDevice(GPIO_Config.mute_state, pull_up=True, bounce_time=0.05)
mute_set = DigitalOutputDevice(GPIO_Config.mute_set)
mute_reset = DigitalOutputDevice(GPIO_Config.mute_reset)
#GPIO functions and callbacksdefvisitor():#on rise of input (debounce 50ms) the doorbell has rung
print("doorbel press detected")
data = {'value':"visitor"}
client.publish(MQTT_Config.HA_name + "/device_automation/Doorbell_Button_" + GPIO_Config.ID + "/action_pressed/action",json.dumps(data),0,False)
#MQTT connection + callback functionsdefon_message(client, userdata, message):#check and handle incomming messages to:# - ring bell# - check mute status# - set bell to mute / unmute
data = message.payload
receive=data.decode("utf-8")
#print(m_decode)#print (m_decode['Server_name'])if message.topic == MQTT_Config.HA_name + "/button/Doorbell_" + GPIO_Config.ID + "/set":
#m_decode = json.loads(receive)#buttonpress = m_decode["doorbell"]
buttonpress = receive
print ("Button message received: " + buttonpress)
if buttonpress == "ring_short":
bel.blink(on_time=0.1, n=1)
if message.topic == MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/set":
#muteCommand = m_decode["mute_set"]
muteCommand = receive
print ("Command received for mute: " + muteCommand)
#switch: for if muteCommand == "ON":
print("setting mute")
mute_set.blink(on_time=0.5, n=1)
time.sleep(0.1)
data = {'mute_state':mute_state.value}
client.publish(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/state",json.dumps(data),0,True)
elif muteCommand == "OFF":
print("resetting mute")
mute_reset.blink(on_time=0.5, n=1)
time.sleep(0.1)
data = {'mute_state':mute_state.value}
client.publish(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/state",json.dumps(data),0,True)
elif muteCommand == "ENQ":
#check status of relays. B-side is inverse of "mute" and is connected to GND. #Pi is set with internal pull-up. Normal operation is mute off (bell rings normaly) #A-side is closed, B-side is open.Therefore internall pul-up gives a value of 1.#value of mute is therefore inverse of pin-value.#mute = True means silent operation. mute = False means ring!
data = {'mute_state':mute_state.value}
client.publish(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/state",json.dumps(data),0,True)
#def MQTT_AutodiscoverConfig():#send config to MQTT broker for autodiscover in Home Assistant# print("send Autoconfig message with MQTT")#init#Set callbacks:#Doorbell button
button.when_pressed = visitor
#button.when_long_press?#Mute change detect#mute_state.when_changed?#MQTT client#client_on_message = MQTT_message#client_on_connect .. etc#call back function for MQTT connectiondefon_connect(client, userdata, flags, rc):if rc==0:
client.connected_flag=True#set flag
print("connected OK")
# MQTT subscribe to command topic to receive commands (in json format)
client.subscribe(MQTT_Config.HA_name + "/button/Doorbell_" + GPIO_Config.ID + "/set",0)
print("Subscribed to: " + MQTT_Config.HA_name + "/button/Doorbell_" + GPIO_Config.ID + "/set")
client.subscribe(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/set",0)
print("Subscribed to: " + MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/set")
data = {'mute_state':mute_state.value}
client.publish(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/state",json.dumps(data),0,True)
print(json.dumps(data))
else:
print("Bad connection Returned code=",rc)
client.bad_connection_flag=True#call back function for disconnect MQTT connection to reconnect#def on_disconnect# MQTT details
paho.Client.connected_flag=False#Create flag in class
paho.Client.bad_connection_flag=False#another flag
client= paho.Client() #create client object
client.username_pw_set(MQTT_Config.user,MQTT_Config.password)
client.on_connect=on_connect #bind call back function
client.on_message= on_message #attach function to callback
client.connect(MQTT_Config.broker,MQTT_Config.port) #establish connection
client.loop_start() #start network loopwhilenot client.connected_flag andnot client.bad_connection_flag: #wait in loop
time.sleep(0.1)
if client.bad_connection_flag:
client.loop_stop() #Stop loop
sys.exit()
# MQTT Home Assistant autodiscover config# MQTT subscribe to command topic to receive commands (in json format)
client.subscribe(MQTT_Config.HA_name + "/button/Doorbell_" + GPIO_Config.ID + "/set",0)
print("Subscribed to: " + MQTT_Config.HA_name + "/button/Doorbell_" + GPIO_Config.ID + "/set")
client.subscribe(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/set",0)
print("Subscribed to: " + MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/set")
data = {'mute_state':mute_state.value}
client.publish(MQTT_Config.HA_name + "/switch/Doorbell_" + GPIO_Config.ID + "/state",json.dumps(data),0,True)
print(json.dumps(data))
#for silent testing..#print("engage silent mode")#mute_set.blink(on_time=0.5, n=1)#time.sleep(0.1)#print("mute status: ")#print(mute_state.value)whileTrue:
try:
# print("still running")
time.sleep(10)
except KeyboardInterrupt:
client.loop_stop() #stop connection loop
client.disconnect() #gracefully disconnect
client.connected_flag=False#reset flag
mute_reset.blink(on_time=0.5, n=1)
time.sleep(0.1)
print("mute status: ")
print(mute_state.value)
print("returned doorbell to normal opperation")
print("Ending program")
exit()
Now with everything connected it's time to setup the software side.
I wrote a python script that can be run as a service. This way it has automatic startup at boot and if it fails.
It subscribes to a "set" MQTT-topic for commands. It sends the actual state of the mute relay to a "state" topic. And when it senses the doorbell being rung, it sends a message to a "trigger" topic.
Ok, design is done. Now for the actual build. The Eagle board layout feature helped me to set up the components in a logical way with easy wiring. Trying to use single sided wiring as much as possible means the least crossing wires (in theory)
Which translates to the following:
And after one burned out transistor replacement (which was a pain to diagnose), I'm up and running!
Ok. So the raspberry Pi GPIO's can only handle 3.3V max. I found some solutions that use a full rectifier and then pass the result through a linear voltage regulator. I bet this works, but it feels wrong somehow.
I chose to go with only a single diode and a capacitor to rectify and dampen the signal.
Some clamping diodes to VDD and GND will make sure I'm within GND - 0,7V and VDD + 0,7V. (I don't have any schottky's laying around). And a last voltage divider with two resistors to really make sure I don't go above VDD.
First draft:
This gave me the chance to (re)learn how to do schematics and simulation. So (quite) a few evenings later:
Then I noticed the relays I had are double latching single coil (I had to google this too). It means they get stuck in ON or OFF position and need a reverse voltage to switch where they also get stuck.
This turns out to be exactly what I want for the mute function that runs in series with the bell-actuator. It just means I have to learn how to do a proper H-bridge. The relays also switch on 5V and the Pi's GPIO-pins give me 3.3V. This means PNP's (I never liked PNP's, they seem upside down somehow and give me headaches)
This also seems to work, but now I'm committed to learn something new...
So another full day of research, and an eventual help-line to someone who knows what they are talking about:
And the full schematic:
I ended up using one double latching single coil relay for the mute function. The relay has two "sides". One inverted to the other. This gives me an input for the actual status, so I can check this on start-up and check if setting or resetting has worked.
The other relays is parallel to the doorbell button to ring it. It is a "normal" active to close 5V relay that I can operate with a transistor or a button on the board.
This all means the following functionality: I can ring the doorbell with a button on the board or with a GPIO-pin output. This is handy for testing and making my neighbours think I have loads of visitors.
I can mute my doorbell and still get a signal when the doorbell button is pressed (or when I operate the ring-relay. Using the on board button OR a GPIO-pin output).
I also added connections for a DHT22 temperature and humidity sensor. Because, why not?