Hello everyone,
for helping me migrating my KNX 1.x configuration to the new KNX 2.x binding and avoid “copy & paste” errors I wrote a “quick & dirty” Python script. As others might find this useful as well, I provide it here.
Some things to mention:
- The syntax to invoke the script:
migrateKNX.py -o -i migrate/test.items -t migrate/test.things my.items
-o: Includes the old KNX 1.x configuration as comment starting with // KNX 1.x: , so that it can later be easily removed. This is intended to ease debugging the script output
-i: The items file to write the new configuration to (files will be overwritten without warning!)
-t: The things file to write the new configuration to (files will be overwritten without warning!)
my.items: The file containing the old KNX 1.x configuration
-
This is no “call the script and everything is done solution”. The .things file does not contain any device information or bridge configuration. This has to be added manually. For every existing knx item the channel configuration is generated and the item configuration is adapted to that channel. If you use more than one KNX device in the configuration the “generic” string in the .items and device configuration has to be adapted as well.
-
Lines from the existing items file, not containing a KNX configuration will be written to the new .items file without modification. After adapting the device configuration manually I could just copy the .items file to my configuration folder.
-
The script will not work correctly when an item has more than one binding configuration. But in this cases the output can still be used to add the missing parts
-
The script will most certainly contain bugs. It worked for my configuration, but I can give no guarantee whatsoever, that it will work with your configuration. Make a backup of your existing configuration first, before using the script.
-
I was not allowed to upload a Python or text file, so I included it as code block. Maybe there is an other way to include the file without messing the Python indention?
-
I am not a Python specialist, so this script is certainly not a “Python best practices” example
Juelicher
#!/usr/bin/python3
from argparse import ArgumentParser
import re
itemNameWidth = 47
itemLabelWidth = 49
itemIconWidth = 15
itemGroupsWidth = 45
itemTagsWidth = 17
parser = ArgumentParser()
parser = ArgumentParser(description='Migrate openHAB KNX 1.x configuration to KNX 2.x')
parser.add_argument("-i", "--items", dest="items", type=str, default="knx.items.new", help="File to write newly generated items to")
parser.add_argument("-t", "--things", dest="things", type=str, default="knx.things.new", help="File to write newly generated items to")
parser.add_argument("-o", "--original", dest="original", action="store_true", default = False, help="Include original configuration in items and things file")
parser.add_argument("input", type=str, default="my.items", help="File to read current KNX 1.x configuration from")
args = parser.parse_args()
inFile = open(args.input, "r")
itemsFile = open(args.items, "w")
thingsFile = open(args.things, "w")
def removeComments(line):
#remove line comments
return re.sub(r"\/\/.*$", "", line)
def hasKNXConfiguration(line):
return re.match(r"^.*{.*knx\s*=.*}.*$", line)
def getItemType(line):
itemType = re.match(r"^\s*(Switch)|(Number)|(Dimmer)|(Contact)|(DateTime)|(Rollershutter)|(String)", line).group(0)
return itemType
def getItemName(line):
item = re.match(r"^\s*(?P<type>.+?)\s+(?P<name>.+?)\s+", line)
return item.group("name")
def getItemLabel(line):
itemLabel = ""
match = re.match(r"[^\[{]*?\s+(?P<label>\".+?\")\s+[^\]}]*", line)
if (match):
itemLabel = match.group("label")
return itemLabel
def getItemIcon(line):
itemIcon = ""
# Remove text inside quotes before matching to avoid matches within the label
match = re.match(r".*?\s+(?P<icon><.+?>)\s+.*?", re.sub(r"\"(.*?)\"", "", line))
if (match):
itemIcon = match.group("icon")
return itemIcon
def getItemGroup(line):
itemGroup = ""
# Remove text inside quotes before matching to avoid matches within the label
match = re.match(r".*?\s+(?P<group>\(.+?\))\s+.*?", re.sub(r"\"(.*?)\"", "", line))
if (match):
itemGroup = match.group("group")
return itemGroup
def getItemTags(line):
itemTags = ""
match = re.match(r".*?\s+(?P<tags>\[\s+\".*?\"\s+\])\s+.*", line)
if (match):
itemTags = match.group("tags")
return itemTags
def getItemKNXConfig(line):
itemKNXConfig = ""
match = re.match(r".*?\{\s*knx\s*=\s*\"(?P<knx>.*?)\"\s*,{0,1}.*\s*\}", line)
if (match):
itemKNXConfig = match.group("knx")
return itemKNXConfig
def getItemAutoUpdate(line):
itemAutoUpdate = ""
match = re.match(r".*?\{.*(?P<auto>autoupdate\s*=\s*\".+\")\s*\}", line)
if (match):
itemAutoUpdate = match.group("auto")
return itemAutoUpdate
def getKNXGroupAdress(line):
KNXGroupAdress = ""
#<*\d+\/\d+\/\d+
match = re.match(r"<*\d+\/\d+\/\d+", line)
if (match):
KNXGroupAdress = match.group("ga")
return itemKNXGroupAdress
def appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate):
return name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " " + icon.ljust(itemIconWidth) + groups.ljust(itemGroupsWidth) + tags.ljust(itemTagsWidth) + " { channel=\"knx:device:bridge:generic:" + name + "\" }\n"
def assembleSwitch(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type switch : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " [ ga=\"" + KNX + "\" ]\n"
itemsLine = "Switch " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleContact(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type contact : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " [ ga=\"" + KNX + "\" ]\n"
itemsLine = "Contact " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleString(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type string : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " [ ga=\"" + KNX + "\" ]\n"
itemsLine = "String " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleNumber(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type number : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " [ ga=\"" + KNX + "\" ]\n"
itemsLine = "Number " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleDateTime(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type datetime : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth) + " [ ga=\"" + KNX + "\" ]\n"
itemsLine = "DateTime " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleDimmer(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type dimmer : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth)
gaList = KNX.split(",")
if len(gaList) == 3:
onOff, increaseDecrease, percent = gaList[0].strip(), gaList[1].strip(), gaList[2].strip()
thingsLine = thingsLine + " [ switch=\"" + onOff + "\", position=\"" + percent + "\", increaseDecrease=\"" + increaseDecrease + "\" ]\n"
elif len(gaList) == 2:
onOff, increaseDecrease = gaList[0].strip(), gaList[1].strip()
thingsLine = thingsLine + " [ switch=\"" + onOff + "\", increaseDecrease=\"" + increaseDecrease + "\" ]\n"
elif len(gaList) == 1:
percent = gaList[0].strip()
thingsLine = thingsLine + " [ position=\"" + percent + "\" ]\n"
itemsLine = "Dimmer " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
def assembleRollerShutter(name, label, icon, groups, tags, KNX, autoupdate):
thingsLine = "Type rollershutter : " + name.ljust(itemNameWidth) + " " + label.ljust(itemLabelWidth)
gaList = KNX.split(",")
if len(gaList) == 3:
upDown, stopMove, percent = gaList[0].strip(), gaList[1].strip(), gaList[2].strip()
thingsLine = thingsLine + " [ upDown=\"" + upDown + "\", stopMove=\"" + stopMove + "\", position=\"" + percent + "\" ]\n"
elif len(gaList) == 2:
upDown, stopMove = gaList[0].strip(), gaList[1].strip()
thingsLine = thingsLine + " [ upDown=\"" + upDown + "\", stopMove=\"" + stopMove + "\" ]\n"
itemsLine = "Rollershutter " + appendItemConfig(name, label, icon, groups, tags, KNX, autoupdate)
return thingsLine, itemsLine
for inputLine in inFile:
clearedLine = removeComments(inputLine)
if (not hasKNXConfiguration(clearedLine)):
itemsFile.write(inputLine) # Write lines without KNX configuration unmodified to items file
continue
if (args.original):
itemsFile.write("\n// KNX 1.x: " + inputLine)
thingsFile.write("\n// KNX 1.x: " + inputLine)
itemType = getItemType(clearedLine)
newConfig = ""
if (itemType == "Switch"):
newConfig = assembleSwitch(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "Number"):
newConfig = assembleNumber(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "Dimmer"):
newConfig = assembleDimmer(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "Contact"):
newConfig = assembleContact(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "DateTime"):
newConfig = assembleDateTime(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "Rollershutter"):
newConfig = assembleRollerShutter(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
elif (itemType == "String"):
newConfig = assembleString(getItemName(clearedLine), getItemLabel(clearedLine), getItemIcon(clearedLine), getItemGroup(clearedLine), getItemTags(clearedLine), getItemKNXConfig(clearedLine),getItemAutoUpdate(clearedLine))
if (newConfig != ""):
thingsFile.write(newConfig[0])
itemsFile.write(newConfig[1])
inFile.close()
itemsFile.close()
thingsFile.close()