Browse Source

2020-11-30:
- integrated support for InterTechno remotes/devices with fixed code
no need to specify RX/TX commands, just specify a device ID like IT_A1, IT_C3 etc.

FloKra 3 years ago
parent
commit
7e9a038d4c
3 changed files with 379 additions and 141 deletions
  1. 8 0
      CHANGELOG.txt
  2. 344 133
      src/cul2mqtt.py
  3. 27 8
      src/cul2mqtt_devices.yaml

+ 8 - 0
CHANGELOG.txt

@@ -0,0 +1,8 @@
+2020-11-30:
+  - integrated support for InterTechno remotes/devices with fixed code
+    no need to specify RX/TX commands, just specify a device ID like IT_A1, IT_C3 etc. 
+	
+2020-04-06: 
+  - devices configuration using YAML file (cul2mqtt_devices.yaml)
+  - calculate TX code from RX code for InterTechno compatible devices (code from FHEM project ported to Python)
+    no need any more to identify and specify TX codes for these devices, just specify the RX code and the TX is generated on the fly when needed

+ 344 - 133
src/cul2mqtt.py

@@ -10,58 +10,58 @@ import serial
 import sys
 import time
 from time import sleep
+from time import localtime, strftime
 import datetime
 import paho.mqtt.client as mqtt
+import json
 import os
 
-version = 0.1
+version = 0.2
 
-verbose = True
+verbose = False
 debug = False
 
 deviceConfigFile = "/home/pi/cul2mqtt_devices.yaml"
 
+#Log levels: DEBUG, INFO, WARNING, ERROR or CRITICAL
+#loglevel = "DEBUG"
+log_enable = True
+log_path = '/home/pi/logs/cul2mqtt'
 
-mqtt_server = "mqtt.lan"
+mqtt_server = "10.1.1.11"
 mqtt_port = 1883
-mqtt_user = ""
-mqtt_password = ""
+mqtt_user = "admin"
+mqtt_password = "2Bierum4"
 
-TX_interface_prefer = "UART" # UART or MQTT
+TX_interface_prefer = "MQTT" # UART or MQTT
 
-repeat_received_commands = False # does not yet work correctly
+repeat_received_commands = True # not yet implemented
 
 if len(sys.argv) >= 2:
     if sys.argv[1] == "-q":
         verbose = False
     elif sys.argv[1] == "-v":
         verbose = True
-    elif sys.argv[1] == "-d":
-        debug = True
-        verbose = True
 
 # serial (USB) CUL device
 receive_from_serial_cul = True
 send_on_serial_cul = True
 serialPort = '/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0'
 serialBaudrate = 38400
-serialTimeout = 1 # s, should be not too high, otherwise program could block
-culInitCmd = 'X21\r\n' # CUL init command for normal operation - should not be changed normally
-culInitTimeout = 3 # s
-forceSerialCULConnected = True # if true, program exits when serial CUL cannot be connected to
+serialTimeout = 1
+culInitCmd = 'X21\r\n' # CUL init command for normal operation
+culInitTimeout = 3
+forceSerialCULConnected = False
+serialCULAvailable = False
 
 # MQTT CUL
-receive_from_mqtt_cul = False
-send_on_mqtt_cul = False
-mqtt_topic_cul_received = "MQTT-CUL/received"
-mqtt_topic_cul_send = "MQTT-CUL/send"
-
-filterSelfSentIncomingTimeout = 500
+receive_from_mqtt_cul = True
+send_on_mqtt_cul = True
+mqtt_topic_cul_received = "T5/CUL/received"
+mqtt_topic_cul_send = "T5/CUL/send"
 
 
-
-# do not change below
-serialCULAvailable = False
+filterSelfSentIncomingTimeout = 100
 
 devdata = {}
 RXcodesToDevFunction = {}
@@ -85,38 +85,88 @@ def no_duplicates_constructor(loader, node, deep=False):
 
 yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, no_duplicates_constructor)
 
