Hi aeisenhuth,
At the bottom of this post is the general flow
in my setup i've got a python program running in the background on my pi:
RX09.py
Since i haven't installed Domoticz on the Pi (as you have) i've added the lines to enable the RTR09 in /etc/rc.local instead
rc.local lines:
Code: Select all
# Add these lines to enable RX 09 Eldat USB Tranciever EasyWave
sudo modprobe cp210x
sudo sh -c 'echo 155a 1006 > /sys/bus/usb-serial/drivers/cp210x/new_id'
python /home/pi/project/python_programs/rx09.py /dev/ttyUSB0 5331 &
The code for the Serproxy handling the communication with the RX09:(
rx09.py)
It requires the device and port number (for the socket) at startup
Code: Select all
# based upon: https://github.com/richardkchapman/serproxy/blob/master/serproxy.py
#
# Python version of serproxy
#
# Python 3.5.1
# Windows 10, Pi 4.59. V7+
#
# Accept requests by socket (TCP/IP Raw) and forward to Serial Port
# Return Serial response to socket
#
# Original Author: Richard K Chapman
# Adaptations: WimR
#
# Date: 2018 01 12
# 2018 02 28 added read csv with link switch id domoticz idx
# read serial line and log
#
from socket import *
from threading import Thread
import logging
import serial
import time
import sys
import os
import csv
import json, urllib, urllib2
ser = None
allClients = []
BUFSIZ = 1024
serialDevice = None
def serialReader():
global ser
global allClients
global serialDevice
line = []
oldLine = ''
oldTime = time.time()
allDevices = {}
while 1:
try:
# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
port=serialDevice,
baudrate=57600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
# ser.open() # Doesn't seem to be needed
ser.isOpen()
logging.info('Serial port opened %s', ser)
# load device table from csv file
ifile = open('/home/pi/project/python_programs/devices.csv', "rU")
reader = csv.reader(ifile, delimiter=";")
allDevices = dict([(row[0],row[1]) for row in reader])
ifile.close()
logging.info('Device table loaded in dictionnary')
# main listener loop
while 1:
out = ser.read(1)
line.append(out)
# wait till '\r' as end of line char
if out == '\r':
myLine = ''.join(line)
myTime = time.time()
logging.info('Serial respons : %s', myLine)
# if myLine starts with REC :split line use second part as key in dict and lauch json call
if myLine[0:3] == 'REC':
logging.info('Key : %s', myLine[4:10])
logging.info('Mapped to : %s', allDevices.get(myLine[4:10]))
logging.info('Command : %s', myLine[11])
if myLine[11] == 'A':
myCommand = 'On'
else:
myCommand = 'Off'
logging.info('Command : %s', myCommand)
# switches may bounce and emit rapidly 2 to 5 times the same command
if myLine <> oldLine or myTime - oldTime > 1:
# transfer received command to Domoticz
result_load = json.load(urllib2.urlopen("http://192.168.1.54:8084/json.htm?type=command¶m=switchlight&idx=%s&switchcmd=%s&level=0" % (allDevices.get(myLine[4:10]), myCommand), timeout=5))
logging.info('Respons domoticz : %s', str(result_load))
oldTime = myTime
oldLine = myLine
else:
# write line to all clients
for elem in line:
for client in allClients:
client.send(elem)
line = []
except:
# Wait a while then try again
logging.debug('Error on serial port')
if ser is not None:
ser.close()
ser = None
time.sleep(10)
def handler(clientsock,addr):
global ser
global allClients
logging.info('connected from: %s', addr)
allClients.append(clientsock)
while 1:
data = clientsock.recv(BUFSIZ)
if not data:
break
if not (ser is None):
ser.write(data)
logging.info('Message received: %s ', data)
logging.info('disconnected from: %s', addr)
allClients.remove(clientsock)
clientsock.close()
def mainProgram():
global serialDevice
global port
if len(sys.argv)>=4 and sys.argv[1] == '-p':
writePidFile(sys.argv[2])
del sys.argv[2]
del sys.argv[1]
if len(sys.argv) != 3:
print ('usage: ' + sys.argv[0] + ' device port')
exit()
print ('Logfile: SerProxy.Log')
logging.basicConfig(filename='/tmp/SerProxy.Log', filemode='w', format='%(asctime)s %(levelname)s:%(message)s', level=logging.DEBUG)
serialDevice=sys.argv[1]
port=int(sys.argv[2])
print ('Serial Device: ' + sys.argv[1])
print ('Port: ' + sys.argv[2])
Thread(target=serialReader).start()
ser = None
allClients = []
listenAddr = ('', port)
try:
serversock = socket(AF_INET, SOCK_STREAM)
serversock.bind(listenAddr)
serversock.listen(2)
logging.info('waiting for connection')
while 1:
clientsock, addr = serversock.accept()
Thread(target=handler, args=(clientsock, addr)).start()
except KeyboardInterrupt:
if not ser is None:
ser.close()
os._exit(0)
def writePidFile(pidfile):
pid = str(os.getpid())
f = open(pidfile, 'w')
f.write(pid)
f.close()
if __name__=='__main__':
mainProgram()
This python program uses a simple csv file to permanently store the mapping between a physical transmitter and the corresponding Domoticz idx:
devices.csv In my case residing in the same dir as rx09.py (in my case /home/pi/project/python_programs)
devices.csv
Code: Select all
2ead4f;99
2eae9d;101
2eae2d;103
201a6c;104
On the Domoticz side i've created a
plugin that will communicate via sockets to the serialproxy (rx09.py)
It requires the RTR09 model, the IP address of the serialproxy and port number as Parameters
plugin.py
Code: Select all
# Domoticz Easywave Python Plugin using Serial Proxy
#
# Tested on:
# Domoticz: Version: 3.8153
# DSM: Version: 6.1
# SerialProxy: Windows 10, Python 3.5.1
# Raspberry Pi 4.9.59-v7+, Python 2.7.13
#
# Type "C" or "D" devices not tested, handling incomming messages from Easywave transmitters not implemented (SerProxy fires of JSON)
#
#
# Easywave protocol is used by brands Niko & Eldat to communicate between RF transmitters and recievers (868.30Mhz)
#
# This plugin requires Eldat RTR09 USB transceiver on Serial Proxy
#
# Two models exist: 64 virtual switches (RX09E5026-02-01K)
# 128 virtual switches (RX09E5031-02-01K)
#
# TCP communication will be setup to Serial Proxy
# Serial Proxy will transfer communication to and from Tranceiver proper
#
# Basic Easywave protocol: send: TXP,Switch_identifier,Command
# response: OK or ERROR to indicate correctly formed send string
#
# receiving: REC,Unique_key,Command
#
# Switch_identifier: two bytes HEX, transceiver ties a hardcoded Unique_key for each Switch_identifier
# Unique_key: six bytes HEX
# Command: A,B,C,D depending on transmitter/receiver e.g. Switch A=On, B=Off, Gong A=Ring
#
# On creating the hardware All the Virtual switches will be created as devices (currently only simple On/Of switches)
#
# Socket communication based upon: https://pymotw.com/2/socket/tcp.html
#
# Author: WimR
#
# Date: 2018 01 12
#
# <param field="SerialPort" label="Serial Port" width="150px" required="true" default="COM3" />
"""
<plugin key="Easywave" name="Easywave (Niko - Eldat)" author="WimR" version="0.3.0" wikilink="http://www.domoticz.com/wiki/plugins/plugin.html" externallink="http://www.eldat.de/produkte/_div/rx09e_sp_en.pdf">
<params>
<param field="Mode1" label="Model" width="250px">
<options>
<option label=" 64 Switches (RX09E5026-02-01K)" value="64"/>
<option label="128 Switches (RX09E5031-02-01K)" value="128" default="64" />
</options>
</param>
<param field="Mode2" label="IP Address Serial Proxy" width="250px" required="true" default="127.0.0.1" />
<param field="Mode3" label="Port" width="150px" required="true" default="5331" />
<param field="Mode6" label="Debug" width="75px">
<options>
<option label="True" value="Debug"/>
<option label="False" value="Normal" default="true" />
</options>
</param>
</params>
</plugin>
"""
import Domoticz
import sys
import socket
import time
def onStart():
i = 0
Domoticz.Log("onStart called")
if Parameters["Mode6"] == "Debug":
Domoticz.Debugging(1)
# if first time create all possible Switch devices
if (len(Devices) == 0):
# Create Virtual Switches (Number depending USB model)
# Mapping: Hex code id of Virutal Switch [0-7F] will be mapped to INT(Hex) + 1 as Unit [1-128]
for i in range(0, int(Parameters["Mode1"])):
Domoticz.Device(Name="EasyWave TX - " + format( i , '02X'), Unit=i+1, TypeName="Switch").Create()
Domoticz.Log("Device " + format( i , '02X') + " created.")
Domoticz.Log("Plugin has " + str(len(Devices)) + " devices associated with it.")
DumpConfigToLog()
return
def onStop():
Domoticz.Debug("onStop called")
def onConnect(Connection, Status, Description):
Domoticz.Debug("onConnect called")
def onMessage(Connection, Data, Status, Extra):
Domoticz.Debug("onMessage called")
return
def onCommand(Unit, Command, Level, Hue):
# define expected Succes response from Serial
response_ok = "OK\r" # '
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the port where the server is listening
Domoticz.Log("onMessage called using socket params: " + Parameters["Mode2"] + " " + str(Parameters["Mode3"]))
server_address = (str(Parameters["Mode2"]), int(Parameters["Mode3"]))
sock.connect(server_address)
try:
# Send data
# If Command received from Virtual Switch send command and toggle Switch state
Domoticz.Log("onCommand called for Unit " + str(Unit) + " : Hex : " + str(format( Unit -1 , '02X')) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
if Command == 'On':
# if Devices[Unit].nValue == 0:
message = "TXP," + str(format( Unit -1 , '02X')) + ",A" + '\r\n'
sock.sendall(str.encode(message))
# Look for the response before updating
amount_received = 0
amount_expected = 3
time.sleep(1)
while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
if repr(data)[1:6] == repr(response_ok)[0:5]:
Domoticz.Debug("Response OK")
Devices[Unit].Update(1,"On")
else:
Domoticz.Log("Device not Updated. Serial Response: " + repr(data))
else:
message = "TXP," + str(format( Unit -1 , '02X')) + ",B" + '\r\n'
sock.sendall(str.encode(message))
# Look for the response before updating
amount_received = 0
amount_expected = 3
time.sleep(1)
while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
if repr(data)[1:6] == repr(response_ok)[0:5]:
Domoticz.Debug("Response OK")
Devices[Unit].Update(0,"Off")
else:
Domoticz.Log("Device not Updated. Serial Response: " + repr(data))
finally:
message = None
return
def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
def onDisconnect(Connection):
Domoticz.Log("RX09 Disconnected")
return
def onHeartbeat():
return True
# Generic helper functions
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))
return
General
So when the Easywave Hardware is added in Domoticz it will currently create 64/128 switches pending on the model chosen.
The code allows for simple switches but can easily be adapted to cater for others
It will use then a socket communication to contact the serial proxy whenever a switch command is executed.
The serial proxy (residing on the machine with the RX09 installed) accepts the char stream on the socket and transfers these to the RX09.
Should the respons on the serial be different from "REC...." the respons will be returned on the socket (e.g.: OK or ERROR)
In case the serial yields "REC..." the serial proxy will map the key to an idx and fire a JSON call to Domoticz (currently only simple switches implemented but easily adapted to others)
This setup is now running stable for some days.
regards
Wim