Hi, just sharing the implementation of the devicehandler I wrote in order to parse the webpage my Arduino is feeding with humidity and temperature measurements.
I struggled with the HTTP request despite the multiple posts in this forum so I ended-up adapting a working HTTP request from [OBSOLETE] URI Switch -- Device Handler for controlling items via HTTP calls (thanks to the submitters @surge919 and @tguerena).
Then I faced difficulties to understand how to parse the webpage the GET received. I initially used html parsing example but this just returned the entire website content as a single string which wasn’t easy to parse.
I then moved to xmlparser and then extracted the content which I could browse and extract easily my 2 numbers.
Here is my website HTML:
<html>
<head>
<title>Temperature and Humidity Sensors</title>
</head>
<body>
<h1>Temperature and Humidity readings</h1>
<table border="0" cellspacing="0" cellpadding="4">
<tr>
<td>ID</td>
<td>Timestamp</td>
<td>Temperature</td>
<td>Humidty</td>
</tr>
<tr><td>29066</td><td>2016-12-09 14:55:05</td><td>21.60</td><td>48.10</td></tr> </table>
</body>
</html>
```
This was parsed as:
html[ attributes={};
value=[
head[
attributes={};
value=[
title[
attributes={};
value=[
Temperature and Humidity Sensors
]]]],
body[
attributes={};
value=[
h1[
attributes={};
value=[
Temperature and Humidity readings
]],
table[
attributes={border=0, cellspacing=0, cellpadding=4};
value=[
tr[
attributes={};
value=[
td[
attributes={};
value=[
ID
]],
td[
attributes={};
value=[
Timestamp
]],
td[
attributes={};
value=[
Temperature
]],
td[
attributes={};
value=[
Humidty
]]]],
tr[
attributes={};
value=[
td[
attributes={};
value=[29075]
],
td[
attributes={};
value=[
2016-12-09 15:40:23
]],
td[
attributes={};
value=[21.50]
],
td[
attributes={};
value=[47.70]
]]]
]]]]]]
I could then refer to my temperature and humidity values by the below hierarchy and convert into a float with the needed precision:
log.debug "Temperature: ${html.body.table.tr[1].td[2].text()}" log.debug "Humidity: ${html.body.table.tr[1].td[3].text()}" def temp_float = html.body.table.tr[1].td[2].text() def humid_float = html.body.table.tr[1].td[3].text() sendEvent(name: "temperature", value: temp_float.toString().format( java.util.Locale.US,"%.1f", temp_float.toFloat())) sendEvent(name: "humidity", value: humid_float.toString().format(java.util.Locale.US,"%.1f", humid_float.toFloat())) }
The complete code of my devicehandler is below:
/**
* Temperature_Humidity_sensor
*
* Copyright 2016
*
* Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
preferences {
section("Internal Access"){
input "internal_ip", "text", title: "Internal IP", required: false
input "internal_port", "text", title: "Internal Port (if not 80)", required: false
input "internal_query_path", "text", title: "Internal query Path (/yourphppage.php)", required: false
}
}
metadata {
definition (name: "Humidity_Temperature", namespace: "xxxxx", author: "xxxxxxxxxx", category: "C2")
{
capability "refresh"
capability "polling"
capability "capability.temperatureMeasurement"
capability "capability.relativeHumidityMeasurement"
}
tiles(scale: 2) {
multiAttributeTile(name:"temperature", type:"generic", width:6, height:4) {
// value tile (read only)
tileAttribute("device.humidity", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}%', unit:"%", backgroundColors:[
[value: 40, color: "#153591"],
[value: 50, color: "#1e9cbb"],
[value: 60, color: "#90d2a7"],
[value: 70, color: "#44b621"],
[value: 80, color: "#f1d801"],
[value: 90, color: "#d04e00"],
[value: 95, color: "#bc2323"]])
}
tileAttribute("device.temperature", key: "SECONDARY_CONTROL") {
attributeState("default", label:'${currentValue}Âş', unit:"dC")
}
}
standardTile("tempdetail", "device.temperature", width: 2, height: 2, canChangeIcon: false) {
state "default", label: '${currentValue}Âş', unit:"dC",
icon: "st.Weather.weather2", backgroundColors:[
[value: 10, color: "#153591"],
[value: 15, color: "#1e9cbb"],
[value: 18, color: "#90d2a7"],
[value: 20, color: "#44b621"],
[value: 22, color: "#f1d801"],
[value: 25, color: "#d04e00"],
[value: 28, color: "#bc2323"]]
}
standardTile("listView", "device.humidity", width: 2, height: 2, canChangeIcon: false) {
state "default", label: '${currentValue}%',
icon: "st.Weather.weather12", backgroundColors:[
[value: 40, color: "#153591"],
[value: 50, color: "#1e9cbb"],
[value: 60, color: "#90d2a7"],
[value: 70, color: "#44b621"],
[value: 80, color: "#f1d801"],
[value: 90, color: "#d04e00"],
[value: 95, color: "#bc2323"]]
}
standardTile("refresh", "device.thermostatMode", decoration: "ring", width: 2, height: 2) {
state "default", action:"refresh", icon:"st.secondary.refresh"
}
main("listView")
}
}
def installed() {
log.debug "Executing 'installed'"
}
def updated() {
log.debug "Executing 'updated'"
refresh()
}
def poll(){
log.debug "Executing 'poll'"
refresh()
}
def parse(description) {
log.debug "Executing 'parse'"
def msg = parseLanMessage(description)
log.debug msg.body
if (msg.status == 200)
{
def xmlParser = new XmlParser()
def html = xmlParser.parseText(msg.body)
assert html instanceof groovy.util.Node
log.debug "Temperature: ${html.body.table.tr[1].td[2].text()}"
log.debug "Humidity: ${html.body.table.tr[1].td[3].text()}"
def temp_float = html.body.table.tr[1].td[2].text()
def humid_float = html.body.table.tr[1].td[3].text()
sendEvent(name: "temperature", value: temp_float.toString().format( java.util.Locale.US,"%.1f", temp_float.toFloat()))
sendEvent(name: "humidity", value: humid_float.toString().format(java.util.Locale.US,"%.1f", humid_float.toFloat()))
}
}
private getCallBackAddress() {
log.debug "Hub address"
log.debug device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
return device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
// gets the address of the device
private getHostAddress() {
def ip = getDataValue("ip")
def port = getDataValue("port")
if (!ip || !port) {
def parts = device.deviceNetworkId.split(":")
if (parts.length == 2) {
ip = parts[0]
port = parts[1]
} else {
log.warn "Can't figure out ip and port for device: ${device.id}"
}
}
log.debug "Using IP: $ip and port: $port for device: ${device.id}"
return convertHexToIP(ip) + ":" + convertHexToInt(port)
}
private Integer convertHexToInt(hex) {
log.debug hex
return Integer.parseInt(hex,16)
}
private String convertHexToIP(hex) {
log.debug hex
return [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
private String convertIPtoHex(ipAddress) {
String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
log.debug "IP address entered is $ipAddress and the converted hex code is $hex"
return hex
}
private String convertPortToHex(port) {
String hexport = port.toString().format( '%04x', port.toInteger() )
log.debug hexport
return hexport
}
def refresh() {
log.debug "Executing refresh"
def host = internal_ip
def port = internal_port
def hosthex = convertIPtoHex(host)
def porthex = convertPortToHex(port)
device.deviceNetworkId = "$hosthex:$porthex"
log.debug "The device id configured is: $device.deviceNetworkId"
def path = internal_query_path
log.debug "path is: $path"
def headers = [:]
headers.put("HOST", "$host:$port")
try {
def hubAction = new physicalgraph.device.HubAction(
method: "GET",
path: path,
headers: headers
)
// log.debug hubAction
return hubAction
}
catch (Exception e) {
log.debug "Hit Exception $e on $hubAction"
}
}
<img src="//cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/smartthings/original/3X/8/4/849dafb4aa8eaab2cc70c43103cb252f093c7724.jpg" width="312" height="500">
<img src="//cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/smartthings/original/3X/5/3/5384ff04cfa47da1269f85cf4dea10f3511fab85.jpg" width="312" height="500">
<img src="//cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/smartthings/original/3X/1/6/16c1cbb3108219e08109538827602b5b4dc289a9.jpg" width="312" height="500">
<img src="//cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/smartthings/original/3X/4/d/4d5f72db4bfae5b8eeab0fd3ce14c5cfeb08d198.jpg" width="312" height="500">
Hope it helps!