-print("CUL2MQTT v" + str(version))
-print("=====================================")
+
+log_last_date = None
+logfilehandle = False
+
+
+def log_start():
+    global logfilehandle, log_last_date
+    if log_enable:
+        if not os.path.exists(log_path):
+            os.makedirs(log_path)
+            
+        try:
+            _log_current_date = strftime("%Y%m%d")
+            #_logfilename = _log_current_date + "_" + logfile_name
+            _logfilename = _log_current_date + ".log"
+            logfilehandle = open(log_path + '/' + _logfilename, 'a')
+            log_last_date = _log_current_date
+        except:
+            pass
+
+
+def log_rotate():
+    global logfilehandle, log_last_date
+    if log_enable:
+        _log_current_date = strftime("%Y%m%d")
+        
+        if log_last_date != _log_current_date:
+            try:
+                logfilehandle.close()
+                _logfilename = _log_current_date + ".log"
+                logfilehandle = open(log_path + '/' + _logfilename, 'a')
+                log_last_date = _log_current_date
+            except:
+                pass
+            
+            
+def log_write(_msg):
+    global logfilehandle
+    print(_msg)
+    log_rotate()
+    if log_enable:
+        try:
+            logfilehandle.write("[" + str(datetime.datetime.now()) + "] " + _msg + "\n")
+            logfilehandle.flush()
+        except:
+            # guat dann hoit ned...
+            pass
+
+log_start()
+
+log_write("CUL2MQTT v" + str(version))
+log_write("=====================================")
 
 try:
     with open(deviceConfigFile) as devfile:
         devdata = yaml.load(devfile, Loader=yaml.FullLoader)
         if debug:
-            print()
-            print()
-            print("==== parsing config file ====")
-            print()
-            print(devdata)
-            print()
+            log_write("")
+            log_write("")
+            log_write("==== parsing config file ====")
+            log_write("")
+            log_write(devdata)
+            log_write("")
         
         for deviceid in devdata:
             if debug:
-                print()
-                print()
-                print("Device: " + deviceid)
-                print(devdata[deviceid])
+                log_write("")
+                log_write("")
+                log_write("Device: " + deviceid)
+                log_write(devdata[deviceid])
             
             if "RX" in devdata[deviceid].keys():
                 if devdata[deviceid]["RX"] != "":
                     if debug:
-                        print("RX codes:")
+                        log_write("RX codes:")
                     for key, value in devdata[deviceid]["RX"].items():
                         if debug:
-                            print(key, "->", value)
+                            log_write(key, "->", value)
                         if value in RXcodesToDevFunction.keys():
-                            print()
-                            print()
-                            print("ERROR: RX-string '" + str(value) + "' is already defined for another device! Must be unique.")
+                            log_write("")
+                            log_write("")
+                            log_write("ERROR: RX-string '" + str(value) + "' is already defined for another device! Must be unique.")
                             raise
                         else:
                             RXcodesToDevFunction[value] = deviceid, key
@@ -125,43 +175,43 @@ try:
                 if devdata[deviceid]["cmdTopic"] != "":
                     cmdTopic = devdata[deviceid]["cmdTopic"]
                     if debug:
-                        print("cmdTopic: " + cmdTopic)
+                        log_write("cmdTopic: " + cmdTopic)
                     if cmdTopic in InTopicsToDevIds.keys():
-                        print()
-                        print()
-                        print("ERROR: cmdTopic '" + str(cmdTopic) + "' is already defined for another device! Must be unique.")
+                        log_write("")
+                        log_write("")
+                        log_write("ERROR: cmdTopic '" + str(cmdTopic) + "' is already defined for another device! Must be unique.")
                         raise
                     else:
                         InTopicsToDevIds[cmdTopic] = deviceid
             
         if debug:
-            print()
-            print()
-            print()
-            print("RXcodesToDevFunction:")
-            print(RXcodesToDevFunction)
-            print()
-            print()
-            print("InTopicsToDevIds:")
-            print(InTopicsToDevIds)
-            print()
-            print()
-            print("InTopicsToDevIds.keys():")
-            print(InTopicsToDevIds.keys())
-            print()
-            print()
-            print("==== parsing config file complete ====")
-            print()
-            print()
-            print()
-            print()
+            log_write("")
+            log_write("")
+            log_write("")
+            log_write("RXcodesToDevFunction:")
+            log_write(RXcodesToDevFunction)
+            log_write("")
+            log_write("")
+            log_write("InTopicsToDevIds:")
+            log_write(InTopicsToDevIds)
+            log_write("")
+            log_write("")
+            log_write("InTopicsToDevIds.keys():")
+            log_write(InTopicsToDevIds.keys())
+            log_write("")
+            log_write("")
+            log_write("==== parsing config file complete ====")
+            log_write("")
+            log_write("")
+            log_write("")
+            log_write("")
 
 except ConstructorError as err:
