Input field for number/free text for openHAB UIs

Hi all,
I would need (as I think many of you) a field to enter a value or free text in the UIs of openHAB, so I tried to use a webview to display a small html page containing a text field and some javascript code to manage everything.

This is the html files textInput.html (in OPENHAB_DIR\webapps\static)

<html>
<script language="JavaScript">
    function getParam(param)
    {
        var qs = (function(a) {
            if (a == "") return {};
            var b = {};
            for (var i = 0; i < a.length; ++i)
            {
                var p=a[i].split('=', 2);
                if (p.length == 1)
                    b[p[0]] = "";
                else
                    b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
            }
            return b;
        })(window.location.search.substr(1).split('&'));       
        return qs[param]
    }
    function checkInput(id)
    {
        var x=document.getElementById(id).value;
        if (isNaN(x)) 
        {   document.getElementById(id).value = ""
            return false;
        }
        else
        {   return true;
        }
    }
    function resetInput(id)
    {
        document.getElementById(id).value = ""
    }
    function httpGetAsync(theUrl)
    {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", theUrl, true); // true for asynchronous 
        xmlHttp.send(null);
    }
    function sendValue() {
        if checkInput('textInput') {
            url = "http://localhost:8080/CMD?" + getParam('item') + "=" + document.getElementById('textInput').value
            httpGetAsync(url)
            resetInput('textInput')
        } else {
            resetInput('textInput')
        }
    }
</script>
<form>
    <input name="textInput" id="textInput" type="text" onChange="sendValue()">
</form>
</html>

This is the part of sitemap

Webview url="http://localhost:8080/static/textInput.html?item=ScheduledEvent_hh" height=3

Leaving aside for now the rendering aspects (which can probably be improved), my tests about this technique are the following:

  • It works on Classic UI when visited by a browser on PC

  • It does not work on Classic UI when visited by iPhone (chrome and safari): The input text is not displayed

  • It does not work on Classic UI when visited by Android (chrome): the webview shows connection refused

  • It does not work on HABdroid: clicking on the field the keyboard appears but disappears immediately not allowing text input

  • It works partially on iOS UI app (so it does not work!): all the javascript functions work except function that use XMLHttpRequest () for HTTP GET request

Someone else has tried this technique for entering text?
Thoughts? Suggestions?

(I’m using openHAB 1.x)

Thanks!

General question: is there a reason why text input is not possible in general? It is in fact possible for voice input (e.g. Habdroid on Android), it sets the static string item VoiceCommand. It should be possible to add text input e.g. as a popup prompt in Habdroid?

The only reason is, there isn’t a widget yet, and I don’t know why (If anyone has the skills…)

I would like a free Text Input very much.
Of course this should be realized in a proper way, one should be able to set allowed chars, length and so on… Maybe that’s the reason…

2 Likes

I’ve adapted this implementation for OH 2, and solved compatibility with Classic UI on Android:

<html>
<script language="JavaScript">
    var openhabIP = "192.168.1.99"
    function resetInput(id)
    {
        document.getElementById(id).value = ""
    }
    function httpGetAsync(theUrl)
    {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", theUrl, true); // true for asynchronous 
        xmlHttp.send(null);
    }
    function sendValue() {
            url = "http://" + openhabIP + ":8080/classicui/CMD?Item=" + document.getElementById('textInput').value
            httpGetAsync(url)
            resetInput('textInput')
    }
</script>
<form onSubmit="return false">
    <input name="textInput" id="textInput" type="text" onChange="sendValue()">
</form>
</html>

This does remove the dynamic input capability, which I think was causing problems. In my case, I only need the one input box so I’m not bothered.

To adapt this back to OH 1 you would simply remove classicui/ from "8080/classicui/CMD?Item=", and it should work.

Chris great idea and wonderful implementation. I’ve taken what you created and made some modifications to allow for multiple ITEMS to be updated. on a single page.

