... it was the 'Hey' in your code that lead me to think that this 'Test' message was yours ... while it was an old greetings no one remembered
Thank you for the debug codes, the connack and suback ways to manage. The plugin is now working and automatically create sensors upon MQTT message arrival
I also noticed that when you Create a device ... the whole plugin reset if you attempt to update its values immediately.
One annoying point was related to dimmers: it took me some time to discover that if you want the dimmer to keep the value, you must update with nValue=2 (?!?!) and sValue containing the integer ... and i must confess this is a pain having to read code from others to discover this kind of subtlities (didn't found in wiki); Anyway, with this plugin, we don't anymore are in need for node-red to map topics <--> idx, so hurrah
Code: Select all
#
# neOCampus
#
# This plug-in will try to automatically instantiate sensors / actuators according
# to their types.
# neOCampus MQTT conventions: <building>/<room>/<kind>
# e.g u4/302/luminosity --> message features a unitID (kind of UUID) along with a subID (i2c addr)
#
# Based on:
# Python Plugin MQTT Subscribe Example
# Author: Dnpwwo
#
# Notes:
# - Switch Subtype (e.g DIMMER=7) --> domoticz/main/RFXNames.h
#
# Dr Thiebolt F. Apr.18 initial release
#
"""
<plugin key="MQTTinventory" name="MQTT inventory for neOCapmpus" author="casimir" version="0.0.1" externallink="http://neocampus.univ-tlse3.fr/">
<params>
<param field="Address" label="IP Address" width="200px" required="true" default="neocampus.univ-tlse3.fr"/>
<param field="Port" label="Connection" required="true" width="200px" default="1883"/>
<param field="Username" label="Username" width="200px"/>
<param field="Password" label="Password" width="200px"/>
<param field="Mode1" label="Topic" width="125px" default="TestTopic/#"/>
<param field="Mode6" label="Debug" width="150px">
<options>
<option label="None" value="0" default="true" />
<option label="Python Only" value="2"/>
<option label="Basic Debugging" value="62"/>
<option label="Basic+Messages" value="126"/>
<option label="Connections Only" value="16"/>
<option label="Connections+Queue" value="144"/>
<option label="All" value="-1"/>
</options>
</param>
</params>
</plugin>
"""
#
# Import zone
#
#
import Domoticz
import time
import json
from datetime import datetime
import threading
# Extend Domoticz's python modules search paths with sites packages
import os
import sys
import site
_path2add=site.getsitepackages()
for _p in _path2add:
if (os.path.exists(_p) and not os.path.abspath(_p) in sys.path):
sys.path.append(os.path.abspath(_p))
#
# Variables
#
#
NEOCAMPUS_SENSORS_TYPES = [ "temperature", "co2", "luminosity", "hygro", "humidity", "energy", "digital", "noise" ]
NEOCAMPUS_ACTUATORS_TYPES = [ "lighting", "shutter", "display" ]
#
# Plugin class
#
#
class BasePlugin:
# Domoticz attributes
enabled = True
mqttConn = None
# attributes
DEFL_HEARTBEAT = 20
DEFL_MQTT_KEEPALIVE = 60
DOMOTICZ_MAXUNITS = 255 # Units range from 1 to 255
NEOCAMPUS_TESTTOPIC = 'TestTopic' # neOCampus sandbox
NOISE_THRESHOLD_MAX = 1000 # maximum value for noise threshold (pulses counts over scan_window)
# [apr.18] noise sensors emit messages whenever noise is
# detected ... and stay quiet when ambiant is calm, thus
# we need a kind of timer to cooldown the noise indicator
heartBeatNoiseProcessing = None # Noise management through heartBeat
heartBeatNoiseProcessingLock = None
_heartBeat = None
_heartBeatCnt = 0
_connack = False
_suback = False
_shutdown = False
_devicesNamesDict = None
_availableUnits = None
def __init__(self):
self._heartBeat = __class__.DEFL_HEARTBEAT
self._availableUnits = list(range( 1, __class__.DOMOTICZ_MAXUNITS+1 ))
self._devicesNamesDict = dict()
self.heartBeatNoiseProcessing = list()
self.heartBeatNoiseProcessingLock = threading.Lock()
def onStart(self):
Domoticz.Log("onStart called ...")
# Debug mode ?
if Parameters["Mode6"] != "0":
try:
Domoticz.Debugging(int(Parameters["Mode6"]))
except Exception as ex:
Domoticz.Debugging(1)
# parse existing devices
if len(Devices):
Domoticz.Debug("Devices count = %d" % len(Devices))
# parse existing devices
self._parseDevices()
Domoticz.Debug("Currently registered devices (Name:idx) :")
DumpDictionaryToLog( self._devicesNamesDict )
Domoticz.Debug("Currently available units numbers :" +str(self._availableUnits) )
# change heartbeat
if self._heartBeat is None or not isinstance(self._heartBeat,int):
Domoticz.Debug("\tDomoticz HeartBeat as default ... (10s)")
else:
Domoticz.Heartbeat(self._heartBeat)
Domoticz.Debug("\tHeartbeat set to %d" % self._heartBeat)
self._shutdown = False
self._connack = False
self._suback = False
self._heartBeatCnt = 0
DumpConfigToLog()
self.mqttConn = Domoticz.Connection(Name="MQTT neOCampus", Transport="TCP/IP", Protocol="MQTT", Address=Parameters["Address"], Port=Parameters["Port"])
self.mqttConn.Connect()
def onStop(self):
Domoticz.Debug("onStop called ...")
self._shutdown = True
if (self.mqttConn.Connected()):
# unsubscribe
Domoticz.Debug("\tunsubscribe ...")
self.mqttConn.Send({'Verb' : 'UNSUBSCRIBE', 'Topics': [Parameters["Mode1"]]})
# no need to wait for confirmation
self._suback = False
Domoticz.Debug("\tdisconnect ...")
self.mqttConn.Send({ 'Verb' : 'DISCONNECT' })
# no need to wait for confirmation
self._connack = False
def onConnect(self, Connection, Status, Description):
Domoticz.Debug("onConnect called ...")
if self._shutdown is True: return
if (Status == 0):
# i.e TCP connexion to MQTT broker is established
Domoticz.Debug("MQTT connection is on progress ...")
# ... now we need to establish the MQTT link
# generate our MQTT clientID
_clientID = "_".join(["domoticz", Parameters['Key'], Parameters['Username']])
sendData = { 'Verb' : 'CONNECT', 'ID' : _clientID }
#sendData = { 'Verb' : 'CONNECT' } # pseudo-random clientID
Connection.Send(sendData)
# ... and now wait for a connack to start the subscription
else:
Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["Address"]+":"+Parameters["Port"]+" with error: "+Description)
Domoticz.Debug("retry to connect ...")
time.sleep(1) # to avoid too many attempts per second
self.mqttConn.Connect()
def onMessage(self, Connection, Data):
Domoticz.Log("onMessage called with: "+Data["Verb"])
DumpDictionaryToLog(Data)
if Data.get('Verb','').lower() == "pingresp":
Domoticz.Debug("ping response received :)")
return
if Data.get('Verb','').lower() == "connack":
if Data.get('Status',42) == 0:
Domoticz.Debug("connection ACK received :)")
self._connack = True
# [apr.18] only support for ONE topic to subscribe to
Domoticz.Debug("start subscription to "+ Parameters["Mode1"])
self.mqttConn.Send({'Verb' : 'SUBSCRIBE', 'Topics': [{'Topic':Parameters["Mode1"], 'QoS': 0}]})
return
else:
Domoticz.Log("connection ACK with error code: " + str(Data.get('Status',42)))
# retry to establish MQTT connection ...
time.sleep(1) # to avoid too many attempts per second
self.mqttConn.Connect()
return
if Data.get('Verb','').lower() == "suback":
#Domoticz.Debug("suback payload = " + str(Data))
# suback payload = {'Topics': [{'Status': 0, 'Description': 'Success - Maximum QoS 0'}], 'Verb': 'SUBACK', 'PacketIdentifier': 1}
# [apr.18] only support for ONE topic
if (Data['Topics'][0])['Status'] == 0:
Domoticz.Debug("subscription ACK received :)")
self._suback = True
return
else:
Domoticz.Debug("subscribe ACK with errors: " + str(Data))
return
if Data.get('Verb','').lower() == "publish":
Domoticz.Debug("message received :)")
# Test message ?
if Data['Topic'].endswith("/test") and Data['Retain'] is True:
# probably our old test message ...
Domoticz.Debug("old test message received ... discard!")
return
if Data['Topic'].endswith("/command"):
# command message
Domoticz.Debug("command message received ... discard!")
return
# ok, start to process message
self._processMQTTmessage( topic=Data['Topic'], payload=json.loads(Data.get('Payload').decode('utf-8')) )
return
# unknown message ?!?!
Domoticz.Debug("unhandled message: "+ str(Data))
def onCommand(self, Unit, Command, Level, Hue):
Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
# checks ...
if Unit not in Devices: return
_dev = Devices[ Unit ]
topic = _dev.Options.get('topic')
if topic is None or 'order' not in _dev.Options: return
if not topic.endswith('/command'): return
if Command.lower() != "set level":
Domoticz.Debug( "unknown Command = '%s'" % Command )
return
# determine kind of sensor / actuator
kindID = topic.split('/')[-2]
if kindID == 'temperature':
return self._processTemperatureCommand( _dev, topic, order=_dev.Options.get('order'), value=Level )
elif kindID == 'luminosity':
return self._processLuminosityCommand( _dev, topic, order=_dev.Options.get('order'), value=Level )
elif kindID == 'noise':
return self._processNoiseCommand( _dev, topic, order=_dev.Options.get('order'), value=Level )
else:
Domoticz.Debug("unknwown kindID = %s" % kindID)
def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
Domoticz.Log("onNotification received: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
def onDeviceModified(self, Unit):
Domoticz.Log("onDeviceModified called for Unit " + str(Unit))
def onDeviceRemoved(self, Unit):
Domoticz.Log("onDeviceRemoved called for Unit " + str(Unit))
def onDisconnect(self, Connection):
Domoticz.Log("onDisconnect called ...")
if self._shutdown is True: return
# TODO:trying to guess
Domoticz.Debug("Connection: " + str(Connection))
# [apr.18] there's no (simple) way to know if this is a regular disconnect (e.g user disable action)
# or disconnect due to network outage for example.
# Anyway, reconnect will silently fails is device is disabled ... or retry to connect otherwise
self._connack = False
self._suback = False
Domoticz.Debug("\ttrying to reconnect ...")
time.sleep(1) # to avoid too many attempts per second
self.mqttConn.Connect()
def onHeartbeat(self):
Domoticz.Log("onHeartbeat called ...")
if self._shutdown is True: return
if (self.mqttConn.Connected()):
# manage MQTT keep alive (if nothing has been sent)
if (self._heartBeatCnt % (__class__.DEFL_MQTT_KEEPALIVE / self._heartBeat) ) == 0:
Domoticz.Debug("... sending PINGREQ to MQTT broker ...")
self.mqttConn.Send({ 'Verb' : 'PING' })
self._heartBeatCnt += 1
# manage Noise cooldown ...
_now = datetime.now()
with self.heartBeatNoiseProcessingLock:
for devUnit in self.heartBeatNoiseProcessing:
if devUnit not in Devices:
self.heartBeatNoiseProcessing.remove( devUnit )
continue
_dev = Devices[ devUnit ]
try:
_lastupdate = datetime.strptime( _dev.LastUpdate, "%Y-%m-%d %H:%M:%S" )
except Exception as ex:
Domoticz.Debug("unable to convert lastUpdate time from '%s': " % + str(ex) )
continue
# lastupdate > 3/5 heartbeat
if int((_now - _lastupdate).total_seconds()) < int((3*self._heartBeat)/5): continue
# ok cooldown noise indicator
nValue = _dev.nValue - 1
sValue = ( "noise cooldown" if nValue > 1 else "calm :)" )
UpdateDevice( Dev=_dev, nValue=nValue, sValue=sValue)
# remove noise processing ?
if nValue <= 1:
self.heartBeatNoiseProcessing.remove( devUnit )
# Function to parse existing devices to create a reverse dictionary
# to ease check, insertion of new devices
def _parseDevices( self ):
for unit in Devices:
'''
# [apr.18] Devices names are prefixed with the Parameters['Name']
# ... we'll remove from "neOCampus-inventory - u4/302/xxx" ton only keep 'u4/302/xxx"
_regexedDeviceName = Devices[unit].Name.replace(Parameters['Name'] + ' - ','',1).lstrip()
if _regexedDeviceName not in self._devicesNamesDict:
self._devicesNamesDict[ _regexedDeviceName ] = unit
try:
self._availableUnits.remove( unit )
except ValueError as ex:
Domoticz.Error("Unit %d has been already removed ?!?! ... continuing :s" % unit)
'''
# [apr.18] we don't anymore rely on 'Name' field of Devices but Options associated that contains 'name'
name = Devices[unit].Options['name']
if name not in self._devicesNamesDict:
self._devicesNamesDict[ name ] = unit
try:
self._availableUnits.remove( unit )
except ValueError as ex:
Domoticz.Error("Unit %d has been already removed ?!?! ... continuing :s" % unit)
#
# neOCampus functions
# _processMQTTmessage
# _publishMQTTmessage
# _processTemperatureCommand
# _processLuminosityCommand
# _processNoiseCommand
#
# process neOCampus MQTT messages
def _processMQTTmessage( self, topic=None, payload=None ):
Domoticz.Debug("_processMQTTmessage: Topic = %s Payload = %20s ..." % (topic,str(payload)) )
# itemID = u4/302/noise [auto_36FC:57]
# itemID_ctrl = u4/302/noise/threshold
# itemID_ctrl = u4/302/noise/sensitivity
# determine type (i.e sensor or actuator) of item
kindID = topic.split('/')[-1]
if kindID == 'device': return
if kindID not in NEOCAMPUS_SENSORS_TYPES and kindID not in NEOCAMPUS_ACTUATORS_TYPES:
Domoticz.Log("unknown kind of topic '%s' ..." % topic)
return
# if not 'value' in message ... discard
if 'value' not in payload: return
# compute item & item_control identity
if topic.startswith(__class__.NEOCAMPUS_TESTTOPIC):
itemID=topic.lstrip(__class__.NEOCAMPUS_TESTTOPIC+'/')
else:
itemID=topic
itemID_ctrlBase = itemID
if payload.get('subID') is not None:
itemID+=" [%s:%d]" % (payload.get('unitID'),payload.get('subID'))
else:
itemID+=" [%s]" % payload.get('unitID','??')
Domoticz.Debug("ItemID = " + itemID)
# let's start to create/update sensors'n actuators
_device = None
_deviceUnit = None
_options = { 'topic' : topic, 'name' : itemID } # we save itemID in Options in case user change names in web UI
if kindID == "temperature":
if itemID not in self._devicesNamesDict and "value" in payload:
# create sensor
_device = None
_deviceUnit = self._availableUnits[0]
try:
_device = Domoticz.Device(Name=itemID, Unit=_deviceUnit, TypeName="Temperature", Options=_options, Description="name:"+itemID, Used=1).Create()
except Exception as ex:
Domoticz.Error("unable to CREATE sensor Name = '%s': " % (itemID) + str(ex))
return
if _device is None: return
# delete used unit number and add new device to reverse dict
self._devicesNamesDict[ itemID ] = _deviceUnit
self._availableUnits.pop(0)
# sensor control
#TODO: create a switch to send commands to sensor (e.g change acquisition frequency)
elif itemID in self._devicesNamesDict:
# Domoticz device already exists
try:
_deviceUnit = self._devicesNamesDict[ itemID ]
_device = Devices[ _deviceUnit ]
except Exception as ex:
Domoticz.Error("unable to FIND sensor Name = '%s': " % (itemID) + str(ex))
return
else:
return
if "value" not in payload: return
UpdateDevice( Dev=_device, sValue=str(payload['value']) )
elif kindID == "luminosity":
if itemID not in self._devicesNamesDict and "value" in payload:
# create sensor
_device = None
_deviceUnit = self._availableUnits[0]
try:
_device = Domoticz.Device(Name=itemID, Unit=_deviceUnit, TypeName="Illumination", Options=_options, Description="name:"+itemID, Used=1).Create()
except Exception as ex:
Domoticz.Error("unable to CREATE sensor Name = '%s': " % (itemID) + str(ex))
return
if _device is None: return
# delete used unit number and add new device to reverse dict
self._devicesNamesDict[ itemID ] = _deviceUnit
self._availableUnits.pop(0)
# sensor control
#TODO: create a switch to send commands to sensor (e.g change acquisition frequency)
elif itemID in self._devicesNamesDict:
try:
_deviceUnit = self._devicesNamesDict[ itemID ]
_device = Devices[ _deviceUnit ]
except Exception as ex:
Domoticz.Error("unable to FIND sensor Name = '%s': " % (itemID) + str(ex))
return
else:
return
if "value" not in payload: return
UpdateDevice( Dev=_device, nValue=int(payload['value']) , sValue=str(payload['value']) )
elif kindID == "noise":
# noise control: threshold
# one for all noise sensors in same location
itemID_ctrl = itemID_ctrlBase + "/threshold"
if itemID_ctrl not in self._devicesNamesDict:
_options_ctrl = { 'topic':topic+'/command', 'name':itemID_ctrl, 'dest':'all', 'order':'threshold' }
_device = None
_deviceUnit = self._availableUnits[-1]
try:
_device = Domoticz.Device(Name=itemID_ctrl, Unit=_deviceUnit, TypeName="Switch", Switchtype=7, Options=_options_ctrl, Description="name:"+itemID_ctrl, Used=1).Create()
except Exception as ex:
Domoticz.Error("unable to CREATE sensor control Name = '%s': " % (itemID_ctrl) + str(ex))
return
if _device is None: return
# delete used unit number and add new device to reverse dict
self._devicesNamesDict[ itemID_ctrl ] = self._availableUnits.pop(-1)
# noise control: sensitivity
# one for all noise sensors in same location
itemID_ctrl = itemID_ctrlBase + "/sensitivity"
if itemID_ctrl not in self._devicesNamesDict:
_options_ctrl = { 'topic':topic+'/command', 'name':itemID_ctrl, 'dest':'all', 'order':'sensitivity' }
_device = None
_deviceUnit = self._availableUnits[-1]
try:
_device = Domoticz.Device(Name=itemID_ctrl, Unit=_deviceUnit, TypeName="Switch", Switchtype=7, Options=_options_ctrl, Description="name:"+itemID_ctrl, Used=1).Create()
except Exception as ex:
Domoticz.Error("unable to CREATE sensor control Name = '%s': " % (itemID_ctrl) + str(ex))
return
if _device is None: return
# delete used unit number and add new device to reverse dict
self._devicesNamesDict[ itemID_ctrl ] = self._availableUnits.pop(-1)
# noise sensor
if itemID not in self._devicesNamesDict and "value" in payload:
_device = None
_deviceUnit = self._availableUnits[0] # TODO: create function that allocates an id
try:
_device = Domoticz.Device(Name=itemID, Unit=_deviceUnit, TypeName="Alert", Options=_options, Description="name:"+itemID, Used=1).Create()
except Exception as ex:
Domoticz.Error("unable to CREATE sensor Name = '%s': " % (itemID) + str(ex))
return
if _device is None: return
# delete used unit number and add new device to reverse dict
self._devicesNamesDict[ itemID ] = self._availableUnits.pop(0)
elif itemID in self._devicesNamesDict:
try:
_deviceUnit = self._devicesNamesDict[ itemID ]
_device = Devices[ _deviceUnit ]
except Exception as ex:
Domoticz.Error("unable to FIND sensor Name = '%s': " % (itemID) + str(ex))
return
else:
return
if "value" not in payload: return
# a message is received whenever there is noise ...
UpdateDevice( Dev=_device, nValue=4, sValue='[%d%s] NOISE NOISE!!' % (payload['value'],payload.get('value_units','')) )
# [apr.18] we'll switch back to normal condition upon HeartBeat processing
with self.heartBeatNoiseProcessingLock:
if _deviceUnit not in self.heartBeatNoiseProcessing:
self.heartBeatNoiseProcessing.append( _deviceUnit )
# publish a MQTT message over a topic
def _publishMQTTmessage( self, topic, msg ):
if not self.mqttConn.Connected():
Domoticz.Debug("unable to publish while not Connected :|")
return
# publish message
Domoticz.Debug( "MQTT publish '%s' <-- %s" % (topic,str(msg)) )
return self.mqttConn.Send( {'Verb' : 'PUBLISH', 'QoS': 0, 'Topic': topic, 'Payload': json.dumps(msg)} )
# process Temperature commands
def _processTemperatureCommand( self, dev, topic, order, value ):
if order!="frequency": return
# Not yet implemented!
return
# process Luminosity commands
def _processLuminosityCommand( self, dev, topic, order, value ):
if order!="frequency": return
# Not yet implemented!
return
# process Noise commands
def _processNoiseCommand( self, dev, topic, order, value ):
msg = dict()
msg['dest'] = 'all'
msg['order'] = order
msg['value'] = value
if order=="sensitivity":
self._publishMQTTmessage( topic, msg )
#UpdateDevice(Dev=dev, nValue=value, sValue="sensitivity=%d" % value)
UpdateDevice( Dev=dev, nValue=2, sValue=str(value) )
elif order=="threshold":
_value = (value * __class__.NOISE_THRESHOLD_MAX)/100
msg['value'] = _value
self._publishMQTTmessage( topic, msg )
# perequation over 100% --> NOISE_THRESHOLD_MAX
#UpdateDevice(Dev=dev, nValue=value, sValue="threshold=%d" % _value )
UpdateDevice( Dev=dev, nValue=2, sValue=str(value) )
else:
# Not yet implemented!
Domoticz.Debug("Noise command order '%s' not yet implemented" % str(order) )
return
'''
*** to update a device values:
def UpdateIcon(Unit, iconID):
if Unit not in Devices: return
d = Devices[Unit]
if d.Image != iconID: d.Update(d.nValue, d.sValue, Image=iconID)
*** to process messages:
import msgpack
def onMessage(self, Connection, Data):
try:
self.unpacker.feed(Data)
for result in self.unpacker:
Domoticz.Debug("Got: %s" % result)
if 'exception' in result: return
if result['cmd'] == 'status':
UpdateDevice(self.statusUnit,
(1 if result['state_code'] in [5, 6, 11] else 0), # ON is Cleaning, Back to home, Spot cleaning
self.states.get(result['state_code'], 'Undefined')
)
UpdateDevice(self.batteryUnit, result['battery'], str(result['battery']), result['battery'],
AlwaysUpdate=(self.heartBeatCnt % 100 == 0))
if Parameters['Mode5'] == 'dimmer':
UpdateDevice(self.fanDimmerUnit, 2, str(result['fan_level'])) # nValue=2 for show percentage, instead ON/OFF state
else:
level = {38: 10, 60: 20, 77: 30, 90: 40}.get(result['fan_level'], None)
if level: UpdateDevice(self.fanSelectorUnit, 1, str(level))
elif result['cmd'] == 'consumable_status':
mainBrush = cPercent(result['main_brush'], 300)
sideBrush = cPercent(result['side_brush'], 200)
filter = cPercent(result['filter'], 150)
sensors = cPercent(result['sensor'], 30)
UpdateDevice(self.cMainBrushUnit, mainBrush, str(mainBrush), AlwaysUpdate=True)
UpdateDevice(self.cSideBrushUnit, sideBrush, str(sideBrush), AlwaysUpdate=True)
UpdateDevice(self.cFilterUnit, filter, str(filter), AlwaysUpdate=True)
UpdateDevice(self.cSensorsUnit, sensors, str(sensors), AlwaysUpdate=True)
except msgpack.UnpackException as e:
Domoticz.Error('Unpacker exception [%s]' % str(e))
'''
global _plugin
_plugin = BasePlugin()
def onStart():
global _plugin
_plugin.onStart()
def onStop():
global _plugin
_plugin.onStop()
def onConnect(Connection, Status, Description):
global _plugin
_plugin.onConnect(Connection, Status, Description)
def onMessage(Connection, Data):
global _plugin
_plugin.onMessage(Connection, Data)
def onCommand(Unit, Command, Level, Hue):
global _plugin
_plugin.onCommand(Unit, Command, Level, Hue)
def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
global _plugin
_plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)
def onDeviceModified(Unit):
global _plugin
_plugin.onDeviceModified(Unit)
def onDeviceRemoved(Unit):
global _plugin
_plugin.onDeviceRemoved(Unit)
def onDisconnect(Connection):
global _plugin
_plugin.onDisconnect(Connection)
def onHeartbeat():
global _plugin
_plugin.onHeartbeat()
#
# Helpers functions
#
#
# Update Device into DB
def UpdateDevice( Unit=None, Dev=None, nValue=None, sValue=None, Image=None):
# Make sure that the Domoticz device still exists (they can be deleted) before updating it
if (Unit is not None) and (Unit in Devices):
Dev = Devices.get('Unit')
if Dev is None:
Domoticz.Error("Error while trying to update values, Dev is None ?!?!")
return
if nValue is None:
nValue = Dev.nValue
if sValue is None:
sValue = Dev.sValue
if Image is None:
Image = Dev.Image
Dev.Update(nValue=nValue, sValue=str(sValue), Image=Image)
Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Dev.Name+")")
return
def DumpConfigToLog():
for x in Parameters:
if Parameters[x] != "":
Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
Domoticz.Debug("Device count: " + str(len(Devices)))
for x in Devices:
Domoticz.Debug("Device: " + str(x) + " - " + str(Devices[x]))
Domoticz.Debug("Device ID: '" + str(Devices[x].ID) + "'")
Domoticz.Debug("Device Name: '" + Devices[x].Name + "'")
Domoticz.Debug("Device nValue: " + str(Devices[x].nValue))
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
Domoticz.Debug("Device Options: " + str(Devices[x].Options))
return
def DumpDictionaryToLog(theDict, Depth="-"):
if isinstance(theDict, dict):
for x in theDict:
if isinstance(theDict[x], dict):
Domoticz.Log(Depth+"> Dict '"+x+"' ("+str(len(theDict[x]))+"):")
DumpDictionaryToLog(theDict[x], Depth+"---")
elif isinstance(theDict[x], list):
Domoticz.Log(Depth+"> List '"+x+"' ("+str(len(theDict[x]))+"):")
DumpListToLog(theDict[x], Depth+"---")
elif isinstance(theDict[x], str):
Domoticz.Log(Depth+">'" + x + "':'" + str(theDict[x]) + "'")
else:
Domoticz.Log(Depth+">'" + x + "': " + str(theDict[x]))
def DumpListToLog(theList, Depth="-"):
if isinstance(theList, list):
for x in theList:
if isinstance(x, dict):
Domoticz.Log(Depth+"> Dict ("+str(len(x))+"):")
DumpDictionaryToLog(x, Depth+"---")
elif isinstance(x, list):
Domoticz.Log(Depth+"> List ("+str(len(theList))+"):")
DumpListToLog(x, Depth+"---")
elif isinstance(x, str):
Domoticz.Log(Depth+">'" + x + "':'" + str(theList[x]) + "'")
else:
Domoticz.Log(Depth+">'" + str(x) + "': " + str(theList[x]))