-    print("ERROR on parsing configfile:")
-    print(err)
+    log_write("ERROR on parsing configfile:")
+    log_write(err)
     exit(1)
 except:
-    print("ERROR opening configfile")
+    log_write("ERROR opening configfile")
     exit(1)
 
 
@@ -198,7 +248,7 @@ def touch(fname, times=None):
 
 def on_connect(client, userdata, flags, rc):
     if verbose:
-        print("MQTT connected with result code " + str(rc))
+        log_write("MQTT connected with result code " + str(rc))
     if receive_from_mqtt_cul:
         if mqtt_topic_cul_received != "":
             client.subscribe(mqtt_topic_cul_received)
@@ -206,21 +256,22 @@ def on_connect(client, userdata, flags, rc):
     for in_topic in InTopicsToDevIds.keys():
         if in_topic != "":
             client.subscribe(in_topic)
-            print("MQTT subscribed: " + in_topic)
+            if verbose:
+                log_write("MQTT subscribed: " + in_topic)
 
 def on_disconnect(client, userdata, rc):
     if rc != 0:
-        print("Unexpected MQTT disconnection. Will auto-reconnect")
+        log_write("Unexpected MQTT disconnection. Will auto-reconnect")
         
 def on_message(client, userdata, msg):
     #print(msg.topic + ": " + str(msg.payload))
     
     payload = msg.payload.decode("utf-8")
-    print(msg.topic + ": " + str(payload))
-    
     
+    if verbose:
+        log_write("MQTT received: " + msg.topic + " -> " + str(payload))
     
-    #print()
+    #log_write("")
     #print("TOPIC: ")
     #print(msg.topic)
     #print("TOPIC REC: ")
@@ -229,31 +280,33 @@ def on_message(client, userdata, msg):
     # MQTT message is output from CUL
     if receive_from_mqtt_cul and msg.topic == mqtt_topic_cul_received:
         payload = payload.rstrip()
-        print("MQTT-CUL RX: '" + payload + "'")
-        cul_received(payload)
+        if verbose:
+            log_write("")
+            log_write("MQTT-CUL RX: '" + payload + "'")
+        cul_received(payload, "MQTT")
         
     else:
         for in_topic, dev in InTopicsToDevIds.items():
             if msg.topic == in_topic:
-                print()
-                print("MQTT command received - Topic: '" + msg.topic + "', Payload: '" + payload + " -> Device: '" + dev + "'")
+                #log_write("")
+                log_write("MQTT received - '" + msg.topic + "' = '" + payload + "' => DEV: " + dev)
                 #print(devdata[dev])
                 
                 if 'name' in devdata[dev].keys():
-                    print('Device Name: ' + devdata[dev]['name'])
+                    log_write('devName: ' + devdata[dev]['name'])
                 
                 if 'statTopic' in devdata[dev].keys():
-                    print('statTopic: ' + devdata[dev]['statTopic'])
-                    #mqttc.publish(devdata[dev]['statTopic'], payload, qos=0, retain=False)
+                    log_write('statTopic: ' + devdata[dev]['statTopic'])
+                    mqttc.publish(devdata[dev]['statTopic'], payload, qos=0, retain=False)
                     
                 if 'add_statTopics_on' in devdata[dev].keys():
-                    print("add_statTopics_on:")
+                    log_write("add_statTopics_on:")
                     for res in devdata[dev]['add_statTopics_on']:
                         #print(res)
                         if 'on_payload' in res and 'topic' in res and 'payload' in res:
                             if payload == res['on_payload'] and payload != "" and res['topic'] != "" and res['payload'] != "":
