Converting UI items back to *.items File

Hy

Almost 1.5 months ago I switched to OH3 from my OH2 machine. That worked great and at the same time I had a first hand on the semantics model and all that belongs to it. In my OH2 times I manged all my configurations in my text files since it was way easier to implement a new sensor by copy & paste and adjusting the name.
But as I saw the whole semantics and the Model thing I decided to switch to full UI configuration and only leave my rules text based.

Now 1.5 months later I really struggle to edit several items at once since it needs so many mouse clicks where in the text based files a replace command can do this in seconds.
So I’m conveting back all my Items to the *.items text files. Ohhhh boy…

First I tried to do it manually by looking at the *json files in the userdata folder but this took too long so I wrote a small python script which does the job for me.

The script is far from finished but it might help others if they have the same feelings about UI inputs as me.
Currently it converts:

  • itemType
  • name
  • label
  • category / icon
  • Group
  • Tag
  • Channel Link (only tested mqtt)
  • stateDescription
  • expire
  • widget
  • cellWidget
  • autoupdate
  • listWidget

So as you can see it does not convert Alexa, HomeKit, GA, Synonyms etc.
Since I don’t use them I can’t verify if they’re working but I think it’s rather easy to add those to the script if you need them.

So here is the script:
PLS ONLY USE IT WHEN YOU’RE SURE WHAT YOU’RE DOING

It needs a copy of those for files located in the same directory as the script:
org.openhab.core.items.Item.json
org.openhab.core.items.Metadata.json
org.openhab.core.thing.link.ItemChannelLink.json
org.openhab.core.thing.Thing.json

When finished it writes all Items to the export.items file where you can sort them take a few out and make your own *.items files as you desire.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json

print("pls wait...")

