Json issue; ST v. HE

Attempting to bring across my Alarm. Problems are solved, new one arise!

I have a wemos sending a message in this format.

POST / HTTP/1.1
Host: [192.168.1.217:39501]
(http://192.168.1.217:39501/) 
Content-Type: application/json;charset=utf-8 
Server: Alarm System Connection: close 
{"stat_str": "Ready", 
"stat_update_from": "System" 
}

This message format was working well in ST, but not in HE. Any thoughts as to why? And how to go about fixing?

This is a sample error in the logs:

2018-10-04 09:46:30.477 debug mac:4E6574777278, ip:c0a80105, port:e5cc, headers:UE9TVCAvIEhUVFAvMS4xDQpDb25uZWN0aW9uOiBjbG9zZQ0KSG9zdDogMTkyLjE2OC4xLjIxNzozOTUwMQ0KU2VydmVyOiBBbGFybSBTeXN0ZW0NCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbjtjaGFyc2V0PVVURi04DQo=, body:

2018-10-04 09:46:30.474 debug Not valid json response/message
1 Like

Upon further research, I think it might be this change that is required porting from ST to HE.

Add to params calls that give a java error the following before headers:
requestContentType: "application/json",

If I'm on the right track, where would it go?

def parseDescriptionAsMap(description)
{
	description.split(",").inject([:])
	{
		map, param ->
		def nameAndValue = param.split(":")
		if (nameAndValue.size() > 1)
			map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
}

private getHeader(userpass = null)
{
	def headers = [:]
	headers.put("Host", getHostAddress())
	headers.put("Content-Type", "application/x-www-form-urlencoded")
	if (userpass != null)
	   headers.put("Authorization", userpass)
	return headers
}

Is anyone able to lend a hand?

I know the baseball post season has just started (Go Brewers), but this Aussie would be grateful for some help.

Have you looked through this thread? Iā€™m not sure if the answer to your question is buried in there or not.

Yes, I've had a look through that thread, and tried a few things, but I'm not sure what is going on. Not a coder!

Tearing my hair out - what's left anyway.

Edit: I'm not even sure it's the right area in the Device Code.

@chuck.schwer are you able to help please?

This is the error message in logs

> 2018-11-04 22:13:50.093:debug mac:4E6574777278, ip:c0a80105, port:ce73, headers:UE9TVCAvIEhUVFAvMS4xDQpDb25uZWN0aW9uOiBjbG9zZQ0KSG9zdDogMTkyLjE2OC4xLjIxNzozOTUwMQ0KU2VydmVyOiBBbGFybSBTeXN0ZW0NCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbjtjaGFyc2V0PVVURi04DQo=, body:

This is part of the driver. The relevant part (I suspect) is after the snip.

def parse(description)
{
	def map = [:]
	def events = []
	def cmds = []
	
	if(description == "updated") return
	def descMap = parseDescriptionAsMap(description)
	
	if (descMap == null)
	{
		log.debug "Not valid json response/message"
		log.debug description
		return
	}
	
	def body = new String(descMap["body"].decodeBase64())

	def slurper = new JsonSlurper()
	def result;
	try
	{
		result = slurper.parseText(body)
	}
	catch (e)
	{
		log.debug "Invalid response from system: " + body
		return
	}
	
	// Received an alarm stat string so update tile status but dont display it since the event string below will be logged
	if (result.containsKey("stat_str"))
	{
 		handleAlarmStatus(result.stat_str)
	}
	
	// If we receive a key containing 'stat_update_from' then it is an alarm status so add it to the event log and update tile
	if (result.containsKey("stat_update_from"))
	{
		def dateTime = new Date()
		def sensorStateChangedDate = dateTime.format("yyyy-MM-dd HH:mm", location.timeZone)
		def status_string = result.stat_str + " by " + result.stat_update_from + " at " + sensorStateChangedDate
		// Send the status string that we have built
		sendEvent(name: "events", value: "${status_string}", displayed: true, isStateChange: true)
	}
	
	if (result.containsKey("zone_status"))
	{
		handleZoneStatus(result.zone_id, result.zone_status)
	}
	
	// This code will pull zone information out of the /getzonenamespage (and allows for zones not in order)
	if (result.containsKey("maxzoneid"))
	{
		log.debug "Handling getzonenames page"
		handleCreateZones(result.zones)
	}
}

---
  snip
---
private handleZoneStatus(zoneId, zoneStatus)
{
	def thisZoneDeviceId = "alarmchildzone"+zoneId
	def curdevice = null
	try
	{
		// Got a zone status so first try to find the correct child device
		curdevice = getChildDevices()?.find { it.deviceNetworkId == thisZoneDeviceId }
	}
	catch (e)
	{
		log.debug "Failed to find child zone for zone " + zoneId + "exception ${e}"
	}

	if (curdevice == null)
	{
		log.debug "Failed to find child device for zone: " + zoneId + " expecting " + thisZoneDeviceId
		/*
		def children = getChildDevices()

		log.debug "device has ${children.size()} children"
		children.each { child ->
		log.debug "child ${child.displayName} has deviceNetworkId ${child.deviceNetworkId}"
		}
		*/
	}
	else
	{
		// Check the device type for this child, since the different child device types need different event types
		boolean isMotionDevice = (curdevice.capabilities.find { it.name == "Motion Sensor" } != null)
		boolean isSmokeDevice = (curdevice.capabilities.find { it.name == "Smoke Detector" } != null)

		// Handle the specific zone status result
		switch (zoneStatus)
		{				  
			case "Active":
				log.debug "Got Active zone: " + zoneId + ", which is called - " + curdevice
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "active", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "smoke", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "open", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break					

			case "Inactive":
				log.debug "Got Inactive zone: " + zoneId + ", which is called - " + curdevice
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "inactive", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "inactive")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "clear", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "clear")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "closed", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "closed")
				}
				break 			

			case "Bypassed - Active":
				log.debug "Got Active Bypassed zone: " + zoneId + ", which is called - " + curdevice
				sendEvent(name: "panelzone"+zoneId, value: "bypass", displayed: false, isStateChange: true)
				if (isMotionDevice)
				{
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break

			case "Bypassed - Inactive":
				log.debug "Got Inactive Bypassed zone: " + zoneId + ", which is called - " + curdevice
				sendEvent(name: "panelzone"+zoneId, value: "bypass", displayed: false, isStateChange: true)
				if (isMotionDevice)
				{
					curdevice?.sendEvent(name: "motion", value: "inactive")
				}
				else if (isSmokeDevice)
				{
					curdevice?.sendEvent(name: "smoke", value: "clear")					
				}
				else
				{
					curdevice?.sendEvent(name: "contact", value: "closed")
				}
				break

			case "Tamper":
				log.debug "Got Tamper for zone: " + zoneId + ", which is called - " + curdevice
				// We'll set it to open for now, since at least that gives an indication something is wrong!
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "active", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "smoke", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "open", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break

			default:
				log.debug "Unknown status received: ${zoneId} is ${zoneStatus}"
				break
		}
	}
}