-                                print("  on '" + payload + "' -> publish '" + res['payload'] + "' on topic '" + res['topic'] + "'")
-                                #mqttc.publish(res[topic], res[payload], qos=0, retain=False)
+                                log_write("  on '" + payload + "': '" + res['payload'] + "' => '" + res['topic'] + "'")
+                                mqttc.publish(res[topic], res[payload], qos=0, retain=False)
                                 
                 #if 'TX' in devdata[dev].keys():
                 #    if payload in devdata[dev]['TX']:
@@ -264,9 +317,11 @@ def on_message(client, userdata, msg):
                 
                 global lastSentDev, lastSentDevCmd, lastSentCmdTime
                 now = int(round(time.time() * 1000))
-                print("dev="+dev+", lastSentDevCmd="+lastSentDevCmd)
+                if debug:
+                    log_write("dev="+dev+", lastSentDevCmd="+lastSentDevCmd)
                 if dev == lastSentDev and payload == lastSentDevCmd and (now - lastSentCmdTime) < 1000:
-                    print("MQTT: ignored command as we just sent this.")
+                    if verbose:
+                        log_write("MQTT: ignored command as we just sent this.")
                     pass
                 else:
                     cul_send(dev, payload)
@@ -283,35 +338,176 @@ def on_message(client, userdata, msg):
 
                     
 def publish_device_statusupdate(device, cmd):
-    if 'statTopic' in devdata[device].keys():
-        statTopic = devdata[device]['statTopic']
-        print("MQTT publish statTopic: " + statTopic + " -> " + cmd)
-        mqttc.publish(statTopic, cmd, qos=0, retain=False)
-    
-    if 'add_statTopics_on' in devdata[device].keys():
-        print("  MQTT publish add_statTopics_on:")
-        for res in devdata[device]['add_statTopics_on']:
-            #print(res)
-            if 'on_payload' in res and 'topic' in res and 'payload' in res:
-                if cmd == res['on_payload']:
-                    print("    on '" + res['on_payload'] + "' -> publish '" + res['payload'] + "' on topic '" + res['topic'] + "'")
-                    mqttc.publish(res['topic'], res['payload'], qos=0, retain=False)
-                    
-    if 'add_statTopics' in devdata[device].keys():
-        print("  MQTT publish on add_statTopics:")
-        for res in devdata[device]['add_statTopics']:
-            print("    '" + cmd + "' -> '" + res + "'")
-            mqttc.publish(res, cmd, qos=0, retain=False)
+    if device in devdata.keys():
+        if 'statTopic' in devdata[device].keys():
+            statTopic = devdata[device]['statTopic']
+            log_write("MQTT publish: '" + cmd + "' -> '" + statTopic + "'")
+            mqttc.publish(statTopic, cmd, qos=0, retain=False)
         
+        if 'add_statTopics_on' in devdata[device].keys():
+            log_write("  MQTT publish add_statTopics_on:")
+            for res in devdata[device]['add_statTopics_on']:
+                #print(res)
+                if 'on_payload' in res and 'topic' in res and 'payload' in res:
+                    if cmd == res['on_payload']:
+                        log_write("    on '" + res['on_payload'] + "' -> publish '" + res['payload'] + "' on topic '" + res['topic'] + "'")
+                        mqttc.publish(res['topic'], res['payload'], qos=0, retain=False)
+                        
+        if 'add_statTopics' in devdata[device].keys():
+            log_write("  MQTT publish on add_statTopics:")
+            for res in devdata[device]['add_statTopics']:
+                log_write("    '" + cmd + "' -> '" + res + "'")
+                mqttc.publish(res, cmd, qos=0, retain=False)
         
-def parseRXCode(rx_code):
+
+
+def parseRXCode(rx_code, source_cul):
+    receivedForDevice = None
+    receivedCmnd = None
     if rx_code in RXcodesToDevFunction:
         receivedForDevice = RXcodesToDevFunction[rx_code][0]
         receivedCmnd = RXcodesToDevFunction[rx_code][1]