exportStr = []
with open('org.openhab.core.items.Item.json') as json_file:
	data = json.load(json_file)
	
	for p in data:				
		# itemType
		itemType = (data[p]['value']['itemType'] + " ") 
		
		# name
		name = str(p)
	
		# label
		label = ""
		if 'label' not in data[p]['value']:
			label = (" \"" + name + "\"")
		else:			
			label = (" \"" + data[p]['value']['label'] + "\"")
			
		# category
		category = ""
		if 'category' in data[p]['value']:		
			category = data[p]['value']['category']
			if category:
				category = (" <" + category + ">")
		
		# groupNames
		groupNames = ""
		if 'groupNames' in data[p]['value']:			
			groupNames = ""
			for erg in data[p]['value']['groupNames']:
				groupNames = groupNames + erg + ", "
			if groupNames:
				groupNames = (" (" + groupNames.rstrip(', ') + ")")
		
		# tags
		tags = ""
		if 'tags' in data[p]['value']:			
			tags = ""
			for ergTags in data[p]['value']['tags']:
				tags = tags + "\"" + ergTags + "\", "
			if tags:
				tags = (" [" + tags.rstrip(', ') + "]")
					
		
		
		# ItemChannelLink
		channel = ""
		uid = ""
		with open('org.openhab.core.thing.link.ItemChannelLink.json') as json_file_links:
			dataLinks = json.load(json_file_links)		
			# print(dataLinks)
			for pLinks in dataLinks:
				if name == dataLinks[pLinks]['value']['itemName']:
					uid = dataLinks[pLinks]['value']['channelUID']['uid']
					with open('org.openhab.core.thing.Thing.json') as json_file_things:
						dataThings = json.load(json_file_things)		
						# print(dataThings)
						for pThings in dataThings:
							if pThings in uid:
								channel = uid
								
		# Metadata
		metadata = ""
		stateDescription = ""
		expire = ""
		widget = ""
		cellWidget = ""
		autoupdate = ""
		listWidget = ""
		
		with open('org.openhab.core.items.Metadata.json') as json_file_meta:
			dataMeta = json.load(json_file_meta)		
			# print(dataMeta)
			for pMeta in dataMeta:
				if name in pMeta:
					if "stateDescription:" in pMeta:
						stateDescription = "stateDescription=\"\"["
						if 'pattern' in dataMeta[pMeta]['value']['configuration']:
							stateDescription = stateDescription + "pattern=\"" + dataMeta[pMeta]['value']['configuration']['pattern'] + "\", "
						if 'min' in dataMeta[pMeta]['value']['configuration']:
							stateDescription = stateDescription + "min=\"" + dataMeta[pMeta]['value']['configuration']['min'] + "\", "
						if 'max' in dataMeta[pMeta]['value']['configuration']:
							stateDescription = stateDescription + "max=\"" + dataMeta[pMeta]['value']['configuration']['max'] + "\", "
						if 'step' in dataMeta[pMeta]['value']['configuration']:
							stateDescription = stateDescription + "step=\"" + dataMeta[pMeta]['value']['configuration']['step'] + "\", "
						if 'options' in dataMeta[pMeta]['value']['configuration']:							
							stateDescription = stateDescription + "options=\"" + dataMeta[pMeta]['value']['configuration']['options'] + "\", "
						stateDescription = stateDescription.rstrip(", ") + "], "
						# print(stateDescription)
					
					if "expire:" in pMeta:
						expire = "expire=\""
						if 'value' in dataMeta[pMeta]['value']:
							expire = expire + dataMeta[pMeta]['value']['value']					
						expire = expire + "\", "
						# print(expire)
						
					if "widget:" in pMeta:
						widget = "expire=\""
						if 'value' in dataMeta[pMeta]['value']:
							widget = widget + dataMeta[pMeta]['value']['value']
						widget = widget + "\", "
						# print(widget)
						
					if "cellWidget:" in pMeta:
						cellWidget = "cellWidget=\""
						if 'value' in dataMeta[pMeta]['value']:
							cellWidget = cellWidget + dataMeta[pMeta]['value']['value']
						cellWidget = cellWidget + "\", "
						# print(cellWidget)
						
					if "autoupdate:" in pMeta:
						autoupdate = "autoupdate=\""
						if 'value' in dataMeta[pMeta]['value']:
							autoupdate = autoupdate + dataMeta[pMeta]['value']['value']
						autoupdate = autoupdate + "\", "
						# print(autoupdate)
					
					if "listWidget:" in pMeta:
						listWidget = "listWidget=\"" + dataMeta[pMeta]['value']['value'] + "\"["
						if 'title' in dataMeta[pMeta]['value']['configuration']:
							listWidget = listWidget + "title=\"" + dataMeta[pMeta]['value']['configuration']['title'] + "\", "
						if 'subtitle' in dataMeta[pMeta]['value']['configuration']:
							listWidget = listWidget + "subtitle=\"" + dataMeta[pMeta]['value']['configuration']['subtitle'] + "\", "
						if 'icon' in dataMeta[pMeta]['value']['configuration']:
							listWidget = listWidget + "icon=\"" + dataMeta[pMeta]['value']['configuration']['icon'] + "\", "
						if 'min' in dataMeta[pMeta]['value']['configuration']:
							listWidget = listWidget + "min=\"" + dataMeta[pMeta]['value']['configuration']['min'] + "\", "
						if 'max' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "max=\"" + dataMeta[pMeta]['value']['configuration']['max'] + "\", "
						if 'step' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "step=\"" + dataMeta[pMeta]['value']['configuration']['step'] + "\", "
						if 'vertical' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "vertical=\"" + dataMeta[pMeta]['value']['configuration']['vertical'] + "\", "
						if 'label' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "label=\"" + dataMeta[pMeta]['value']['configuration']['label'] + "\", "
						if 'scale' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "scale=\"" + dataMeta[pMeta]['value']['configuration']['scale'] + "\", "
						if 'scaleSteps' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "scaleSteps=\"" + dataMeta[pMeta]['value']['configuration']['scaleSteps'] + "\", "
						if 'scaleSubSteps' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "scaleSubSteps=\"" + dataMeta[pMeta]['value']['configuration']['scaleSubSteps'] + "\", "
						if 'unit' in dataMeta[pMeta]['value']['configuration']:							
							listWidget = listWidget + "unit=\"" + dataMeta[pMeta]['value']['configuration']['unit'] + "\", "						
						listWidget = listWidget.rstrip(", ") + "], "
						# print(listWidget)
					
			metadata = (stateDescription + expire + widget + listWidget + cellWidget + autoupdate).rstrip(", ")
		
		
		lastEnt = ""
		if channel:
			lastEnt = "channel=\"" + channel + "\", "
		if metadata:
			lastEnt = lastEnt + metadata + ", "
		if lastEnt:
			lastEnt = (" { " + lastEnt.rstrip(", ") + " }")
				
		exportStr.append(channel + " " + itemType + name + label + category + groupNames + tags + lastEnt)			


# export all the data to a file and sort it 
f = open("export.items", "w")
for line in sorted(exportStr):
	f.write(line.split(" ", 1)[1] + "\n")
f.close()

			
print("done")

EDIT: exported Items list is now sorted after ItemType and then after channel so same channels stay together it’s therefore easier to separate them into different *.items files.

Andy

9 Likes

Nice script!
I’m just diving into OH since a couple of weeks and I’m also gonna do everything via GUI, but I’m still creating the items text files and afterwards I add them into the GUI via Add Items from Textual Definition. If I change something (icon, model…) I can simply edit my text files and import them again.

Nice work, i had the same toughts abut ui based vs file based.

The main Problem i see is that the textfiles get crowded with all the metadata that is now required. But thats my personal opinion.

yes that is true, you need a wide monitor to display them all on a single line :wink:
It probably all depends on how often you use a text editor and add new sensors. Currently I’m expanding my system a lot and therefore copy & paste is so much easier with textfiles.

It the same when building a new system. typically you add i.e. a bunch of thermostates, switches, sensors.

A templating funktion would be nice in the ui.Maybe it is an Idea that it is possible to select items groups to beexported. Them you have a template of an item. With a texteditor you could duplicate and adapt it and the import it to the ui

There is no need to have all the information for 1 item in one line.

Dimmer          zWaveLightSwitchDimmer1Wohnzimmer
                       "Dimmer [%d]"
                       <slider>
                       (gPersist,gOffSimulationLichtWohnzimmer)
                       {channel="zwave:device:5001237820:node17:switch_dimmer1"}

