Page 1 of 5

Eldat Easywave RXR09 Script

Posted: Thursday 01 March 2018 11:29
by aeisenhuth
Hi folks, after I got the Eldat RTR09 running on a RPi in Domoticz, I can not move on now.
I have created a dummy blinds switch and linked there under action "on" a python script:

import serial

ser = serial.Serial (
port = '/dev/ttyUSB0',
Baudrate = 57600,
Parität = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS
)
ser.write ("TXP,01,B")
ser.close()


When I enter the command TXP, 01, B in Windows Hyperterminal, the shutter goes down. It sends ASCII. :?:

Unfortunately nothing happens with this script. Maybe someone who knows a lot about Python can help me here.

Thank you :roll:

Re: Eldat Easywave RXR09 Script

Posted: Thursday 08 March 2018 11:35
by aeisenhuth
Can someone help here, please?

Re: Eldat Easywave RXR09 Script

Posted: Sunday 11 March 2018 17:59
by WimR
Hi aeisenhuth,

my setup is a bit different but should give you some ideas.

My Domoticz is running on a Synology and i didn't dare to mess around with the setup of the DSM to add the RTR09(RX09).
So i've setup the RX09 on a Pi and added a SerialProxy.
Within Domoticz i've created a python plugin Easywave.
I'm currently testing stability of the setup.
Got 7 physical receivers (6 Niko Easywave RF relais (driving lights) + Eldat Funkgong RCP09) and 6 physical Easywave RF switches from Niko + 1 low voltage RF transmitter Eldat.

From your code i suspect its missing "\r\n" at the end of "TXP,05,A" to get the command translated into a Easywave telegram.

If interested i can send the code for the my serproxy and Easywave plugin

regards

Wim

Re: Eldat Easywave RXR09 Script

Posted: Sunday 11 March 2018 20:26
by WimR
Hi aeisenhuth,

so ser.write("TXP,01,B\r\n") should yield a respons "OK\r" on the serial

regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 12 March 2018 12:57
by aeisenhuth
Hi WimR, tonight I will test with the command: ser.write ("TXP, 01, B \ r \ n"). I would be interested in your plugin. Best regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 12 March 2018 14:00
by WimR
Ok,

i'll post the code for the Plugin + Serproxy this evening.

regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 12 March 2018 20:09
by WimR
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&param=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

Re: Eldat Easywave RXR09 Script

Posted: Tuesday 13 March 2018 8:57
by aeisenhuth
Hi WimR, great work :shock:

I will try to install that on my Pi. you're really a genius for me.

Re: Eldat Easywave RXR09 Script

Posted: Saturday 17 March 2018 11:22
by WimR
Hi aeisenhuth,

I don't own any Niko or Eldat Easywave controlled blinds but have adapted both rx09.py and the plugin.py in order to cater for them.

Some assumptions on my part that needs to be verified:
1) the Easywave telegram for the 'Stop' of a blind is terminating with 'C' ('A' being On, 'B' being' Off) e.g.: TXP,01,C\r\n
2) i didn't check the nValue for a 'Stop' update (currently in the code it's assumed to be 0)

I did check that the simple switches created by the plugin can be altered into a 'Venitian Blind EU' via Tab Switches/Select switch/Edit/Change On/off into Venitian Blind EU.
You may also alter the name of the switch into something meaningfull e.g. the created device name: "Easywave TX - 01" into "Rolladen Hinten"

RX09.py
  • I added some code to RX09 in order to clearly log any new physical transmitter detected by the Transceiver
  • I changed the code so it maps 'C' telegrams into a JSON call with command 'Stop'
  • I also added a parameter to rx09.py : the Domoticz IP address (used in the JSON call)
    In the prior version it was a fixed IP
    If running on the same machine it should be 127.0.0.1
    So starting this version of the rx09 now is: rx09.py /dev/ttyUSB0 5531 127.0.0.1 &

Code: Select all