-        print("receivedForDevice = " + receivedForDevice + ", receivedCmnd = " + receivedCmnd)
+        #print("receivedForDevice = " + receivedForDevice + ", receivedCmnd = " + receivedCmnd)
+        #log_write("DEV: " + receivedForDevice + ", CMD: " + receivedCmnd + ", RX: " + rx_code)
+        log_write("")
+        log_write("CUL '" + source_cul + "' received '" + rx_code + "' => DEV: " + receivedForDevice + ", CMD: " + receivedCmnd)
+    else:
+        if rx_code.startswith("i"):
+            receivedForDevice, receivedCmnd = decodeInterTechnoRX(rx_code)
+            print(receivedForDevice, receivedCmnd)
+    
+    if receivedForDevice != None and receivedCmnd != None:
         publish_device_statusupdate(receivedForDevice, receivedCmnd)
+
+def decodeInterTechnoRX(rx_code):
+    # decode old fixed code from InterTechno remotes
+    _housecode = None
+    _devaddr = None
+    _command = None
+    _itname = None
+    
+    #print(rx_code[0:1])
+    #print(rx_code[1:3])
+    #print(rx_code[3:5])
+    #print(rx_code[5:7])
     
-def cul_received(payload):
+    if rx_code[0:1] == "i": 
+        if rx_code[1:3] == "00": _housecode = "A"
+        elif rx_code[1:3] == "40": _housecode = "B"
+        elif rx_code[1:3] == "10": _housecode = "C"
+        elif rx_code[1:3] == "50": _housecode = "D"
+        elif rx_code[1:3] == "04": _housecode = "E"
+        elif rx_code[1:3] == "44": _housecode = "F"
+        elif rx_code[1:3] == "14": _housecode = "G"
+        elif rx_code[1:3] == "54": _housecode = "H"
+        elif rx_code[1:3] == "01": _housecode = "I"
+        elif rx_code[1:3] == "41": _housecode = "J"
+        elif rx_code[1:3] == "11": _housecode = "K"
+        elif rx_code[1:3] == "51": _housecode = "L"
+        elif rx_code[1:3] == "05": _housecode = "M"
+        elif rx_code[1:3] == "45": _housecode = "N"
+        elif rx_code[1:3] == "15": _housecode = "O"
+        elif rx_code[1:3] == "55": _housecode = "P"
+        
+        if rx_code[3:5] == "00": _devaddr = "1"
+        elif rx_code[3:5] == "40": _devaddr = "2"
+        elif rx_code[3:5] == "10": _devaddr = "3"
+        elif rx_code[3:5] == "50": _devaddr = "4"
+        elif rx_code[3:5] == "04": _devaddr = "5"
+        elif rx_code[3:5] == "44": _devaddr = "6"
+        elif rx_code[3:5] == "14": _devaddr = "7"
+        elif rx_code[3:5] == "54": _devaddr = "8"
+        elif rx_code[3:5] == "01": _devaddr = "9"
+        elif rx_code[3:5] == "41": _devaddr = "10"
+        elif rx_code[3:5] == "11": _devaddr = "11"
+        elif rx_code[3:5] == "51": _devaddr = "12"
+        elif rx_code[3:5] == "05": _devaddr = "13"
+        elif rx_code[3:5] == "45": _devaddr = "14"
+        elif rx_code[3:5] == "15": _devaddr = "15"
+        elif rx_code[3:5] == "55": _devaddr = "16"
+        
+        if rx_code[5:7] == "15": _command = "ON"
+        elif rx_code[5:7] == "14": _command = "OFF"
+        
+        if _housecode != None and _devaddr != None and _command != None:
+            _itname = "IT_" + _housecode + _devaddr
+            #print("valid IT code: '" + _itname + "' => '" + _command + "'")
+            return _itname, _command
+            
+        else:
+            return False
+            
+        #else:
+        #    print("unknown or invalid IT code '" + rx_code + "'")
+        
+    #else:
+    #    print("unknown or invalid IT code '" + rx_code + "'")
+
+def encodeInterTechnoRX(itname, cmd):
+    # decode old fixed code from InterTechno remotes
+    _housecode = None
+    _devaddr = None
+    _command = None
+    
+    #print(itname[0:3])
+    #print(itname[3:4])
+    #print(itname[4:])
+    
+    if itname[0:3] == "IT_": 
+        if itname[3:4] == "A": _housecode = "00"
+        elif itname[3:4] == "B": _housecode = "40"
+        elif itname[3:4] == "C": _housecode = "10"
+        elif itname[3:4] == "D": _housecode = "50"
+        elif itname[3:4] == "E": _housecode = "04"
+        elif itname[3:4] == "F": _housecode = "44"
+        elif itname[3:4] == "G": _housecode = "14"
+        elif itname[3:4] == "H": _housecode = "54"
+        elif itname[3:4] == "I": _housecode = "01"
+        elif itname[3:4] == "J": _housecode = "41"
+        elif itname[3:4] == "K": _housecode = "11"
+        elif itname[3:4] == "L": _housecode = "51"
+        elif itname[3:4] == "M": _housecode = "05"
+        elif itname[3:4] == "N": _housecode = "45"
+        elif itname[3:4] == "O": _housecode = "15"
+        elif itname[3:4] == "P": _housecode = "55"
+        
+        if itname[4:] == "1": _devaddr = "00"
+        elif itname[4:] == "2": _devaddr = "40"
+        elif itname[4:] == "3": _devaddr = "10"
+        elif itname[4:] == "4": _devaddr = "50"
+        elif itname[4:] == "5": _devaddr = "04"
+        elif itname[4:] == "6": _devaddr = "44"
+        elif itname[4:] == "7": _devaddr = "14"
+        elif itname[4:] == "8": _devaddr = "54"
+        elif itname[4:] == "9": _devaddr = "01"
+        elif itname[4:] == "10": _devaddr = "41"
+        elif itname[4:] == "11": _devaddr = "11"
+        elif itname[4:] == "12": _devaddr = "51"
+        elif itname[4:] == "13": _devaddr = "05"
+        elif itname[4:] == "14": _devaddr = "45"
+        elif itname[4:] == "15": _devaddr = "15"
+        elif itname[4:] == "16": _devaddr = "55"
+        
+        if cmd == "ON": _command = "15"
+        elif cmd == "OFF": _command = "14"
+        
+        #print("IT housecode=", _housecode, "- devaddr=", _devaddr, "- command=", _command)
+                
+        if _housecode != None and _devaddr != None and _command != None:
+            _rxcode = "i" + _housecode + _devaddr + _command
+            #print("encoded IT RX code: '" + itname + "' => '" + cmd + "' = '" + _rxcode)
+            return _rxcode
+            
+        else:
+            return False
+            
+        #else:
+        #    print("unknown or invalid IT code '" + rx_code + "'")
+        
+    #else:
+    #    print("unknown or invalid IT code '" + rx_code + "'")    
+
+def cul_received(payload, source_cul):
     #global lastAction_bwm1, lastAction_bwm2, lastAction_bwm3
     global lastReceivedTime, lastReceivedIndex, lastReceivedMaxAge
     