First , I want to let everyone know some of the issues I experienced with this configuration.

  • HTTPS Issues: Since I’m running an openhabianpi rig with NGINX reverse proxy with authentication I needed to change the ip address variable to my domain name. Once authenticated on the browser, I was able to update the items without any problems. I also tested this on Habdroid and can report that everything is a-ok.
  • ITEM Name: Man alive! this was a big problem for me. It took me a long time to figure what you meant by dynamic input “I may still not truly understand”, but for anyone wanting to make use of this, please remember to change the word “Item” in the url string. This needs to be the actual name of the item you wish to update.
  • MULTIPLES of MULTIPLES: I was able to create a separate HTML file for each grouping of items that I wanted to updated. For example, I have a unique html for for each user consisting of 4 items in each. Since I have 10 users I created 10 custom html files. I was then able to link to each individual html file via the sitemap and the Webview function.

This is not a complex configuration just a repetitive one. But I with this I was able to start the process of making the administration of the system more user friendly to non tech people. Coupling this configuration with some smart rules will allow end-uses to manage what kind of notifications they receive via email.

Sample html file

<html>
<script language="JavaScript">
    var openhabIP = "REPLACE_THIS_VALUE_WITH_IP_OR_DOMAIN_NAME"
    function resetInput(id)
    {
        document.getElementById(id).value = ""
    }
    function httpGetAsync(theUrl)
    {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", theUrl, true); // true for asynchronous 
        xmlHttp.send(null);
    }
		function sendUNValue() {
            url = "https://" + openhabIP + "/classicui/CMD?Per_1_UName=" + document.getElementById('Per_1_UName').value
            httpGetAsync(url)
 //           resetInput('textInput')
    }
	    function sendFNValue() {
            url = "https://" + openhabIP + "/classicui/CMD?Per_1_FName=" + document.getElementById('Per_1_FNam').value
            httpGetAsync(url)
 //           resetInput('textInput')
    }
	    function sendLNValue() {
            url = "https://" + openhabIP + "/classicui/CMD?Per_1_LName=" + document.getElementById('Per_1_LName').value
            httpGetAsync(url)
 //           resetInput('textInput')
    }
	    function sendEMValue() {
            url = "https://" + openhabIP + "/classicui/CMD?Per_1_Email=" + document.getElementById('Per_1_Email').value
            httpGetAsync(url)
//            resetInput('textInput')
    }
</script>
<body onSubmit="return false">
    
	<h2>User Name</h2>
	<input name="textInput" id="Per_1_UName" type="text" onChange="sendUNValue()">
	<h2>First Name</h2>
	<input name="textInput" id="Per_1_FNam" type="text" onChange="sendFNValue()">
	<h2>Last Name</h2>
	<input name="textInput" id="Per_1_LName" type="text" onChange="sendLNValue()">
	<h2>Email Address</h2>
	<input name="textInput" id="Per_1_Email" type="text" onChange="sendEMValue()">
</body>
</html>

Please Note that you’ll need to replace the following fields:
*REPLACE_THIS_VALUE_WITH_IP_OR_DOMAIN_NAME: Self explanatory
*Per_1_UName: Name of the 1st Item you wan to post.
*Per_1_FNam: Name of the 2nd Item you wan to post.
*Per_1_LName: Name of the 3rd Item you wan to post.
*Per_1_Email: Name of the 4th Item you wan to post.
*The names of the scripts if you wish.