# based upon: https://github.com/richardkchapman/serproxy/blob/master/serproxy.py
#
# Python version of serproxy for Easywave (RX09) v20180316
#
# 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 Original version
#       2018 02 28 Added read csv with link: switch id TO domoticz idx
#                  Read serial line and add to log as 'respons'
#       2018 03 16 Added 'C' telegram mapped to 'Stop' (for devices Venitian Blinds EU)
#
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
  global domoticzIP

  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 allDevices.get(myLine[4:10]) is None:
                        logging.info('Warning : unknown physical RF transmitter: %s  Please add to device table with corresponding idx', myLine[4:10])
                   else:
                        # known physical RF transmitter
                           if myLine[11] == 'A':
                               myCommand = 'On'
                           elif myLine[11] == 'C':
                                myCommand = "Stop"
                           else:
                                myCommand = 'Off'
                           logging.info('Command : %s', myCommand)
                           # switches may bounce and emit rapidly 2 to 5 times the same command: debounce by comparing content and time lapse (more than 1 sec apart)
                           if myLine <> oldLine or myTime - oldTime > 1:
                                # transfer received command to Domoticz
                                result_load = json.load(urllib2.urlopen("http://%s:8084/json.htm?type=command&param=switchlight&idx=%s&switchcmd=%s&level=0" % (domoticzIP, allDevices.get(myLine[4:10]), myCommand), timeout=5))
                                logging.info('Respons domoticz : %s', str(result_load))
                                # arm debounce mechanism
                                oldTime = myTime
                                oldLine = myLine
                else:
                   # respons is not from physical switch
                   # write line to all clients
                   for elem in line:
                       for client in allClients:
                           client.send(elem)
                # Clear line
                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
  global domoticzIP

  if len(sys.argv)>=5 and sys.argv[1] == '-p':
    writePidFile(sys.argv[2])
    del sys.argv[2]
    del sys.argv[1]
  if len(sys.argv) != 4:
    print ('usage: ' + sys.argv[0] + ' device port domoticzIP')
    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])
  domoticzIP=sys.argv[3]
  print ('Serial Device: ' + sys.argv[1])
  print ('Port: ' + sys.argv[2])
  print ('Domoticz IP: ' + sys.argv[3])


  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()