yes of course, I know, but I like it more compact on one line but his might change when I added all necessary metadata information to the items

Depending on what you are changing, this can be done in seconds through the REST API docs or editing the JSONDB file directly. I’m not denigrating your script which is quite nice, just pointing out that pointing and clicking through MainUI is not the only option for making changes to the config.

Just be aware that if you change the name of an Item in this way it will generate a duplicate Item with the new name. You’ll still have to delete the old one manually.

If these are all linked to Things then I find “Create Equipment from Thing” to be just about as fast as I ever was with text files. But indeed, if you try to create each Item one by one it would be a pain.

There are a couple of other things worth mentioning. The Links and the metadata are stored separately from the Item in the JSONDB. So it is possible to define the Items up to but not including the { } part in a .items file and then set the metadata and links through the UI, if that approach works best for you.

Also if you are defining default widgets, create a custom widget (under developer tools) and then reuse that on your Items. That way the actual widget config is handled on one place and you don’t have to redo it (copy and paste) for each and every similar Item.

Again, I’m not arguing against this script. I’m just trying to point out that there are approaches to creating and managing Items through the UI that make it so you don’t have to click/edit through MainUI.

tl;dr for the techniques:

  • Use the import from text file way to create and edit your Items. You can create your one liner and copy/paste/edit it right there in the form to make a similar change to lots of similar Items.
  • Use the REST API Docs to query for an Item, edit the JSON with your changes, and push it back to update the Item. Repeat for each similar Item.
  • Stop openHAB and modify the JSONDB manually (I suggest this only if you are trying to change the name of an Item). Make sure you change the name everywhere in all the .jsondb files.
  • Make use of the “Create Equipment from Thing” and “Create Points from Thing” to create all the Items linked to one Thing in one go.
2 Likes

wow thx for all the infos
I didn’t thought of the REST API trick to change things, hmm interesting

By the way, as I tried to figure out how to build my script I found some strange tings in the org.openhab.core.thing.link.ItemChannelLink.json file.

I found two Things with the same content but different IDs but only one is visible in the Things page.
How is this possible and how does OpenHab decided which Thing it reads and does use it?

This one is displayed in the Things Tab in the Settings.

"SqueezeEvita_Power -\u003e squeezebox:squeezeboxplayer:myServer:7aa937d0a974:power": {
    "class": "org.openhab.core.thing.link.ItemChannelLink",

This one is apparently not used anymore but why is it still there and is not removed?

"SqueezeEvita_Power -\u003e squeezebox:squeezeboxplayer:myServer:evita:power": {
    "class": "org.openhab.core.thing.link.ItemChannelLink",

How can I “clean” the file so that only one entry is displayed and used? Does OpenHAB has such a “cleaning” feature?

Andy

I couldn’t say. I’ve not heard of such a thing before now. You should be able to tell which one it’s showing at any given time.

Those are not Things. Those are Links. At least one side of the link appears to be orphaned. Does the Item side still exist? Based on what you’ve said the Thing no longer exists.

I recommend removing it using the REST API Docs. There is a delete link rest API call, but you’ll also need the name of the Item which should be part of the full record. You’ve only shown a couple of lines.

here is the full link thing.
The only difference is the name “evita” vs “7aa937d0a974”
One of them is currently active ant its the one with the number (7aa937d0a974)

  "SqueezeEvita_Power -\u003e squeezebox:squeezeboxplayer:myServer:evita:power": {
    "class": "org.openhab.core.thing.link.ItemChannelLink",
    "value": {
      "channelUID": {
        "segments": [
          "squeezebox",
          "squeezeboxplayer",
          "myServer",
          "evita",
          "power"
        ],
        "uid": "squeezebox:squeezeboxplayer:myServer:evita:power"
      },
      "configuration": {
        "properties": {}
      },
      "itemName": "SqueezeEvita_Power"
    }
  }
  "SqueezeEvita_Power -\u003e squeezebox:squeezeboxplayer:myServer:7aa937d0a974:power": {
    "class": "org.openhab.core.thing.link.ItemChannelLink",
    "value": {
      "channelUID": {
        "segments": [
          "squeezebox",
          "squeezeboxplayer",
          "myServer",
          "7aa937d0a974",
          "power"
        ],
        "uid": "squeezebox:squeezeboxplayer:myServer:7aa937d0a974:power"
      },
      "configuration": {
        "properties": {}
      },
      "itemName": "SqueezeEvita_Power"
    }
  }

In the org.openhab.core.thing.Thing.json file there is only one thing:

"squeezebox:squeezeboxplayer:myServer:7aa937d0a974": {

So openhab first looks into the thing file determines the name and searches in the org.openhab.core.thing.link.ItemChannelLink.json file for the corresponding link.

Navigate to the Item in Settings -> Items. Does the orphaned Link appear there? If so click on it and then delete.

If not, open the REST API Docs and go to the Links section and fill out the ID and Item name for the Link you want to delete and delete it from there.

the following may be of interest in this context:

Yes you’re right there was an orphane link thx for the tip

That would be a really nice feature totally support this, best of two worlds!

1 Like