Sample Site Map

	Frame label="User Management"	{
		Text item=S_Users icon="parents_3_1"	{
			Frame 	{
				Text item=Per_1_UName icon="parents_3_1"	{
					Text 		item=Per_1_UName
					Text 		item=Per_1_FName
					Text 		item=Per_1_LName
					Text 		item=Per_1_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User1.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_2_UName icon="parents_3_1"	{
					Text 		item=Per_2_UName
					Text 		item=Per_2_FName
					Text 		item=Per_2_LName
					Text 		item=Per_2_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User2.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_3_UName icon="parents_3_1"	{
					Text 		item=Per_3_UName
					Text 		item=Per_3_FName
					Text 		item=Per_3_LName
					Text 		item=Per_3_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User3.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_4_UName icon="parents_3_1"	{
					Text 		item=Per_4_UName
					Text 		item=Per_4_FName
					Text 		item=Per_4_LName
					Text 		item=Per_4_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User4.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_5_UName icon="parents_3_1"	{
					Text 		item=Per_5_UName
					Text 		item=Per_5_FName
					Text 		item=Per_5_LName
					Text 		item=Per_5_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User5.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_6_UName icon="parents_3_1"	{
					Text 		item=Per_6_UName
					Text 		item=Per_6_FName
					Text 		item=Per_6_LName
					Text 		item=Per_6_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User6.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_7_UName icon="parents_3_1"	{
					Text 		item=Per_7_UName
					Text 		item=Per_7_FName
					Text 		item=Per_7_LName
					Text 		item=Per_7_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User7.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_8_UName icon="parents_3_1"	{
					Text 		item=Per_8_UName
					Text 		item=Per_8_FName
					Text 		item=Per_8_LName
					Text 		item=Per_8_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User8.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_9_UName icon="parents_3_1"	{
					Text 		item=Per_9_UName
					Text 		item=Per_9_FName
					Text 		item=Per_9_LName
					Text 		item=Per_9_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User9.html" height=20 label="User Information"
															}
													
														}
											}
		Frame 	{
				Text item=Per_10_UName icon="parents_3_1"	{
					Text 		item=Per_10_UName
					Text 		item=Per_10_FName
					Text 		item=Per_10_LName
					Text 		item=Per_10_Email
					Text		label="Change Values"		{
							Webview url="https://YOUR-DOMAIN-OR-IP/static/User10.html" height=20 label="User Information"
															}
													
														}
											}
												}
										}	

Obviously, you’ll need to create the items for all of these, but i’ll spare you the details.

Again thanks Chris for this great workaround.

I had another go at this, building from previous posts, to try to make this work in BasicUI and have a look and feel that fits in.

There is still a lot of room for improvement, but I am no Javascript and HTML developer at all, so this is how far I get. I use the REST API, and not the methods through ClassicUI. The limitation is that the values only update when the screen is refreshed. I don’t know how to do all the plumbing or leverage BasicUI functions to make automatic updates work. For my purpose, this is just fine.
The html file below is called from a webview element in the sitemap with 2 parameters, item (for the item to show and update) and label (a label to put in front of the input box). That looks like this:

Frame label="Meterstanden groene stroom" {
	Webview url="http://192.168.0.10:8080/static/textinput.html?item=PVEmeter1&label=GS1"
	Webview url="http://192.168.0.10:8080/static/textinput.html?item=PVEmeter2&label=GS2"
}

My sitemap then looks like this:

And here is the html file:

<html class="ui-icons-enabled">
<script>
    var openHabianPI = "192.168.0.10"
    function getParam(param)
    {
        var qs = (function(a) {
            if (a == "") return {};
            var b = {};
            for (var i = 0; i < a.length; ++i) {
                var p=a[i].split('=', 2);
                if (p.length == 1)
                    b[p[0]] = "";
                else
                    b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
            }
            return b;
        })(window.location.search.substr(1).split('&'));       
        return qs[param]
    }
    function httpGet(theUrl)
    {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.onreadystatechange = function() {
    		if (this.readyState == 4 && this.status == 200) {
		    	document.getElementById('textInput').value = xmlHttp.responseText;
		    }
		};
        xmlHttp.open("GET", theUrl, true);
        xmlHttp.setRequestHeader("Accept", "text/plain");
        xmlHttp.send();
        return 
    }
    function httpPut(theUrl, theValue)
    {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("PUT", theUrl, true);
        xmlHttp.setRequestHeader("Content-type", "text/plain");
        xmlHttp.setRequestHeader("Accept", "application/json");
        xmlHttp.send(theValue)
    }
	function sendValue()
	{
        url = "http://" + openHabianPI + ":8080/rest/items/" + getParam('item') + "/state";
        httpPut(url, document.getElementById('textInput').value)
    }
	function getValue()
	{
        url = "http://" + openHabianPI + ":8080/rest/items/" + getParam('item') + "/state";
        httpGet(url)
    }
</script>
<head>

	<link rel="stylesheet" type="text/css" href="../basicui/mdl/material.min.css" />
	<link rel="stylesheet" type="text/css" href="../basicui/material-icons.css" />
	<link rel="stylesheet" type="text/css" href="../basicui/roboto.css" />
	<link rel="stylesheet" type="text/css" href="../basicui/smarthome.css" />
	<script src="../basicui/mdl/material.min.js"></script>
	
	<style>
		form {
			margin-bottom: 0;
		}
		.mdl-form__row {
			border-bottom-style: none;
			height: auto;
			padding-top: 0;
			padding-right: 0;
			padding-bottom: 0;
			padding-left: 0;
		}
	</style>
</head>
<body class="mdl-color-text--grey-700" data-icon-type="svg">
	<form action="JavaScript:sendValue()">
		<div class="mdl-form__row mdl-cell mdl-cell--6-col mdl-cell--8-col-tablet">
			<span class="mdl-form__icon">
				<img data-icon="text" src="../icon/text?format=svg" />
			</span>
			<div class="mdl-form__label" id=label>
			</div>
			<div>
				<input type="text" name="textInput" id="textInput">
			</div>
			<div>
				<input type="submit" value="Submit">
			</div>
		</div>
		<script>
			document.getElementById('label').innerHTML = getParam('label');
			document.getElementById('textInput').value = getValue()
		</script>
	</form>
</body>
</html>

I would definitely like to see something like this being picked up and included in the standard functionality of the UI’s. Unfortunately, I don’t know enough about it to be able to do it myself.

3 Likes

Hi Mark, thanks for this. I just included it in my sitemap as well for registration of water and heating meter values.

works like a charm!

i noticed when replacing the ip address with the domain name (that is accessible locally, in this case assume its “raspberrypi”), the item will be UNDEF and it won’t update on submit.

any ideas?

note: pinging the host name in windows resolved the ip address

this works well, but is there a way to ensure that only a number can be entered in the field or a default value if letters are entered?

How do you make the name accessible?
does name resolving work from openhabian in general? i.e.

pi@raspberrypi:~$ ping -c4 raspberrypi

will potentially answer with localhost (127.0.1.1) and maybe this is the problem…

THX Mark Herwege
i use it to Input Channels and Commands for my TV :slight_smile:

Is this still working for everyone?

Mostly works for me, but it doesn’t actually execute commands. Like it’s doing a postUpdate instead of a sendCommand (in openHAB rule jargon). The value changes, but the events don’t fire off. I’m guessing it’s because the API changed in 2.3 or prior. I don’t see documentation for the way this uses the API. I’ll try to make some changes.

Edit:
I changed post to put and then removed this from the url under sendValue, and it seems to work fine now.

+ "/state"

Seems that it was programmed to just update the state. Maybe the API didn’t change ¯_(ツ)_/¯

Hi guys

do someone find a way to make it accesible from the wan not only from the local network?

G

Hello!

I haven’t tried it in this case, but I’m using Tasker to update some items via RESTful API, and it is working outside the local network in format:

https://username:password@myopenhab.org/rest/items/

So, I guess it should work in this case too. It doesn’t even matter username has @ in the e-mail address.

Best regards,
Davor

I’ve just tried it, and it works, but you have to change address to this one in webview too (https://username:password@myopenhab.org/static…). There are two downsides though - first, it is really slow while loading webviews, and second, this will not work if you access Basic UI (not sure about HABdroid, since I’m not at home - accessing local network via VPN right now) within LAN (it acts the same as accessing it from WAN when you have local addresses in .html and webview).

Best regards,
Davor

1 Like

I am trying this way

The submit change the value but it do not triggered the postcommand

Any ways to trigger it?

This widget code by Jürgen Baginski works perfectly in HabPanel for me.

Just change “VoiceCommand” for whichever item you want to push the text into.

  <div class="form-group">

    <input type="text" class="form-control" no-snap-drag="true"

           ng-model="myvalue" ng-value="itemValue('VoiceCommand')"></input>

    <button type="button" class="btn btn-primary"

           ng-click="sendCmd('VoiceCommand', myvalue)">Send </button>

  </div>

Could you please tell me how you made it work in habpanel and which code you use?
I need a simple textbox to write a todo list in Habpanel

Did anyone get the webview solution working on the iOS version of the Openhab app? it does work on the Android one (at least on the Beta one)