I've cleaned up the plugin.py code a bit and included the code for the 'Stop' Command of a blind being translated into a 'C' telegram
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
#            03 16 : added 'Blinds' : assumption: Simple On/Off Switch can be changed via Switches/Select switch/Edit/Select type from 'On/Off' switch into 'Venetian Blind EU'
#                                                 Commands are 'On', 'Off', 'Stop' will be translated into A,B,C Easywave telegrams
#
"""
<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" # '

    # define dict Domoticz Command : Easywave telegram type e.g.: 'On' -> 'A'
    telegram = {'On':'A','Off':'B','Stop':'C'}
    update_value= {'On':1,'Off':0,'Stop':0}
    
    # 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
        Domoticz.Log("onCommand called for Unit " + str(Unit) + " : Hex : " + str(format( Unit -1 , '02X')) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
        if telegram.get(Command) is None:
            Domoticz.Log("Unknown Command: " + Command)
        else:    
            # Build Easywave telegram
            message = "TXP," + str(format( Unit -1 , '02X')) + "," + telegram.get(Command) + '\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(update_value.get(Command),Command)
            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
Will be interested to hear if this works,

Regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 11:46
by aeisenhuth
Hi WimR,
I used your revised script. Because of an injury on my right hand I could not test earlier.
In the meantime, I can select Hardware Easywave (Niko-Eldat) ;) and under Devices, 64 unused Easywave switches now appear. When I'm at home tonight I'll see if anything happens when I turn on and off. I will also try to change the fermentation to blind.
I give you in any case feedback.
Thanks in advance

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 11:59
by aeisenhuth
Hi WimR even though I'm not at home I have a blind inserted as Venetian Blind Eu and times on / Stop / pressed. Here is the first result.

018-03-19 11:57:04.539 (Easywave RX09 ) processing 'onHeartbeatCallback' message
2018-03-19 11:57:04.539 (Easywave RX09 ) Calling message handler 'onHeartbeat'.
2018-03-19 11:57:13.901 User: aeisenhuth initiated a switch command (104/Easywave RX09 - EasyWave TX - 00/Stop)
2018-03-19 11:57:13.901 (Easywave RX09 ) pushing 'onCommandCallback' on to queue
2018-03-19 11:57:13.906 (Easywave RX09 ) processing 'onCommandCallback' message
2018-03-19 11:57:13.907 (Easywave RX09 ) Calling message handler 'onCommand'.
2018-03-19 11:57:13.907 (Easywave RX09 ) onMessage called using socket params: 127.0.0.1 5331
2018-03-19 11:57:13.907 Error: (Easywave RX09 ) 'onCommand' failed 'ConnectionRefusedError'.
2018-03-19 11:57:13.907 Error: (Easywave RX09 ) ----> Line 111 in /home/pi/domoticz/plugins/Easywave/plugin.py, function onCommand
2018-03-19 11:57:14.536 (Easywave RX09 ) pushing 'onHeartbeatCallback' on to queue
2018-03-19 11:57:14.559 (Easywave RX09 ) processing 'onHeartbeatCallback' message
2018-03-19 11:57:14.559 (Easywave RX09 ) Calling message handler 'onHeartbeat'.
2018-03-19 11:57:24.538 (Easywave RX09 ) pushing 'onHeartbeatCallback' on to queue
2018-03-19 11:57:24.577 (Easywave RX09 ) processing 'onHeartbeatCallback' message
2018-03-19 11:57:24.577 (Easywave RX09 ) Calling message handler 'onHeartbeat'.

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 12:18
by WimR
Hi aeisenhuth,

it seems the command:
sock.connect(server_address)

responds with "connection refused"

Could it be that the Pi refuses an internal reference 127.0.0.1?
Could you launch a hyperterminal from another machine than where the rx09.py is running to connect to RX09.py via socket to check if its working as intended? Log /tmp/SerProxy.Log should tell you.
If the RX09.py is running as intended would it be a security issue?

Otherwise i'll have to install domoticz myself on the Pi to get to the same setup for testing purposes.
Can be done but not before the weekend.

regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 14:00
by aeisenhuth
rx09 jetzt ist: rx09.py / dev / ttyUSB0 5531 127.0.0.1 & I have not changed. After changing other errors.

2018-03-19 13:48:38.393 User: aeisenhuth initiated a switch command (104/Easywave RX09 - EasyWave TX - 00/On)
2018-03-19 13:48:38.393 (Easywave RX09 ) pushing 'onCommandCallback' on to queue
2018-03-19 13:48:38.406 (Easywave RX09 ) processing 'onCommandCallback' message
2018-03-19 13:48:38.406 (Easywave RX09 ) Calling message handler 'onCommand'.
2018-03-19 13:48:38.411 (Easywave RX09 ) onMessage called using socket params: 127.0.0.1 5331
2018-03-19 13:48:38.467 Error: (Easywave RX09 ) 'onCommand' failed 'ConnectionRefusedError'.
2018-03-19 13:48:38.467 Error: (Easywave RX09 ) ----> Line 111 in /home/pi/domoticz/plugins/Easywave/plugin.py, function onCommand

I can make a Hyperterminal to RPi on an XP VMware, but with what command? /tmp/SerProxy.Log don't exist on my RPi.
I could give you a TeamViewer share on my RPi if it suits you.

It would be a treat for me if my 11 blinds are automated.

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 15:11
by WimR
Hi aeisenhuth,

there should be a file "/tmp/SerProxy.Log" on the Pi if the rx09.py is running properly:
due to the line in the code:
logging.basicConfig(filename='/tmp/SerProxy.Log', filemode='w', format='%(asctime)s %(levelname)s:%(message)s', level=logging.DEBUG)

if that's not the case rx09.py isn't running and hence nothing is listening on the socket yielding the "Connection Refused".
You should be able to lauch the rx09.py manualy:
python rx09.py /dev/ttyUSB0 5331 127.0.0.1 &

It should create not only the logfile but also print on the terminal the parameters (device, port an domoticzIP)
Could you try this and tell the respons on the terminal?

If you connect the hyperterminal via TCP/raw to ipadress of the PI, port 5331 and the rx09.py is running you can just type TXP,01,A and the blinds should move.

regards

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 16:29
by aeisenhuth
Hi WimR,
ich have start the command: " python rx09.py /dev/ttyUSB0 5331 127.0.0.1 & " manually.
I am not at Home yet, but the Bilnds switch work in the surface and the Log looks good:

2018-03-19 16:25:41.491 User: aeisenhuth initiated a switch command (167/Easywave RX09 - EasyWave TX - 3F/On)
2018-03-19 16:25:41.491 (Easywave RX09 ) pushing 'onCommandCallback' on to queue
2018-03-19 16:25:41.509 (Easywave RX09 ) processing 'onCommandCallback' message
2018-03-19 16:25:41.509 (Easywave RX09 ) Calling message handler 'onCommand'.
2018-03-19 16:25:41.510 (Easywave RX09 ) onMessage called using socket params: 192.168.178.41 5331
2018-03-19 16:25:41.511 (Easywave RX09 ) onCommand called for Unit 64 : Hex : 3F: Parameter 'On', Level: 0
2018-03-19 16:25:42.513 (Easywave RX09 ) Response OK
2018-03-19 16:25:42.513 (Easywave RX09 - EasyWave TX - 3F) Updating device from 0:'' to have values 1:'On'.
2018-03-19 16:25:45.705 (Easywave RX09 ) pushing 'onHeartbeatCallback' on to queue
2018-03-19 16:25:45.727 (Easywave RX09 ) processing 'onHeartbeatCallback' message

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 16:32
by aeisenhuth
My wife just called me and told me it was dark in the dining room and office :D

Re: Eldat Easywave RXR09 Script

Posted: Monday 19 March 2018 16:38
by WimR
Hi Aeisenhuth,

glad to hear it.
Suppose its something with the startup of rx09.py.

Next step is to hit all your physical switches once (their individual switch-key numbers will show up in the SerProxy.Log).
Add them to devices.csv with their corresponding idx number, kill rx09.py, restart it and you should be in business :)

regards

Re: Eldat Easywave RXR09 Script

Posted: Tuesday 20 March 2018 19:18
by WimR
Hi Aeisenhuth,

i was wondering... :shock:
I suspect that the current setup may yield strange results when hitting a 'Stop' button on a physical switch.

In the current setup a command from a physical switch will be "echoed" by Domoticz due to the JSON call triggering the 'onCommand' and thus resending the command :?
That's no issue for an 'On' or 'Off' but a 'Stop' may yield not the desired effect. I suspect that the blind will then halt somewhere between the initial position and the 'Stop' position.

Have you tried linking a physical switch with one of the Domoticz 'blinds' paired to the same physical blind?
Could you than test the behaviour?

Using the 'Stop' on a Domoticz switch doesn't suffer from the echo effect and should work just fine.

regards

Re: Eldat Easywave RXR09 Script

Posted: Tuesday 20 March 2018 23:35
by aeisenhuth
Hello WimR,
So far I've learned a blind from the balcony. It works reliably, but the icons are displayed incorrectly. When open, the blind icon is closed and vice versa. The stop works fine. I have no switches, only remote controls from Eldat RT32E5004-01-02K; 4-channel hand-held transmitter. The remote control will continue to work. So far no abnormalities. All good. :D

Re: Eldat Easywave RXR09 Script

Posted: Wednesday 21 March 2018 11:29
by WimR
Hi aeisenhuth,

so it seems the coding A,B,C for blinds is inverted A=On=Closed, B=Off=Open in respect to simple switches A=On, B=Off and thus depending the switchtype.
I'll see if i can come up this weekend with a solution.

regards