Here is a copy of the Driver Code in full;
https://1drv.ms/t/s!AnXEg8RBC3NHlepHE7JsoHCflr7PIg

Anyone?

since you are not using httpPost, that thread does not apply to you. I don't know what a "wemos" is.

Is the message in your first post accurate? because that is not a valid message,

there should be a blank line between the headers and the body.
i don't believe line 3 is a valid line either for a header.
and on line 5 it appears there are 2 headers on one line.

It either sends a ā€œSystemā€ JSON Update
with 2 attributes in the body (the ā€˜stat_strā€™ overall system status, and a ā€˜fromā€™ field)
which is used to update the status of the alarm system as a whole (armed/away/home/arming etc)

or

a ā€œzoneā€ JSON update
which has ā€œzone_idā€, ā€œzone_nameā€, and ā€œzone_statusā€ fields

Is there any way to get the full contents of the message received by Hubitat?
It seems thereā€™s something in the format of the message which Hubitat doesnā€™t like, but other systems are ok with.

The exact string sent to the Hubitat server would be in the form: -

POST / HTTP/1.1
Host: 192.168.1.217:39501
Content-Type: application/json;charset=utf-8
Server: Alarm System
Connection: close

{"stat_str": "Ready",
"stat_update_from": "System"
}

@chuck.schwer

do you control the building of that message? It is missing a Content-Length header.

I can contact the developer. What specifically needs adding?

What I mentioned above.. you need to add a Content-Length header.