@@ -323,7 +519,8 @@ def cul_received(payload):
         
     elif payload[:1] == 'i': # is a IT compatible command - so look it up in the code table
         inCmd = payload[:-2] #strip last 2 chars, these only represent signal strength and are not needed
-        print("inCmd: " + inCmd)
+        if verbose:
+            log_write("inCmd: " + inCmd + ", CUL: " + source_cul)
                             
         
         ignoreCommand = False
@@ -337,7 +534,7 @@ def cul_received(payload):
                 tdelta = (now - lastTime)
                 #print("TDELTA = " + str(tdelta))
                 if tdelta < lastReceivedMaxAge:
-                    print("ignoring received command '" + inCmd + "' - already received " + str(tdelta) + " ms ago")
+                    log_write("ignoring command from CUL '" + source_cul + "', CMD: '" + inCmd + "' - already received " + str(tdelta) + " ms ago")
                     ignoreCommand = True
                     #break
             i += 1
@@ -350,7 +547,7 @@ def cul_received(payload):
             else:
                 lastReceivedIndex += 1
                 
-            parseRXCode(inCmd)
+            parseRXCode(inCmd, source_cul)
                             
             #if repeat_received_commands:
             #    lastSentLength = len(lastSent)
@@ -412,7 +609,7 @@ def IT_RXtoTXCode(itReceiveCode):
         statusstr += " -> TX: "
         statusstr += itTransmitTristate
         #print("IT_RXtoTXCode TransmitCode: " + itTransmitTristate)
