Sorry. I appear to have missed these posts and questions. Here is the code as requested. Its VERY messy but has been running for over a year without issue. You will need to add your MQTT info and create MQTT sensors/buttons in HA to control it.
import socket
from queue import Queue
from threading import Thread
import time
import json
from datetime import datetime
import paho.mqtt.client as mqtt
def connectMQTT():
server="FIXME
t = 1833
user = "FIXME
passwd = "FIXME
rc=-1
while rc!=0:
client = mqtt.Client()
client.username_pw_set(user,passwd)
client.on_message=on_message
client.on_disconnect=on_disconnect
client.on_connect=on_connect
client.connect(server)
test=client.publish("foo/bar","test")
rc = test.rc
log("rc="+str(test.rc))
log("mid="+str(test.mid))
log("published="+str(test.is_published()))
if rc !=0:
log("Unable to publish. Retrying in 5 seconds....")
time.sleep(5)
else:
log("Connected!")
return client
def pp(c,topic,payload):
rc=-1
fails=0
totalfails=0
while rc!=0:
if fails > 5:
fails=0
c=connectMQTT()
test=c.publish(topic,payload)
rc = test.rc
if rc!=0:
log("rc="+str(test.rc))
log("mid="+str(test.mid))
log("published="+str(test.is_published()))
log("Unable to publish. Retrying in 1 seconds....")
time.sleep(1)
fails=fails+1
totalfails=totalfails+1
else:
log(topic)
log(payload)
return c
def on_connect(client, userdata, flags, rc):
client.subscribe("dog/cmd")
log("Subscribing...")
def on_disconnect(client, userdata, rc):
if rc != 0:
log("Unexpected MQTT disconnection. Will auto-reconnect")
def on_message(client, userdata, message):
topics = str(message.topic).strip().split("/")
if topics[0]=="dog":
if topics[1]=="cmd":
cmd=message.payload.decode("utf-8")
add_command(q,cmd)
return 0
def log(obj):
now = datetime.now()
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
print(dt_string + " - " + str(obj))
def add_command(q, cmd, safe=True):
shortcuts= {'DISABLE_INSIDE':'{"config":"DISABLE_INSIDE","msgId":759,"dir":"p2d"}',
'ENABLE_INSIDE':'{"config":"ENABLE_INSIDE","msgId":806,"dir":"p2d"}',
'DISABLE_OUTSIDE':'{"config":"DISABLE_OUTSIDE","msgId":809,"dir":"p2d"}',
'ENABLE_OUTSIDE':'{"config":"ENABLE_OUTSIDE","msgId":810,"dir":"p2d"}',
'OPEN_AND_HOLD':'{"cmd":"OPEN_AND_HOLD","msgId":812,"dir":"p2d"}',
'HOLD':'{"cmd":"OPEN_AND_HOLD","msgId":812,"dir":"p2d"}',
'OPEN':'{"cmd":"OPEN","msgId":799,"dir":"p2d"}',
'CLOSE':'{"cmd":"CLOSE","msgId":815,"dir":"p2d"}',
'POWER_ON':'{"config":"POWER_ON","msgId":820,"dir":"p2d"}',
'POWER_OFF':'{"config":"POWER_OFF","msgId":818,"dir":"p2d"}',
'GET_DOOR_STATUS':'{"config":"GET_DOOR_STATUS","msgId":754,"dir":"p2d"}',
'STATUS':'{"config":"GET_DOOR_STATUS","msgId":754,"dir":"p2d"}',
'GET_SETTINGS':'{"config":"GET_SETTINGS","msgId":752,"dir":"p2d"}',
'SETTINGS':'{"config":"GET_SETTINGS","msgId":752,"dir":"p2d"}',
'RESET':'RESET'}
log("Incoming Command - " + cmd)
if cmd.upper() in shortcuts:
cmd2=shortcuts[cmd.upper()]
q.put(cmd2)
if cmd.upper().startswith("ENABLE") or cmd.upper().startswith("DISABLE") or cmd.upper().startswith("POWER"):
q.put('{"config":"GET_SETTINGS","msgId":752,"dir":"p2d"}')
elif not safe and cmd:
if cmd != "":
q.put(cmd)
def commands(q):#, event):
while True:
cmd = input("")
add_command(q, cmd, False)
def logger(d,mqtt_client):
door_status=""
door_raw={}
settings={}
door_status_old=None
door_raw_old=None
settings_old=None
ping=time.time()
healthy=False
healthy_old=None
read_delay_seconds=.1
ping_heath_seconds=30
refresh_time=300
next_refresh_time=time.time()+refresh_time
while True:
if not d.empty():
telem=d.get()
#log(telem)
try:
telem_obj = json.loads(telem)
if "door_status" in telem_obj:
door_status = telem_obj['door_status']
door_raw = telem_obj
if door_status != door_status_old or json.dumps(door_raw) != json.dumps(door_raw_old):
log("Door = " + str(door_status))
mqtt_client = pp(mqtt_client,("dog/door"),door_status)
mqtt_client = pp(mqtt_client,("dog/door_detail"),json.dumps(door_raw))
next_refresh_time=time.time()+refresh_time
door_status_old=door_status
door_raw_old=door_raw
healthy=True
if "settings" in telem_obj:
settings = telem_obj['settings']
if json.dumps(settings) != json.dumps(settings_old):
log("Settings = " + str(settings))
mqtt_client = pp(mqtt_client,("dog/door_settings"),json.dumps(settings))
next_refresh_time=time.time()+refresh_time
settings_old=settings
healthy=True
if "PONG" in telem_obj:
ping = int(telem_obj['PONG'])
mqtt_client = pp(mqtt_client,("dog/door_ping"),str(ping))
healthy=True
if time.time() > next_refresh_time:
log("Refresh MQTT data...")
log("Door = " + str(door_status))
log("Settings = " + str(settings))
mqtt_client = pp(mqtt_client,("dog/door"),door_status_old)
mqtt_client = pp(mqtt_client,("dog/door_detail"),json.dumps(door_raw_old))
mqtt_client = pp(mqtt_client,("dog/door_settings"),json.dumps(settings_old))
except:
telem_obj={}
else:
time.sleep(read_delay_seconds)
def broker_connection(q,d,TCP_IP,TCP_PORT,mqtt_client):#, event):
connected=False
encoding='utf-8'
lf=b"\n"
ping_seconds=10
door_seconds=5
setting_seconds=30
error_seconds=3
read_delay_seconds=.1
while True:
try:
s.close()
except:
i=0
log("Connecting...")
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except:
connected=False
connected=True
s.settimeout(1)
s.connect((TCP_IP, TCP_PORT))
s.setblocking(0)
for c in ('{"cmd":"CANCEL_FIRMWARE_UPDATE","msgId":797,"dir":"p2d"}', '{"config":"GET_DOOR_STATUS","msgId":754,"dir":"p2d"}', '{"config":"GET_SETTINGS","msgId":752,"dir":"p2d"}'):
try:
sent=s.send(c.encode(encoding)+lf)
except:
sent=0
if sent==0:
connected=False
time.sleep(.75)
i=0
nextPing = time.time()+ping_seconds
nextDoor = time.time()+door_seconds
nextSetting = time.time()+setting_seconds
while connected:
i=i+1
full_msg = ""
while True:
try:
msg = s.recv(1).decode("utf-8")
except:
msg=""
if len(msg) <= 0:
break
full_msg += msg
if len(full_msg) > 0:
for data in full_msg.replace("}{","}±{").split("±"):
d.put(data)
else:
time.sleep(read_delay_seconds)
if not q.empty():
cmd = q.get()
if cmd=="RESET":
connected=False
else:
try:
log("Sending - "+cmd)
sent=s.send(cmd.encode(encoding)+lf)
log("Sent - "+cmd)
except:
sent=0
if sent==0:
connected=False
elif time.time()>nextDoor:
nextDoor = time.time()+door_seconds
door='{"config":"GET_DOOR_STATUS","msgId":754,"dir":"p2d"}'
try:
sent=s.send(door.encode(encoding)+lf)
except:
sent=0
if sent==0:
connected=False
elif time.time()>nextSetting:
nextSetting = time.time()+setting_seconds
sett='{"config":"GET_SETTINGS","msgId":752,"dir":"p2d"}'
try:
sent=s.send(sett.encode(encoding)+lf)
except:
sent=0
if sent==0:
connected=False
elif time.time()>nextPing:
nextPing = time.time()+ping_seconds
ping='{"PING":"'+str(round(time.time()*1000))+'","msgId":764,"dir":"p2d"}'
try:
sent=s.send(ping.encode(encoding)+lf)
except:
sent=0
if sent==0:
connected=False
time.sleep(error_seconds)
def dog_door():
mqtt_client=connectMQTT()
mqtt_client.loop_start()
myBoker = Thread(target=broker_connection, args=(q,d,'FIXME,3000,mqtt_client))
myBoker.setDaemon(True)
myBoker.start()
myLogger = Thread(target=logger, args=(d,mqtt_client,))
myLogger.setDaemon(True)
myLogger.start()
#commands(q) #Uncomment this to have interactive from the cmdline
while True:
time.sleep(1)
q = Queue()
d = Queue()
if __name__ == "__main__":
dog_door()