added code to handle disconnect, as in when the device is not powered for any reason

This commit is contained in:
Peter Molnar 2020-10-26 10:46:02 +00:00
parent 7bfb98cabf
commit 65caf803ed

235
plugin.py
View file

@ -28,25 +28,35 @@ __email__ = "mail@petermolnar.net"
import Domoticz import Domoticz
import json import json
class BasePlugin: class BasePlugin:
httpConn = None httpConn = None
runAgain = 6 oustandingPings = 0
disconnectCount = 0 connectRetry = 3
def __init__(self): def __init__(self):
return return
def query_status(self, Connection):
Connection.Send(
{
"Verb": "POST",
"URL": "/zeroconf/info",
"Headers": {"Content-Type": "application/json"},
"Data": json.dumps({"data": ""}),
}
)
def onStart(self): def onStart(self):
if Parameters["Mode6"] != "0": if Parameters["Mode6"] != "0":
Domoticz.Debugging(int(Parameters["Mode6"])) Domoticz.Debugging(int(Parameters["Mode6"]))
DumpConfigToLog()
self.httpConn = Domoticz.Connection( self.httpConn = Domoticz.Connection(
Name= Parameters["Address"], Name=Parameters["Address"],
Transport="TCP/IP", Transport="TCP/IP",
Protocol="HTTP", Protocol="HTTP",
Address=Parameters["Address"], Address=Parameters["Address"],
Port=Parameters["Mode1"] Port=Parameters["Mode1"],
) )
self.httpConn.Connect() self.httpConn.Connect()
@ -54,144 +64,205 @@ class BasePlugin:
Domoticz.Log("onStop - Plugin is stopping.") Domoticz.Log("onStop - Plugin is stopping.")
def onConnect(self, Connection, Status, Description): def onConnect(self, Connection, Status, Description):
if (Status == 0): if Status == 0:
Domoticz.Debug("Connected to Sonoff DIY interface") Domoticz.Debug("Connected to Sonoff DIY interface")
Connection.Send({ self.query_status(Connection)
'Verb' : 'POST',
'URL' : '/zeroconf/info',
'Headers' : {
'Content-Type': 'application/json'
},
'Data': json.dumps({
'data': ''
})
})
else: else:
Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["Address"]+":"+Parameters["Mode1"]+" with error: "+Description) Domoticz.Log(
"Failed to connect ("
+ str(Status)
+ ") to: "
+ Parameters["Address"]
+ ":"
+ Parameters["Mode1"]
+ " with error: "
+ Description
)
def onMessage(self, Connection, Data): def onMessage(self, Connection, Data):
strData = Data["Data"].decode("utf-8", "ignore")
Status = int(Data["Status"])
try: try:
strData = Data["Data"].decode("utf-8", "ignore")
strData = json.loads(strData) strData = json.loads(strData)
Domoticz.Log("Parsed JSON:" + json.dumps(strData)) # Status = int(Data["Status"])
if "data" in strData: Domoticz.Debug("Parsed JSON:" + json.dumps(strData))
if (len(Devices) == 0) and "deviceid" in strData["data"]: self.oustandingPings = self.oustandingPings - 1
Domoticz.Device(Name=strData["data"]["deviceid"], Unit=1, Type=244, Subtype=73, Switchtype=7).Create()
if "switch" in strData["data"] and "brightness" in strData["data"]:
n_value = 1 if strData["data"]["switch"] == 'on' else 0
s_value = str(strData["data"]["brightness"])
Devices[1].Update(
nValue=n_value,
sValue=s_value
#SignalLevel=strData["data"]["signalStrength"],
#BatteryLevel=100
)
except: except:
Domoticz.Log("Failed to parse response as JSON" + strData) Domoticz.Error("Failed to parse response as JSON" + strData)
return return
if "data" not in strData:
Domoticz.Debug("`data` missing from Parsed JSON")
return
data = strData["data"]
# skip these messages, they are not status updates
if "deviceid" not in data:
Domoticz.Debug("`deviceid` missing from Parsed JSON")
return
# create new devices if the don't exist just yet
existing_devices = [d.DeviceID for d in Devices.values()]
if data["deviceid"] not in existing_devices:
# I guess brightness is only present in a dimmer
# I could be wrong
if "brightness" in data:
Domoticz.Device(
Name=data["deviceid"],
Unit=1,
Type=244,
Subtype=73,
Switchtype=7,
DeviceID=data["deviceid"],
).Create()
# now the device certainly exists, so find it
device = None
for index, d in Devices.items():
if data["deviceid"] == d.DeviceID:
device = Devices[index]
if not device:
Domoticz.Error("something is wrong: the device was not found?!")
return
if "switch" in data and "brightness" in data:
if data["switch"] == "on":
n_value = 1
else:
n_value = 0
s_value = str(data["brightness"])
device.Update(
nValue=n_value,
sValue=s_value,
SignalLevel=int((1 / (-1 * data["signalStrength"])) * 100),
BatteryLevel=100,
)
def onCommand(self, Unit, Command, Level, Hue): def onCommand(self, Unit, Command, Level, Hue):
Domoticz.Debug("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level)) Domoticz.Debug(
if "Off" == Command or "On" == Command: "onCommand called for Unit "
+ str(Unit)
+ ": Parameter '"
+ str(Command)
+ "', Level: "
+ str(Level)
)
# in case of 'on' and 'off', the dimmable endpoint doesn't seem to respect the switch status
if Command.lower() in ["off", "on"]:
url = "/zeroconf/switch" url = "/zeroconf/switch"
data = { data = {"switch": Command.lower()}
"switch": Command.lower()
}
else: else:
url = "/zeroconf/dimmable" url = "/zeroconf/dimmable"
if Level > 0: if Level > 0:
switch = "on" switch = "on"
else: else:
switch = "off" switch = "off"
data = { data = {"switch": switch, "brightness": Level}
"switch": switch, self.httpConn.Send(
"brightness": Level {
"Verb": "POST",
"URL": url,
"Headers": {"Content-Type": "application/json"},
"Data": json.dumps({"deviceid": "", "data": data}),
} }
self.httpConn.Send({ )
'Verb' : 'POST',
'URL' : url,
'Headers' : {
'Content-Type': 'application/json'
},
'Data': json.dumps({
"deviceid": "",
"data": data
})
})
def onDisconnect(self, Connection): def onDisconnect(self, Connection):
Domoticz.Log("onDisconnect called for connection to: "+Connection.Address+":"+Connection.Port) Domoticz.Log(
"onDisconnect called for connection to: "
+ Connection.Address
+ ":"
+ Connection.Port
)
def onHeartbeat(self): def onHeartbeat(self):
if (self.httpConn != None and (self.httpConn.Connecting() or self.httpConn.Connected())): try:
Domoticz.Debug("onHeartbeat called, Connection is alive.") if self.httpConn and self.httpConn.Connected():
self.httpConn.Send({ self.oustandingPings = self.oustandingPings + 1
'Verb' : 'POST', if self.oustandingPings > 6:
'URL' : '/zeroconf/info', Domoticz.Log(
'Headers' : { "Too many outstanding connection issues forcing disconnect."
'Content-Type': 'application/json' )
}, self.httpConn.Disconnect()
'Data': json.dumps({ self.nextConnect = 0
'data': '' else:
}) self.query_status(self.httpConn)
})
else:
self.runAgain = self.runAgain - 1
if self.runAgain <= 0:
if (self.httpConn == None):
self.onStart()
self.runAgain = 6
else: else:
Domoticz.Debug("onHeartbeat called, connection is dead") # if not connected try and reconnected every 3 heartbeats
self.oustandingPings = 0
self.nextConnect = self.nextConnect - 1
if self.nextConnect <= 0:
self.nextConnect = 3
self.httpConn.Connect()
return True
except:
Domoticz.Log(
"Unhandled exception in onHeartbeat, forcing disconnect."
)
self.onDisconnect(self.httpConn)
self.httpConn = None
global _plugin global _plugin
_plugin = BasePlugin() _plugin = BasePlugin()
def onStart(): def onStart():
global _plugin global _plugin
_plugin.onStart() _plugin.onStart()
def onStop(): def onStop():
global _plugin global _plugin
_plugin.onStop() _plugin.onStop()
def onConnect(Connection, Status, Description): def onConnect(Connection, Status, Description):
global _plugin global _plugin
_plugin.onConnect(Connection, Status, Description) _plugin.onConnect(Connection, Status, Description)
def onMessage(Connection, Data): def onMessage(Connection, Data):
global _plugin global _plugin
_plugin.onMessage(Connection, Data) _plugin.onMessage(Connection, Data)
def onCommand(Unit, Command, Level, Hue): def onCommand(Unit, Command, Level, Hue):
global _plugin global _plugin
_plugin.onCommand(Unit, Command, Level, Hue) _plugin.onCommand(Unit, Command, Level, Hue)
def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile): def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
global _plugin global _plugin
_plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile) _plugin.onNotification(
Name, Subject, Text, Status, Priority, Sound, ImageFile
)
def onDisconnect(Connection): def onDisconnect(Connection):
global _plugin global _plugin
_plugin.onDisconnect(Connection) _plugin.onDisconnect(Connection)
def onHeartbeat(): def onHeartbeat():
global _plugin global _plugin
_plugin.onHeartbeat() _plugin.onHeartbeat()
# Generic helper functions # Generic helper functions
def LogMessage(Message): def LogMessage(Message):
if Parameters["Mode6"] == "File": if Parameters["Mode6"] == "File":
f = open(Parameters["HomeFolder"]+"http.html","w") f = open(Parameters["HomeFolder"] + "http.html", "w")
f.write(Message) f.write(Message)
f.close() f.close()
Domoticz.Log("File written") Domoticz.Log("File written")
def DumpConfigToLog(): def DumpConfigToLog():
for x in Parameters: for x in Parameters:
if Parameters[x] != "": if Parameters[x] != "":
Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'") Domoticz.Debug("'" + x + "':'" + str(Parameters[x]) + "'")
Domoticz.Debug("Device count: " + str(len(Devices))) Domoticz.Debug("Device count: " + str(len(Devices)))
for x in Devices: for x in Devices:
Domoticz.Debug("Device: " + str(x) + " - " + str(Devices[x])) Domoticz.Debug("Device: " + str(x) + " - " + str(Devices[x]))
@ -202,18 +273,24 @@ def DumpConfigToLog():
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel)) Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
return return
def DumpHTTPResponseToLog(httpResp, level=0): def DumpHTTPResponseToLog(httpResp, level=0):
if (level==0): Domoticz.Debug("HTTP Details ("+str(len(httpResp))+"):") if level == 0:
Domoticz.Debug("HTTP Details (" + str(len(httpResp)) + "):")
indentStr = "" indentStr = ""
for x in range(level): for x in range(level):
indentStr += "----" indentStr += "----"
if isinstance(httpResp, dict): if isinstance(httpResp, dict):
for x in httpResp: for x in httpResp:
if not isinstance(httpResp[x], dict) and not isinstance(httpResp[x], list): if not isinstance(httpResp[x], dict) and not isinstance(
Domoticz.Debug(indentStr + ">'" + x + "':'" + str(httpResp[x]) + "'") httpResp[x], list
):
Domoticz.Debug(
indentStr + ">'" + x + "':'" + str(httpResp[x]) + "'"
)
else: else:
Domoticz.Debug(indentStr + ">'" + x + "':") Domoticz.Debug(indentStr + ">'" + x + "':")
DumpHTTPResponseToLog(httpResp[x], level+1) DumpHTTPResponseToLog(httpResp[x], level + 1)
elif isinstance(httpResp, list): elif isinstance(httpResp, list):
for x in httpResp: for x in httpResp:
Domoticz.Debug(indentStr + "['" + x + "']") Domoticz.Debug(indentStr + "['" + x + "']")