-        print(statusstr)
+        log_write(statusstr)
     
     return(itTransmitTristate)
         
@@ -439,19 +636,28 @@ def cul_send(device, cmd):
         #print(devdata[device]['TX'])
         if cmd in devdata[device]['TX']:
             tx_code = devdata[device]['TX'][cmd]
-            print("    TX code for '" + cmd + "': " + tx_code)
+            log_write("    TX code for '" + cmd + "': " + tx_code)
             
     if not tx_code:
+        print("    deviceID: ", device)
         if 'RX' in devdata[device]:
-            #print("RX data available, cmd="+cmd)
-            #print(devdata[device]['RX'])
+            print("    RX code configured, cmd=" + cmd)
+            print(devdata[device]['RX'])
             if cmd in devdata[device]['RX']:
                 rx_code = devdata[device]['RX'][cmd]
-                #print("RX code for '" + cmd + "': " + rx_code)
+                print("    RX code for '" + cmd + "': " + rx_code)
+                tx_code = IT_RXtoTXCode(rx_code)
+                log_write("    TX code for '" + cmd + "': " + tx_code)
+        elif device.startswith("IT_"):
+            # InterTechno device with fixed code - encode RX code for IT device name and convert to TX code
+            rx_code = encodeInterTechnoRX(device, cmd)
+            if rx_code:
+                print("    RX code for '" + cmd + "': " + rx_code)
                 tx_code = IT_RXtoTXCode(rx_code)
-                print("    TX code for '" + cmd + "': " + tx_code)
+                log_write("    TX code for '" + cmd + "': " + tx_code)
+            
     if not tx_code:
-        print("    no valid TX code for this device/command")
+        log_write("    no valid TX code for this device/command")
     else:
         now = int(round(time.time() * 1000))
         
@@ -462,14 +668,14 @@ def cul_send(device, cmd):
             lastTime = 0
             
         lastTimeAge = now - lastTime
-        print('    lastTime: ' + str(lastTimeAge) + 'ms ago')
+        log_write('    lastTime: ' + str(lastTimeAge) + 'ms ago')
         
         if lastTimeAge > lastSentMinInterval: # only send if last time + min interval is exceeded
             lastSentTime[culSendCmdsKeyName] = now # save what we send, so that we dont repeat our own sent messages if repeating is enabled
             
             TX_interface = TX_interface_prefer
             if 'TX_interface' in devdata[device].keys():
-                print("    TX_interface: " + devdata[device]['TX_interface'])
+                log_write("    TX_interface: " + devdata[device]['TX_interface'])
                 
             if TX_interface == "UART" and not serialCULAvailable:
                 TX_interface = "MQTT"
@@ -481,34 +687,38 @@ def cul_send(device, cmd):
             lastSentDevCmd = cmd
             
             if send_on_mqtt_cul and (TX_interface == "MQTT" or TX_interface == "both"):
-                print("    TX via MQTT: " + tx_code)
+                log_write("    TX via MQTT: " + tx_code)
                 mqttc.publish(mqtt_topic_cul_send, tx_code, qos=0, retain=False)
                 
             if serialCULAvailable and send_on_serial_cul and (TX_interface == "UART" or TX_interface == "both"):
-                print("    TX via UART: " + tx_code)
+                log_write("    TX via UART: " + tx_code)
                 culCmd = tx_code + '\r\n'
                 ser.write(culCmd.encode('utf-8'))
                 
         else:
-            print("WARNING: CUL send command repeated too quickly.")
+            log_write("WARNING: CUL send command repeated too quickly.")
             
 
+
+# main
 if receive_from_serial_cul or send_on_serial_cul:
     if not os.path.exists(serialPort):
-        print("ERROR opening connection to serial CUL... device '" + serialPort + "' does not exist.")
+        log_write("ERROR opening connection to serial CUL... device '" + serialPort + "' does not exist.")
+        if log_enable: writeLog("CUL2MQTT v"+version+" starting")
+        
         if receive_from_mqtt_cul:
             if forceSerialCULConnected:
                 exit(2)
             else:
