开发Python插件
Overview
Page (still) under construction.
This page is aimed at developers wishing to improve Domoticz functionality via Python Plugins. If you simply want to use a plugin that a developer has already created for you, please see Using Python plugins
The Domoticz Python Framework is not a general purpose Domoticz extension. It is designed to allow developers to easily create an interface between a piece of Hardware (or Virtual Hardware) and Domoticz. To that end it provides capabilities that seek to do the hard work for the developer leaving them to manage the messages that move backwards and forwards between the two.
The Framework provides the plugin with a full Python 3 instance that exists for as long as Domoticz is running. The plugin is called by Domoticz when relevant events occur so that it can manage connectivity to the hardware and state synchronisation.
Multiple copies of the same plugin can run for users that have more than one instance of a particular hardware type.
The following things should not be attempted using the Python Framework:
- Use of asynchronous code or libraries. This will not work the way you expect.
- Use of call back functions. This will not work the way you expect.
- Waiting or sleeping. Plugins are single threaded so the whole plugin system will wait.
Python libraries known to not function well with the framework: requests
Overall Structure
The plugin documentation is split into two distinct parts:
- Plugin Definition - Telling Domoticz about the plugin
- Runtime Structure - Interfaces and APIs to manage message flows between Domoticz and the hardware
Getting started
If you are writing a Python plugin from scratch, you may want to begin by using the Script Template in domoticz/plugins/examples/BaseTemplate.py, which is also available from the github source repo at https://github.com/domoticz/domoticz/blob/master/plugins/examples/BaseTemplate.py
Plugin Definition
To allow plugins to be added without the need for code changes to Domoticz itself requires that the plugins be exposed to Domoticz in a generic fashion. This is done by having the plugins live in a set location so they can be found and via an XML definition embedded in the Python script itself that describes the parameters that the plugin requires.
During Domoticz startup all directories directly under the 'plugins' directory are scanned for python files named plugin.py
and those that contain definitions are indexed by Domoticz. When the Hardware page is loaded the plugins defined in the index are merged into the list of available hardware and are indistinguishable from natively supported hardware. For the example below the Kodi plugin would be in a file domoticz/plugins/Kodi/plugin.py
. Some example Python scripts can be found in the domoticz/plugins/examples
directory.
Plugin definitions expose some basic details to Domoticz and a list of the parameters that users can configure. Each defined parameters will appear as an input on the Hardware page when the plugin is selected in the dropdown.
Definitions look like this:
"""
<plugin key="Kodi" name="Kodi Players" author="dnpwwo" version="1.0.0" wikilink="http://www.domoticz.com/wiki/plugins/Kodi.html" externallink="https://kodi.tv/">
<params>
<param field="Address" label="IP Address" width="200px" required="true" default="127.0.0.1"/>
<param field="Port" label="Port" width="30px" required="true" default="9090"/>
<param field="Mode1" label="MAC Address" width="150px" required="false"/>
<param field="Mode2" label="Shutdown Command" width="100px">
<options>
<option label="Hibernate" value="Hibernate"/>
<option label="Suspend" value="Suspend"/>
<option label="Shutdown" value="Shutdown"/>
<option label="Ignore" value="Ignore" default="true" />
</options>
</param>
<param field="Mode3" label="Notifications" width="75px">
<options>
<option label="True" value="True"/>
<option label="False" value="False" default="true" />
</options>
</param>
<param field="Mode6" label="Debug" width="75px">
<options>
<option label="True" value="Debug"/>
<option label="False" value="Normal" default="true" />
</options>
</param>
</params>
</plugin>
"""
Definition format details:
Tag | Description/Attributes | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
<plugin> | Required.
| ||||||||||||||
<params> | Simple wrapper for param tags. Contained within <plugin>. | ||||||||||||||
<param> | Parameter definitions, Contained within <params>.
Parameters are used by the Hardware page during plugin addition and update operations. These are stored in the Hardware table in the database and are made available to the plugin at runtime.
| ||||||||||||||
<options> | Simple wrapper for option tags. Contained within <param>.
Parameter definitions that contain this tag will be shown as drop down menus in the Hardware page. Available values are defined by the <option> tag. | ||||||||||||||
<option> | Instance of a drop down option. Contained within <options>.
|
Runtime Structure
Domoticz exposes settings and device details through four Python dictionaries.
Settings
Contents of the Domoticz Settings page as found in the Preferences database table. These are always available and will be updated if the user changes any settings. The plugin is not restarted. They can be accessed by name for example: Settings["Language"]
Parameters
These are always available and remain static for the lifetime of the plugin. They can be accessed by name for example: Parameters["SerialPort"]
Description | |
---|---|
Key | Unique short name for the plugin, matches python filename. |
HomeFolder | Folder or directory where the plugin was run from. |
Author | Plugin Author. |
Version | Plugin version. |
Address | IP Address, used during connection. |
Port | IP Port, used during connection. |
Username | Username. |
Password | Password. |
Mode1 | General Parameter 1 |
... | |
Mode6 | General Parameter 6 |
SerialPort | SerialPort, used when connecting to Serial Ports. |
Devices
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Key | Unit.
Unit number for the device as specified in the Manifest.
Note: Devices can be deleted in Domoticz so not all Units specified will necessarily still be present.
E.g:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Methods | Per device calls into Domoticz to manipulate specific devices
|
Connections
Available soon in an upcoming beta release
Connection objects allow plugin developers to connect to multiple external sources using a variety of transports and protocols simultaneously. By using the plugin framework to handle connectivity Domoticz will remain responsive no matter how many connections the plugin handles.
Connections remain active only while they are in scope in Python after that Domoticz will actively disconnect them so plugins should store Connections that they want to keep in global or class variables.
Function | Description/Attributes | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
__init__ | Defines the connection type that will be used by the object.
This allows Domoticz to make connections on behalf of the plugin. E.g: myConn = Domoticz.Connnection(Transport="TCP/IP", Protocol="JSON", Address=Parameters["Address"], Port=Parameters["Port"])
myConn.Connect()
mySerialConn = Domoticz.Connnection(Transport="Serial", Protocol="XML", Address=Parameters["SerialPort"], Baud=115200)
mySerialConn.Connect()
Both positional and named parameters are supported. | ||||||||||||
Address | Returns the Address associated with the Connection. | ||||||||||||
Port | Returns the Port associated with the Connection. | ||||||||||||
Baud | Returns the Baud Rate of the Connection. | ||||||||||||
Connected | Parameters: None.
Returns True if the connection is connected or listening, otherwise False. | ||||||||||||
Connect | Parameters: None.
Initiate a connection to a external hardware using transport details.
Connect returns immediately and the results will be returned via the onConnect callback. If the address set via the Transport function translates to multiple endpoint they will all be tried in order until the connection succeeds or the list of endpoints is exhausted. | ||||||||||||
Listen | Parameters: None.
Start listening on specifed Port using specified TCP/IP or UDP/IP transport. Connection objects will be created for each client that connects and onConnect will be called. | ||||||||||||
Send | Send the specified message to the external hardware.
Both positional and named parameters are supported. myConn.Send(Message=myMessage, Delay=4)
Headers = {"Connection": "keep-alive", "Accept": "Content-Type: text/html; charset=UTF-8"}
Domoticz.Send("", "GET", "/page.html", Headers)
postData = "param1=value¶m2=other+value"
myHttpConn.Send(postData, "POST", "/MediaRenderer/AVTransport/Control", {'Content-Length':str(len(postData))})
| ||||||||||||
Disonnect | Parameters: None.
Terminate the connection to the external hardware for the connection. |
Images
Developers can ship custom images with plugins in the standard Domoitcz format as described here: [1]. Resultant zip file(s) should be placed in the folder with the plugin itself
Description | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Key | Base.
The base value as specified in icons.txt file in custom image zip file.
| |||||||||||||||
Methods | Per image calls into Domoticz to manipulate specific images
|
Callbacks
Plugins are event driven. If a callback is not used by a plugin then it should not be present in the plugin.py file, Domoticz will not attempt to call callbacks that are not defined.
Parameters in bold are will be added in an upcoming beta release
Domoticz will notify the plugin when certain events occur through a number of callbacks, these are:
Callback | Description |
---|---|
onStart | Parameters: None.
Called when the hardware is started, either after Domoticz start, hardware creation or update. |
onConnect | Parameters: Connection, Status, Description
Called when connection to remote device either succeeds or fails, or when a connection is made to a listening Address:Port. Connection is the Domoticz Connection object associated with the event. Zero Status indicates success. If Status is not zero then the Description will describe the failure. |
onMessage | Parameters: Connection, Data, Status, Extras.
Called when a single, complete message is received from the external hardware (as defined by the Protocol setting). This callback should be used to interpret messages from the device and set the related Domoticz devices as required. Connection is the Domoticz Connection object associated with the event Status and Extras are protocol specific. For HTTP Status is the Response Status (200, 302, 404 etc) and Extras is a dictionary containing the Response Headers |
onNotification | Parameters: Name, Subject, Text, Status, Priority, Sound, ImageFile.
Called when any Domoticz device generates a notification. Name parameter is the device that generated the notification, the other parameters contain the notification details. Hardware that can handle notifications should be notified as required. |
onCommand | Parameters: Unit, Command, Level, Hue.
Called when a command is received from Domoticz. The Unit parameters matches the Unit specified in the device definition and should be used to map commands to Domoticz devices. Level is normally an integer but may be a floating point number if the Unit is linked to a thermostat device. This callback should be used to send Domoticz commands to the external hardware. |
onHeartbeat | Called every 'heartbeat' seconds (default 10) regardless of connection status.
Heartbeat interval can be modified by the Heartbeat command. Allows the Plugin to do periodic tasks including request reconnection if the connection has failed. |
onDisconnect | Parameters: Connection
Called after the remote device is disconnected, Connection is the Domoticz Connection object associated with the event |
onStop | Called when the hardware is stopped or deleted from Domoticz. |
C++ Callable API
Importing the ‘Domoticz’ module in the Python code exposes functions that plugins can call to perform specific functions. All functions are non-blocking and return immediately.
Function | Description/Attributes | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Debug | Parameters: String
Write message to Domoticz log only if verbose logging is turned on. | ||||||||||||
Log | Parameters: String.
Write message to Domoticz log | ||||||||||||
Error | Parameters: String
Write error message to Domoticz log | ||||||||||||
Debugging | Parameters: Integer, 0 or 1.
Set logging level. 1 set verbose logging, all other values use default level | ||||||||||||
Transport This will be replaced by a new Connection object in an upcoming release |
Defines the connection type that will be used by the plugin.
This allows Domoticz to make connections on behalf of the plugin. E.g: Domoticz.Transport("TCP/IP", Parameters["Address"], Parameters["Port"])
Domoticz.Transport(Transport="Serial", Address=Parameters["SerialPort"], Baud=115200)
Both positional and named parameters are supported. | ||||||||||||
Protocol This will be replaced by a new Connection object in an upcoming release |
Parameters: String.
Defines the protocol details that will be used by the plugin. This allows Domoticz to know how to handle incoming messages properly.
| ||||||||||||
Heartbeat | Parameters: Integer.
Set the heartbeat interval in seconds, default 10 seconds. Values greater than 30 seconds will cause a message to be regularly logged about the plugin not responding. The plugin will actually function correctly with values greater than 30 though. | ||||||||||||
Connect This will be replaced by a new Connection object in an upcoming release |
Parameters: None.
Initiate a connection to a external hardware using transport details.
Connect returns immediately and the results will be returned via the onConnect callback. If the address set via the Transport function translates to multiple endpoint they will all be tried in order until the connection succeeds or the list of endpoints is exhausted. | ||||||||||||
Send This will be replaced by a new Connection object in an upcoming release |
Send the specified message to the external hardware.
Both positional and named parameters are supported. Domoticz.Send(Message=myMessage, Delay=4)
Headers = {"Connection": "keep-alive", "Accept": "Content-Type: text/html; charset=UTF-8"}
Domoticz.Send("", "GET", "/page.html", Headers)
postData = "param1=value¶m2=other+value"
Domoticz.Send(postData, "POST", "/MediaRenderer/AVTransport/Control", {'Content-Length':str(len(postData))})
| ||||||||||||
Disconnect This will be replaced by a new Connection object in an upcoming release |
Parameters: None.
Disconnect from the external hardware | ||||||||||||
Notifier | Parameters: Name, String.
Informs the plugin framework that the plugin's external hardware can comsume Domoticz Notifications. |
Examples
There are a number of examples that are available that show potential usages and patterns that may be useful:
Example | Description |
---|---|
Base Template | A good starting point for developing a plugin. |
Denon 4306 | Support for Denon AVR amplifiers.
|
DLink DSP-W215 | TCP/IP connectivity using HTTP protocol.
|
Kodi | Alternate Kodi plugin to the built in one.
|
RAVEn | Alternate RAVEn Energy Monitor plugin to the built in one.
|
Debugging
Debugging embedded Python can be done using the standard pdb functionality that comes with Python using the rpdb
(or 'Remote PDB') Python library.
The only restriction is that it can not be done from a debug instance of Domoticz running in Visual Studio.
Download the rpdb
library from https://pypi.python.org/pypi/rpdb/ and drop the 'rpdb' directory into the directory of the plugin to be debugged (or anywhere in the Python path). Import the library using something like:
def onStart(self): if Parameters["Mode6"] == "Debug": Domoticz.Debugging(1) Domoticz.Log("Debugger started, use 'telnet 0.0.0.0 4444' to connect") import rpdb rpdb.set_trace() else: Domoticz.Log("onStart called")
The rpdb.set_trace()
command causes the Python Framework to be halted and a debugger session to be started on port 4444. For the code above the Domoticz log will show something like:
2017-04-17 15:39:25.448 (MyPlugin) Initialized version 1.0.0, author 'dnpwwo' 2017-04-17 15:39:25.448 (MyPlugin) Debug log level set to: 'true'. 2017-04-17 15:39:25.448 (MyPlugin) Debugger started, use 'telnet 0.0.0.0 4444' to connect pdb is running on 127.0.0.1:4444
Connect to the debugger using a command line tool such as Telnet. Attaching to the debugger looks like this if you start the session on the same machine as Domoticz, otherwise supply the Domoticz IP address instead of '0.0.0.0':
pi@bob:~$ telnet 0.0.0.0 4444 Trying 0.0.0.0... Connected to 0.0.0.0. Escape character is '^]'. --Return-- > /home/pi/domoticz/plugins/MyPlugin/plugin.py(30)onStart()->None -> rpdb.set_trace() (Pdb)
enter commands at the prompt such as:
(Pdb) l 25 def onStart(self): 26 if Parameters["Mode6"] == "Debug": 27 Domoticz.Debugging(1) 28 Domoticz.Log("Debugger started, use 'telnet 0.0.0.0 4444' to connect") 29 import rpdb 30 -> rpdb.set_trace() 31 else: 32 Domoticz.Log("onStart called") 33 34 def onStop(self): 35 Domoticz.Log("onStop called") (Pdb) pp Parameters {'Address': , 'Author': 'dnpwwo', 'HardwareID': 7, 'HomeFolder': '/home/pi/domoticz/plugins/MyPlugin/', 'Key': 'Debug', 'Mode1': , 'Mode2': , 'Mode3': , 'Mode4': , 'Mode5': , 'Mode6': 'Debug', 'Name': 'MyPlugin', 'Password': , 'Port': '4444', 'SerialPort': , 'Username': , 'Version': '1.0.0'} (Pdb) b 53 Breakpoint 1 at /home/pi/domoticz/plugins/MyPlugin/plugin.py:53 (Pdb) continue > /home/pi/domoticz/plugins/MyPlugin/plugin.py(53)onHeartbeat() -> Domoticz.Log("onHeartbeat called") (Pdb) ll 52 def onHeartbeat(self): 53 B-> Domoticz.Log("onHeartbeat called") (Pdb)
Details of debugging commands can be found here: https://docs.python.org/3/library/pdb.html#debugger-commands