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.