-                print("resuming in MQTT-CUL only mode...")
+                log_write("resuming in MQTT-CUL only mode...")
                 TX_interface_prefer = "MQTT"
                 receive_from_serial_cul = False
                 send_on_serial_cul = False
                 serialCULAvailable = False
-                print()
-                print()
+                log_write("")
+                log_write("")
     else:
-        print("opening connection to serial CUL...")
+        log_write("opening connection to serial CUL...")
         serLine = ""
         ser = serial.Serial(port=serialPort,baudrate=serialBaudrate,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=serialTimeout)
         sleep(culInitTimeout)
@@ -516,14 +726,14 @@ if receive_from_serial_cul or send_on_serial_cul:
         serLine = ser.readline()
         serLine = serLine.decode('utf-8').rstrip('\r\n')
         if serLine.startswith("V ") and serLine.find("culfw") != -1:
-            print("connected. CUL version: " + serLine)
+            log_write("connected. CUL version: " + serLine)
             serialCULAvailable = True
             sleep(0.1)
-            print('Initializing CUL with command: ' + culInitCmd.rstrip('\r\n'))
+            log_write('Initializing CUL with command: ' + culInitCmd.rstrip('\r\n'))
             ser.write(culInitCmd.encode('utf-8'))  # initialize CUL in normal receive mode
             sleep(0.5)
         else:
-            print("WARNING: could not connect serial CUL")
+            log_write("WARNING: could not connect serial CUL")
             receive_from_serial_cul = False
             send_on_serial_cul = False
             serialCULAvailable = False
@@ -556,9 +766,10 @@ while True:
             if recvCmd == lastSentCmd and (now - lastSentCmdTime) < filterSelfSentIncomingTimeout:
                 pass
             else:
-                print()
-                print("Serial-CUL RX: '" + recvCmd + "'")
-                cul_received(recvCmd)
+                if verbose:
+                    log_write("")
+                    log_write("Serial-CUL RX: '" + recvCmd + "'")
+                cul_received(recvCmd, "UART")
         
     sleep(0.05)
     

+ 27 - 8
src/cul2mqtt_devices.yaml

@@ -1,12 +1,20 @@
+# No need any more to specify RX codes for InterTechno fixed code devices. 
+# Just use a device ID starting with "IT_" and containing the house code and device number, i.E. "IT_A1", "IT_C4" etc.
+# TX codes for all InterTechno compatible devices are generated on the fly, so also no need to get and specify them here. 
+# name: internal name, seen in log files and verbose output
+# statTopic: MQTT topic to publish to when a valid RX code for this device was received
+#            can be used to control a Tasmota device from an RF remote
+# cmdTopic: MQTT topic to subscribe to (to control a RF device like a InterTechno plug or a ceiling fan remote from i.E. Home Assistant)
+
 "IT_A1":
   name: "InterTechno A1"
   #"TX":
   #  "ON": "is000000000FFF"
   #  "OFF": "is000000000FF0"
-  "RX":
-    "ON": "i000015"
-    "OFF": "i000014"
-  statTopic: "cmnd/T5-AZ-Deckenlampe/POWER"
+  #"RX":
+  #  "ON": "i000015"
+  #  "OFF": "i000014"
+  statTopic: "cmnd/Lamp1/POWER"
   #cmdTopic: ""
 
 "IT_A2":
@@ -14,10 +22,21 @@
   #"TX":
   #  "ON": "is0000F0000FFF"
   #  "OFF": "is0000F0000FF0"
-  "RX":
-    "ON": "i004015"
-    "OFF": "i004014"
-  statTopic: "cmnd/T5-AZ-Stehlampe/POWER"
+  #"RX":
+  #  "ON": "i004015"
+  #  "OFF": "i004014"
+  statTopic: "cmnd/Lamp2/POWER"
+  #cmdTopic: ""
+  
+"IT_F9":
+  name: "InterTechno F9"
+  #"TX":
+  #  "ON": "is0000F0000FFF"
+  #  "OFF": "is0000F0000FF0"
+  #"RX":
+  #  "ON": "i004015"
+  #  "OFF": "i004014"
+  statTopic: "cmnd/SomeOtherLamp/POWER"
   #cmdTopic: ""
 
 "FAN_WZ":