Hubitat Safety Monitor App Migration

The below app was originally developed for ST but with the awesome help from @chuck.schwer and @jp0550 I was able to get most of it working on HE. Really the last part of the migration is the integration with HSM. Now currently I can select the contact sensors for use with HSM. I am not sure about the smoke detectors though, HSM does see one of the devices as a smoke alarm, but I am not sure if this will work. I know HSM works differently then ST's SHM, I am just not sure where to begin in changing the old SHM code to work with HSM.

/**
 *  AlarmDecoder Service Manager
 *
 *  Copyright 2016-2018 Nu Tech Software Solutions, Inc.
 *
 *  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.
 *
 *
 * Version 1.0.0 - Scott Petersen - Initial design and release
 * Version 2.0.0 - Sean Mathews <coder@f34r.com> - Changed to use UPNP Push API in AD2 web app
 * Version 2.0.1 - Sean Mathews <coder@f34r.com> - Adding CID device management support.
 */

/*
 * global support
 */
import groovy.transform.Field

/*
 * Turn on verbose debugging
 */
@Field debug = false

/*
 * Device label name settings
 */
@Field lname = "AlarmDecoder"
@Field sname = "Alarm"
 
definition(
    name: "AlarmDecoder service",
    namespace: "alarmdecoder",
    author: "Nu Tech Software Solutions, Inc.",
    description: "AlarmDecoder (Service Manager)",
    category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    singleInstance: true) { }

preferences {
    page(name: "pageMain", title: titles("page_main"), content: "page_main", install: false, uninstall: false, refreshInterval: 5)
    page(name: "pageDiscoverDevices", title: titles("page_discover_devices"), content: "page_discover_devices", install: true, uninstall: false)
    page(name: "pageRemoveAll", title: titles("page_remove_all"), content: "page_remove_all", install: false, uninstall: false)
    page(name: "pageCidManagement", title: titles("page_cid_management"), content: "page_cid_management", install: false, uninstall: false)
    page(name: "pageAddNewCid", title: titles("page_add_new_cid"), content: "page_add_new_cid", install: false, uninstall: false)
    page(name: "pageAddNewCidConfirm", title: titles("page_add_new_cid_confirm", buildcidlabel()), content: "page_add_new_cid_confirm", install: false, uninstall: false)
    page(name: "pageRemoveSelectedCid", title: titles("page_remove_selected_cid"), content: "page_remove_selected_cid", install: false, uninstall: false)
}

/*
 * Localization strings
 */
 
// string table for titles
def titles(String name, Object... args) {
  def page_titles = [
    "page_main": "${lname} setup and management",
    "page_discover_devices": "Install ${lname} service",
    "page_remove_all": "Remove all ${lname} devices",
    "confirm_remove_all": "Confirm remove all",
    "page_cid_management": "Contact ID device management",
    "page_add_new_cid": "Add new CID virtual switch",
    "page_add_new_cid_confirm": "Add new CID switch : %s",
    "page_remove_selected_cid": "Remove selected virtual switches",
    "href_refresh_devices": "Send UPNP discovery",
    "section_cid_value": "Build CID Value(USER/ZONE) : %s",
    "section_cid_partition": "Select the CID partition",
    "section_cid_names": "Device Name and Label",
    "section_build_cid": "Build CID Number :",
    "input_cid_name": "Enter the new device name or blank for auto",
    "input_cid_label": "Enter the new device label or blank for auto",    
    "info_page_remove_selected_cid": "Attempted to remove selected devices. This may fail if the device is in use. If it fails review the log and manually remove the usage. Press back to continue.",
    "info_add_new_cid_confirm": "Attempted to add new CID device. This may fail if the device is in use. If it fails review the log. Press back to continue.",
    "info_remove_all_a": "Removed all child devices.",
    "info_remove_all_b": "This will attempt to remove all child devices. This may fail if the device is in use. If it fails review the log and manually remove the usage. Press back to continue.",
    "input_cid_devices": "Remove installed CID virutal switches",
    "input_cid_number": "Select the CID number for this device",
    "input_cid_value": "Zero PAD 3 digit User,Zone or simple regex pattern ex. '001' or '...'",
    "input_cid_partition": "Enter the partition or 0 for system",
    "input_cid_number_raw": "Enter CID # or simple regex pattern",
    "input_selected_devices": "Select device(s) (%s found)",
    "defaultSensorToClosed": "Default zone sensors to closed?",
    "shmIntegration": "Integrate with Smart Home Monitor?",
    "shmChangeSHMStatus": "Automatically change Smart Home Monitor status when armed or disarmed?",
  ]
  if (args)
      return String.format(page_titles[name], args)
  else
      return page_titles[name]
}

// string table for descriptions
def descriptions(name, Object... args) {
  def element_descriptions = [
    "href_discover_devices": "Tap to discover and install your ${lname} Appliance",
    "href_remove_all": "Tap to remove all ${lname} virtual devices",
    "href_cid_management": "Tap to manage CID virtual switches",
    "input_cid_devices": "Tap to select",
    "input_cid_number": "Tap to select",
    "href_refresh_devices": "Tap to select",
    "href_remove_selected_cid": "Tap to remove selected virtual switches",
    "href_add_new_cid": "Tap to add new CID switch",
    "href_add_new_cid_confirm": "Tap to confirm and add"
  ]
  if (args)
      return String.format(element_descriptions[name],args)
  else
      return element_descriptions[name]
}

/**
 * Allow remote device to force the HUB to request an
 * update from the AlarmDecoder.
 *
 * Just a few steps :( but it works.
 * AD2 -> ST-CLOUD -> ST-HUB -> AD2 -> ST-HUB -> ST-CLOUD
 */
mappings {
    path("/update") {
        action: [
            GET: "webserviceUpdate"
        ]
    }
}

/**
 * Section note on saving and < icons
 */
def section_no_save_note() {section("Please note") { paragraph "Do not use the \"<\" or the \"Save\" buttons on this page."}}

/**
 * our main page dynamicly generated to control page based upon logic.
 */
def page_main() {

    // make sure we are listening to all network subscriptions
    initSubscriptions()

    // send out a UPNP broadcast discovery
    discover_alarmdecoder()

    // see if we are already installed
    def foundMsg = ""
    def mainDevice = getChildDevice("${state.ip}")
    if (mainDevice) foundMsg = "**** AlarmDecoder already installed ****"

    dynamicPage(name: "page_main") {
        if (!mainDevice) {
            section("") {
                href("pageDiscoverDevices", required: false, title: titles("page_discover_devices"), description: descriptions("href_discover_devices"))
            }
        } else {
            section("") {
                paragraph(foundMsg)
            }
            section("") {
                href("pageRemoveAll", required: false, title: titles("page_remove_all"), description: descriptions("href_remove_all"))
            }        
            section("") {
                href("pageCidManagement", required: false, title: titles("page_cid_management"), description: descriptions("href_cid_management"))
            }
        }
    }
}

/**
 * Page page_cid_management generator.
 */
def page_cid_management() {
    // TODO: Find a way to clear our current values on loading page
    return dynamicPage(name: "page_cid_management") {
        def found_devices = []
        getAllChildDevices().each { device ->
            if (device.deviceNetworkId.contains(":CID-"))
            {
                found_devices << device.deviceNetworkId.split(":")[2].trim()
            }
        }
        section("") {
            if (found_devices.size()) {
                input "input_cid_devices", "enum", required: false, multiple: true, options: found_devices, title: titles("input_cid_devices"), description: descriptions("input_cid_devices"), submitOnChange: true
                if (input_cid_devices) {
                    href("pageRemoveSelectedCid", required: false, title: titles("page_remove_selected_cid"), description: descriptions("href_remove_selected_cid"), submitOnChange: true)
                }
            }
        }
        section("") {
            href("pageAddNewCid", required: false, title: titles("page_add_new_cid"), description: descriptions("href_add_new_cid"))
        }
        section_no_save_note()
    }
}

/**
 * Page page_remove_selected_cid generator
 */
def page_remove_selected_cid() {
    def errors = []
    getAllChildDevices().each { device ->
        if (device.deviceNetworkId.contains(":CID-"))
        {
            // Only remove the one that matches our list
            def device_name = device.deviceNetworkId.split(":")[2].trim()
            def d = input_cid_devices.find{ it == device_name }
            if (d)
            {
                log.trace("removing CID device ${device.deviceNetworkId}")
                try {
                    deleteChildDevice(device.deviceNetworkId)
                    input_cid_devices.remove(device_name)
                    errors << "Success removing " + device_name                    
                }
                catch (e) { 
                    log.error "There was an error (${e}) when trying to delete the child device"
                    errors << "Error removing " + device_name
                }
            }
        }
    }
    return dynamicPage(name: "page_remove_selected_cid") {
        section("") {
            paragraph titles("info_page_remove_selected_cid")
            errors.each { error -> 
                paragraph(error)
            }
        }
    }
}

/**
 * Page page_add_new_cid generator
 */
def page_add_new_cid() {
    // list of some of the CID #'s and descriptions.
    // 000 will trigger a manual input of the CID number.
    def cid_numbers = [  "0": "000 - Other / Custom",
                       "101": "101 - Pendant Transmitter",
                       "110": "110 - Fire",
                       "150": "150 - 24 HOUR (AUXILIARY)",
                       "154": "154 - Water Leakage",
                       "158": "158 - High Temp",
                       "162": "162 - Carbon Monoxide Detected",
                       "401": "401 - Arm AWAY OPEN/CLOSE",
                       "441": "441 - Arm STAY OPEN/CLOSE",
                       "4[0,4]1": "4[0,4]1 - Arm Stay or Away OPEN/CLOSE"]
    
    return dynamicPage(name: "page_add_new_cid") {
        // show pre defined CID number templates to select from
        section("") {
            paragraph titles("section_build_cid", buildcid())
            input "input_cid_number", "enum", required: true, submitOnChange: true, multiple: false, title: titles("input_cid_number"), description: descriptions("input_cid_number"), options: cid_numbers 
        }
        // if a CID entry is selected then check the value if it is "0" to show raw input section
        if (input_cid_number) {
            if (input_cid_number == "0") {
                section {
                    input(name: "input_cid_number_raw", type: "text", required: true, defaultValue: 110, submitOnChange: true, title: titles("input_cid_number_raw"))
                }
            }
            section {
                paragraph titles("section_cid_value", buildcidvalue())
                input(name: "input_cid_value", type: "text", required: true, defaultValue: 001,submitOnChange: true, title: titles("input_cid_value"))
            }
            section {
                paragraph titles("section_cid_partition")
                input(name: "input_cid_partition", type: "number", required: false, defaultValue: 1, submitOnChange: true, title: titles("input_cid_partition"))
            }
            section {
                paragraph titles("section_cid_names")
                input(name: "input_cid_name", type: "text", required: false, submitOnChange: true, title: titles("input_cid_name"))
                input(name: "input_cid_label", type: "text", required: false, submitOnChange: true, title: titles("input_cid_label"))
            }
            // If input_cid_number or input_cid_number_raw have a value
            if ( (input_cid_number && (input_cid_number != "0")) || (input_cid_number_raw)) {
                section(""){ href("pageAddNewCidConfirm", required: false, title: titles("page_add_new_cid_confirm", buildcidlabel()+"("+buildcidnetworkid()+")"), description: descriptions("href_add_new_cid_confirm"))}
            }
            section_no_save_note()
        }
    }
}

/**
 * Helper to build a final CID value from inputs
 */
def buildcid() {
    def cidnum = ""
    if (input_cid_number == "0") {
        cidnum = input_cid_number_raw
    } else {
        cidnum = input_cid_number
    }
    return cidnum
}

def buildcidname() {
    if (input_cid_name) {
        return "CID-" + input_cid_name
    } else {
        return buildcidnetworkid()
    }
}

def buildcidlabel() {
    if (input_cid_label) {
        return "CID-" + input_cid_label
    } else {
        return buildcidnetworkid()
    }
}

def buildcidnetworkid() {
    // get the CID value
    def newcid =  buildcid()

    def cv = buildcidvalue()
    def pt = input_cid_partition
    return "CID-${newcid}-${pt}-${cv}"
}

def buildcidvalue() {
    def cidval = input_cid_value
    return cidval
}

/**
 * Page page_add_new_cid_confirm generator.
 */
def page_add_new_cid_confirm() {
    def errors = []
    // get the CID value
    def newcidlabel =  buildcidlabel()
    def newcidname = buildcidname()
    def newcidnetworkid = buildcidnetworkid()
    def cv = input_cid_value
    def pt = input_cid_partition.toInteger()
    
    // Add virtual CID switch if it does not exist.
    def d = getChildDevice("${state.ip}:${newcidlabel}")
    if (!d)
    {
        def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:${newcidnetworkid}", state.hub,
                                [name: "${state.ip}:${newcidname}", label: "${sname} ${newcidlabel}", completedSetup: true])
        nd.sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false)
        errors << "Success adding ${newcidlabel}"
    } else {
        errors << "Error adding ${newcidlabel}: Exists" 
    }

    return dynamicPage(name: "page_add_new_cid_confirm") {
        section("") {
            paragraph titles("info_add_new_cid_confirm")
            errors.each { error -> 
                paragraph(error)
            }            
        }
    }
}

/**
 * Page page_remove_all generator.
 */
def page_remove_all(params) {
    def message = ""

    return dynamicPage(name: "page_remove_all") {
        if (params?.confirm) {
            uninstalled()
            message = titles("info_remove_all_a")
        } else {
            section("") {
                href("pageRemoveAll", title: titles("confirm_remove_all"), description: descriptions("href_refresh_devices"), required: false, params: [confirm: true])
            }
            message = titles("info_remove_all_b")
        }
        section("") {
            paragraph message
        }
    }
}

/**
 * Page page_discover_devices generator. Called periodically to refresh content of the page.
 */
def page_discover_devices() {

    // send out UPNP discovery messages and watch for responses
    discover_alarmdecoder()

    // build list of currently known AlarmDecoder parent devices
    def found_devices = [:]
    def options = state.devices.each { k, v ->
        if (debug) log.debug "page_discover_devices: ${v}"
        def ip = convertHexToIP(v.ip)
        found_devices["${v.ip}"] = "AlarmDecoder @ ${ip}"
    }

    // How many do we have?
    def numFound = found_devices.size() ?: 0

    return dynamicPage(name: "page_discover_devices") {
        section("Devices") {
            input "input_selected_devices", "enum", required: true, title: titles("input_selected_devices",numFound), multiple: true, options: found_devices
            // Allow user to force a new UPNP discovery message
            href("pageDiscoverDevices", title: titles("href_refresh_devices"), description: descriptions("href_refresh_devices"), required: false)
        }
        section("Smart Home Monitor Integration") {
            input(name: "shmIntegration", type: "bool", defaultValue: true, title: titles("shmIntegration"))
            input(name: "shmChangeSHMStatus", type: "bool", defaultValue: true, title: titles("shmChangeSHMStatus"))
        }
        section("Zone Sensors") {
            input(name: "defaultSensorToClosed", type: "bool", defaultValue: true, title: titles("defaultSensorToClosed"))
        }
    }
}

/*** Pre-Defined callbacks ***/

/**
 *  installed()
 */
def installed() {
    log.trace "installed"
    if (debug) log.debug "Installed with settings: ${settings}"

    // initialize everything
    initialize()
}

/**
 * updated()
 */
def updated() {
    log.trace "updated"
    if (debug) log.debug "Updated with settings: ${settings}"

    // re initialize everything
    initialize()
}

/**
 * uninstalled()
 */
def uninstalled() {
    log.trace "uninstalled"

    // disable all scheduling and subscriptions
    unschedule()

    // remove all the devices and children
    def devices = getAllChildDevices()
    devices.each {
        try {
            log.debug "deleting child device: ${it.deviceNetworkId}"
            deleteChildDevice(it.deviceNetworkId)
        }
        catch(Exception e) {
            log.trace("exception while uninstalling: ${e}")
        }
    }
}

/**
 * initialize called upon update and at startup
 *   Add subscriptions and schdules
 *   Create our default state
 *
 */
def initialize() {
    log.trace "initialize"

    // unsubscribe from everything
    unsubscribe()

    // remove all schedules
    unschedule()

    // Create our default state values
    state.lastSHMStatus = null
    state.lastAlarmDecoderStatus = null

    // Network and SHM subscriptions
    initSubscriptions()

    // if a device in the GUI is selected then add it.
    if (input_selected_devices) {
        addExistingDevices()
    }

    // Device handler -> service subscriptions
    configureDeviceSubscriptions()

    // keep us subscribed to notifications
    getAllChildDevices().each { device ->
        // Only refresh the main device that has a panel_state
        def device_type = device.getTypeName()
        if (device_type == "AlarmDecoder network appliance")
        {
            if (debug) log.debug("initialize: Found device refresh subscription.")
            device.subscribeNotifications()
        }
    }
}

/*** Handlers ***/

/**
 * locationHandler(evt)
 * Local network messages sent to TCP port 39500 and UPNP UDP port 1900 will be captured here.
 *
 * Test from the AlarmDecoder Appliance:
 *   curl -H "Content-Type: application/json" -X POST -d ‘{"message":"Hi, this is a test from AlarmDecoder network device"}’ http://YOUR.HUB.IP.ADDRESS:39500
 */
def locationHandler(evt) {
    if (debug) log.debug "locationHandler"

    def description = evt.description
    def hub = evt?.hubId

    // many events but we only want PUSH notifications and they have all the data in evt.description
    if (!description)
     return

    if (debug)
      log.debug "locationHandler: description: ${description} name: ${evt.name} value: ${evt.value} data: ${evt.data}"

    def parsedEvent = ["hub":hub]
    try {
        parsedEvent << parseEventMessage(description)
    }
    catch(Exception e) {
        log.error("exception in parseEventMessage: evt: ${evt.name}: ${evt.value} : ${evt.data}")
        return
    }

    if (debug) log.debug("locationHandler parsedEvent: ${parsedEvent}")

    // UPNP LAN EVENTS on UDP port 1900 from 'AlarmDecoder:1' devices only
    if (parsedEvent?.ssdpTerm?.contains("urn:schemas-upnp-org:device:AlarmDecoder:1")) {

        // make sure our state.devices is initialized. return discard.
        def alarmdecoders = getDevices()

        // add the device to state.devices if it does not exist yet
        if (!(alarmdecoders."${parsedEvent.ssdpUSN.toString()}")) {
            if (debug) log.debug "locationHandler: Adding device: ${parsedEvent.ssdpUSN}"
            alarmdecoders << ["${parsedEvent.ssdpUSN.toString()}": parsedEvent]
        } else
        { // It exists so update if needed
            // grab the device object based upon ur ssdpUSN
            if (debug) log.debug  "alarmdecoders ${alarmdecoders}"
            def d = alarmdecoders."${parsedEvent.ssdpUSN.toString()}"

            if (debug) log.debug "locationHandler: checking for device changed values on device=${d}"

            // Did the DNI change? if so update it.
            if (d.ip != parsedEvent.ip || d.port != parsedEvent.port) {
                // update the state.devices DNI
                d.ip = parsedEvent.ip
                d.port = parsedEvent.port

                if (debug) log.debug "locationHandler: device DNI changed values!"

                // Update device by its MAC address if the DNI changes
                def children = getChildDevices()
                children.each {
                    if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
                        it.setDeviceNetworkId((parsedEvent.ip + ":" + parsedEvent.port))
                        if (debug) log.debug "Set new network id: " + parsedEvent.ip + ":" + parsedEvent.port
                    }
                }
            }

            // TODO: if the ssdpPath changes we need to fetch a new one
            if (d.ssdpPath != parsedEvent.ssdpPath) {
                // update the ssdpPath
                d.ssdpPath = parsedEvent.ssdpPath
                if (debug) log.debug "locationHandler: device ssdpPath changed values. need to fetch new description.xml."

                // send out reqeusts for xml description for anyone not verified yet
                // FIXME: verifyAlarmDecoders()
            }
        }
    } else if (parsedEvent?.headers && parsedEvent?.body)
    { // HTTP EVENTS on TCP port 39500 RESPONSES
      // for some reason they hit here and in the parse() in the device?
        def headerString = new String(parsedEvent.headers.decodeBase64())
        def bodyString = new String(parsedEvent.body.decodeBase64())
        def type = (headerString =~ /Content-Type:.*/) ? (headerString =~ /Content-Type:.*/)[0] : null

        if (debug) log.debug ("locationHandler HTTP event type:${type} body:${bodyString} headers:${headerString}")

        // XML PUSH data
        if (type?.contains("xml"))
        {
            getAllChildDevices().each { device ->
                // Only refresh the main device that has a panel_state
                def device_type = device.getTypeName()
                if (device_type == "AlarmDecoder network appliance")
                {
                    if (debug) log.debug ("push_update_alarmdecoders: Found device sending pushed data.")
                    device.parse_xml(headerString, bodyString).each { e-> device.sendEvent(e) }
                }
            }
        }
    }
}

/**
 * Handle remote web requests to the ST cloud services for http://somegraph/update
 */
def webserviceUpdate()
{
    log.trace "webserviceUpdate"
    refresh_alarmdecoders()
    return [status: "OK"]
}

/**
 * Handle our child device action button events
 * sets Contact attributes of the alarmdecoder smoke device
 */
def actionButton(id) {
    if (debug) log.debug("actionButton: desc=${id}")

    // grab our primary AlarmDecoder device object
    def d = getChildDevice("${state.ip}")
    if (!d) {
        log.error("actionButton: Could not find primary device '${state.ip}'.")
        return
    }

    /* FIXME: Need a pin code or some way to trust the request.
    if (id.contains(":disarm")) {
        d.disarm()
    } */

    if (id.contains(":armAway")) {
        d.arm_away()
    }
    if (id.contains(":armStay")) {
        d.arm_stay()
    }
    if (id.contains(":chimeMode")) {
        d.chime()
    }
    if (id.contains(":alarmPanic")) {
        d.panic()
    }
    if (id.contains(":alarmAUX")) {
        d.aux()
    }
    if (id.contains(":alarmFire")) {
        d.fire()
    }
    if (id.contains(":CID-")) {
        def cd = getChildDevice("${id}")
        if (!cd) {
            log.error("actionButton: Could not CID device '${id}'.")
            return
        }
        cd.sendEvent(name: "switch", value: "off", isStateChange: true, filtered: true)
    }
}

/**
 * send event to SmokeAlarm device to set state
 */
def smokeSet(evt) {
    if (debug) log.debug("smokeSet: desc=${evt.value}")

    def d = getChildDevices().find { it.deviceNetworkId.contains(":SmokeAlarm") }
    if (!d)
    {
        log.error("smokeSet: Could not find 'SmokeAlarm' device.")
        return
    }
    d.sendEvent(name: "smoke", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to armAway device to set state
 */
def armAwaySet(evt) {
    if (debug) log.debug("armAwaySet ${evt.value}")
    def d = getChildDevice("${state.ip}:armAway")
    if (!d) {
        log.error("armAwaySet: Could not find 'armAway' device.")
        return
    }
    d.sendEvent(name: "switch", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to armStay device to set state
 */
def armStaySet(evt) {
    if (debug) log.debug("armStaySet ${evt.value}")
    def d = getChildDevice("${state.ip}:armStay")
    if (!d) {
        log.error("armStaySet: Could not find 'armStay' device.")
        return
    }
    d.sendEvent(name: "switch", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to alarmbell indicator device to set state
 */
def alarmBellSet(evt) {
    if (debug)
      log.debug("alarmBellSet ${evt.value}")
    def d = getChildDevice("${state.ip}:alarmBellIndicator")
    if (!d) {
        log.error("alarmBellSet: Could not find 'alarmBellIndicator' device.")
        return
    }
    d.sendEvent(name: "contact", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to chime indicator device to set state
 */
def chimeSet(evt) {
    if (debug) log.debug("chimeSet ${evt.value}")
    def d = getChildDevice("${state.ip}:chimeMode")
    if (!d) {
        log.error("chimeSet: Could not find device 'chimeMode'")
        return
    }
    d.sendEvent(name: "switch", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to bypass indicator device to set state
 */
def bypassSet(evt) {
    if (debug) log.debug("bypassSet ${evt.value}")
    def d = getChildDevice("${state.ip}:bypass")
    if (!d) {
        log.error("bypassSet: Could not find device 'bypass'")
        return
    }
    d.sendEvent(name: "contact", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send event to ready indicator device to set state
 */
def readySet(evt) {
    if (debug) log.debug("readySet ${evt.value}")
    def d = getChildDevice("${state.ip}:readyIndicator")
    if (!d) {
        log.error("readySet: Could not find 'readyIndicator' device.")
        return
    }
    d.sendEvent(name: "contact", value: evt.value, isStateChange: true, filtered: true)
}

/**
 * send CID event to the correct device if one exists
 * evt.value example !LRR:001,1,CID_1406,ff
 */
def cidSet(evt) {
    log.info("cidSet ${evt.value}")

    // get our CID state and number
    def parts = evt.value.split(',')

    // 1 digit QUALIFIER 1 = Event or Open, 3 = Restore or Close   
    def cidstate = (parts[2][-4..-4] == "1") ? "on" : "off"

    // 3 digit CID number
    def cidnum = parts[2][-3..-1]

    // the CID report value. Zone # or User # or ...
    def cidvalue = parts[0].split(':')[1]

    // the partition # with 0 being system
    def partition =  parts[1].toInteger()

    if (debug) log.debug("cidSet num:${cidnum} part: ${partition} state:${cidstate} val:${cidvalue}")

    def sent = false
    def rawmsg = evt.value
    def device_name = "CID-${cidnum}-${partition}-${cidvalue}"
    def children = getChildDevices()
    children.each {
        if (it.deviceNetworkId.contains(":CID-")) {
            def match = it.deviceNetworkId.split(":")[2].trim()
            if (device_name =~ /${match}/) {
                if (debug) log.error("cidSet device: ${device_name} matches ${match} sendng state ${cidstate}")
                it.sendEvent(name: "switch", value: cidstate, isStateChange: true, filtered: true)
                sent = true
            } else {
                if (debug) log.error("cidSet device: ${device_name} no match ${match}")
            }
        }
    }

    if (!sent) {
        log.error("cidSet: Could not find 'CID-${cidnum}-${partition}-${cidvalue}|XXX' device.")
        return
    }
}

/**
 * Handle Device Command zoneOn()
 * sets Contact attributes of the alarmdecoder device to open/closed
 */
def zoneOn(evt) {
    if (debug) log.debug("zoneOn: desc=${evt.value}")

    def d = getChildDevices().find { it.deviceNetworkId.endsWith("switch${evt.value}") }
    if (d)
    {
        def sensorValue = "closed"
        if (settings.defaultSensorToClosed == true)
            sensorValue = "open"

        d.sendEvent(name: "contact", value: sensorValue, isStateChange: true, filtered: true)
    }
}

/**
 * Handle Device Command zoneOff()
 * sets Contact attributes of the alarmdecoder device to open/closed
 */
def zoneOff(evt) {
    if (debug) log.debug("zoneOff: desc=${evt.value}")

    def d = getChildDevices().find { it.deviceNetworkId.endsWith("switch${evt.value}") }
    if (d)
    {
        def sensorValue = "open"
        if (settings.defaultSensorToClosed == true)
            sensorValue = "closed"

        d.sendEvent(name: "contact", value: sensorValue, isStateChange: true, filtered: true)
    }
}

/**
 * Handle Smart Home Monitor App alarmSystemStatus events and update the UI of the App.
 *
 */
def shmAlarmHandler(evt) {
    if (settings.shmIntegration == false)
        return

    if (debug) log.debug("shmAlarmHandler -- ${evt.value}, lastSHMStatus ${state.lastSHMStatus}, lastAlarmDecoderStatus ${state.lastAlarmDecoderStatus}")

    if (state.lastSHMStatus != evt.value && evt.value != state.lastAlarmDecoderStatus)
    {
        getAllChildDevices().each { device ->
            if (!device.deviceNetworkId.contains(":switch"))
            {
                if (evt.value == "away")
                    device.arm_away()
                else if (evt.value == "stay")
                    device.arm_stay()
                else if (evt.value == "off")
                    device.disarm()
                else
                    log.debug "Unknown SHM alarm value: ${evt.value}"
            }
        }
    }

    state.lastSHMStatus = evt.value
}

/**
 * Handle Alarm events from the AlarmDecoder and
 * send them back to the Smart Home Monitor API to update the
 * status of the alarm panel
 */
def alarmdecoderAlarmHandler(evt) {
    if (settings.shmIntegration == false || settings.shmChangeSHMStatus == false)
        return

    if (debug) log.trace("alarmdecoderAlarmHandler: ${evt.value}")

    if (state.lastAlarmDecoderStatus != evt.value && evt.value != state.lastSHMStatus) {
        if (debug) log.debug("alarmdecoderAlarmHandler: sendLocationEvent")
        sendLocationEvent(name: "alarmSystemStatus", value: evt.value)
    }

    state.lastAlarmDecoderStatus = evt.value
}

/*** Utility ***/

/**
 * Enable primary network and system subscriptions
 */
def initSubscriptions() {
    // subscribe to the Smart Home Manager api for alarm status events
    if (debug) log.debug("initialize: subscribe to SHM alarmSystemStatus API messages")
    subscribe(location, "alarmSystemStatus", shmAlarmHandler)

    /* subscribe to local LAN messages to this HUB on TCP port 39500 and UPNP UDP port 1900 */
    if (debug) log.debug("initialize: subscribe to locations local LAN messages")
    subscribe(location, "ssdpTerm", locationHandler, [filterEvents: false])
}

/**
 * Called by page_discover_devices page periodically
 */
def discover_alarmdecoder() {
    if (debug) log.debug("discover_alarmdecoder")

    // Request HUB send out a UpNp broadcast discovery messages on the local network
    sendHubCommand(new hubitat.device.HubAction("lan discovery urn:schemas-upnp-org:device:AlarmDecoder:1", hubitat.device.Protocol.LAN))
}

/**
 * Call refresh() on the AlarmDecoder parent device object.
 * This will force the HUB to send a REST API request to the AlarmDecoder Network Appliance.
 * and get back the current status of the AlarmDecoder.
 */
def refresh_alarmdecoders() {
    if (debug) log.debug("refresh_alarmdecoders")

    getAllChildDevices().each { device ->
        // Only refresh the main device that has a panel_state
        def device_type = device.getTypeName()
        if (device_type == "AlarmDecoder network appliance")
        {
            def apikey = device._get_api_key()
            if (apikey) {
                device.refresh()
            } else {
                log.error("refresh_alarmdecoders no API KEY for: ${device} @ ${device.getDataValue("urn")}")
            }
        }
    }
}

/**
 * return the list of known devices and initialize the list if needed.
 *
 * FIXME: SM20180315:
 *        This uses the ssdpUSN as the key when we also use DNI
 *        Why not just use DNI all over or ssdpUSN. Keep it consistent.
 *        We get ssdpUSN from our UPNP discovery messages on port 1900
 *        and then we get DNI messages from our GET requests to the
 *        alarmdecoder web services on port 5000. We can also get DNI
 *        from Notification events we subscribe to when the AlarmDecoder
 *        sends us requests on port 39500. Easy way is to use DNI as we get
 *        it every time from all requests. Downside is we can not have more
 *        than one AlarmDecoder per IP:PORT. This seems ok to me for now.
 *
 *
 *  state.devices structure
 *  [
 *      uuid:0c510e98-8ce0-11e7-81a5-XXXXXXXXXXXXXX:
 *      [
 *          port:1388,
 *          ssdpUSN:uuid:0c510e98-8ce0-11e7-81a5-XXXXXXXXXXXXXX,
 *          devicetype:04,
 *          mac:XXXXXXXXXX02,
 *          hub:936de0be-1cb7-4185-9ac9-XXXXXXXXXXXXXX,
 *          ssdpPath:http://XXX.XXX.XXX.XXX:5000,
 *          ssdpTerm:urn:schemas-upnp-org:device:AlarmDecoder:1,
 *          ip:XXXXXXX2
 *      ],
 *      uuid:592952ba-77b0-11e7-b0c7-XXXXXXXXXXXXXX:
 *      [
 *          port:1388,
 *          ssdpUSN:uuid:592952ba-77b0-11e7-b0c7-XXXXXXXXXXXXXX,
 *          devicetype:04,
 *          mac:XXXXXXXXXX01,
 *          hub:936de0be-1cb7-4185-9ac9-XXXXXXXXXXXXXX,
 *          ssdpPath:/static/device_description.xml,
 *          ssdpTerm:urn:schemas-upnp-org:device:AlarmDecoder:1,
 *          ip:XXXXXXX1
 *      ]
 *  ]
 *
 */
def getDevices() {
    if (!state.devices) {
        state.devices = [:]
    }
    return state.devices
}

/**
 * Add devices selected in the GUI if new.
 */
def addExistingDevices() {
    if (debug) log.debug("addExistingDevices: ${input_selected_devices}")

    def selected_devices = input_selected_devices
    if (selected_devices instanceof java.lang.String) {
        selected_devices = [selected_devices]
    }

    selected_devices.each { dni ->
        def d = getChildDevice(dni)
        if (debug) log.debug("addExistingDevices, getChildDevice(${dni})")
        if (!d) {

            // Find the device with a matching dni XXXXXXXX:XXXX
            def newDevice = state.devices.find { /*k, v -> k == dni*/ k, v -> dni == "${v.ip}" }
            if (debug) log.debug("addExistingDevices, devices.find=${newDevice}")

            if (newDevice) {
                // Set the device network ID so that hubactions get sent to the device parser.
                state.ip = newDevice.value.ip
                state.hub = newDevice.value.hub

                // Set URN for the child device
                state.urn = convertHexToIP(state.ip) 
                if (debug) log.debug("AlarmDecoder webapp api endpoint('${state.urn}')")

                // Create device adding the URN to its data object
                d = addChildDevice("alarmdecoder",
                                   "AlarmDecoder network appliance",
                                   "${state.ip}",
                                   newDevice?.value.hub,
                                   [
                                       name: "${state.ip}",
                                       label: "${lname}(${sname})",
                                       completedSetup: true,
                                       /* data associated with this AlarmDecoder */
                                       data:[
                                                // save mac address to update if IP / PORT change
                                                mac: newDevice.value.mac,
                                                ssdpUSN: newDevice.value.ssdpUSN,
                                                urn: state.urn,
                                                ssdpPath: newDevice.value.ssdpPath
                                            ]
                                   ])

                // Set default device state to notready.
                d.sendEvent(name: "panel_state", value: "notready", isStateChange: true, displayed: true)

            }
            // Add virtual zone contact sensors if they do not exist.
            for (def i = 0; i < 12; i++)
            {
                def newSwitch = state.devices.find { k, v -> k == "${state.ip}:switch${i+1}" }
                if (!newSwitch)
                {
                    def zone_switch = addChildDevice("alarmdecoder", "AlarmDecoder virtual contact sensor", "${state.ip}:switch${i+1}", state.hub, [name: "${state.ip}:switch${i+1}", label: "${sname} Zone Sensor #${i+1}", completedSetup: true])

                    def sensorValue = "open"
                    if (settings.defaultSensorToClosed == true)
                        sensorValue = "closed"

                    // Set default contact state.
                    zone_switch.sendEvent(name: "contact", value: sensorValue, isStateChange: true, displayed: false)
                }
            }

            // Add virtual Smoke Alarm sensors if it does not exist.
            def cd = state.devices.find { k, v -> k == "${state.ip}:SmokeAlarm" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder virtual smoke alarm", "${state.ip}:SmokeAlarm", state.hub,
                [name: "${state.ip}:smokeAlarm", label: "${sname} Smoke Alarm", completedSetup: true])
                nd.sendEvent(name: "smoke", value: "clear", isStateChange: true, displayed: false)
            }

            // Add virtual Arm Stay switch if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:armStay" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:armStay", state.hub,
                [name: "${state.ip}:armStay", label: "${sname} Stay", completedSetup: true])
                nd.sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false)
            }

            // Add virtual Arm Away switch if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:armAway" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:armAway", state.hub,
                [name: "${state.ip}:armAway", label: "${sname} Away", completedSetup: true])
                nd.sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false)
            }

            // Add virtual Chime switch if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:chimeMode" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:chimeMode", state.hub,
                [name: "${state.ip}:chimeMode", label: "${sname} Chime", completedSetup: true])
                nd.sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false)
            }

            // Add virtual Bypass switch if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:bypass" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder status indicator", "${state.ip}:bypass", state.hub,
                [name: "${state.ip}:bypass", label: "${sname} Bypass", completedSetup: true])
                nd.sendEvent(name: "contact", value: "close", isStateChange: true, displayed: false)
            }

            // Add virtual Ready contact if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:readyIndicator" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder status indicator", "${state.ip}:readyIndicator", state.hub,
                [name: "${state.ip}:readyIndicator", label: "${sname} Ready", completedSetup: true])
                nd.sendEvent(name: "contact", value: "close", isStateChange: true, displayed: false)
            }

            // Add virtual Alarm Bell contact if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:alarmBell" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder status indicator", "${state.ip}:alarmBellIndicator", state.hub,
                [name: "${state.ip}:alarmBellIndicator", label: "${sname} Alarm Bell", completedSetup: true])
                nd.sendEvent(name: "contact", value: "close", isStateChange: true, displayed: false)
            }

            // Add FIRE alarm button if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:alarmFire" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:alarmFire", state.hub,
                [name: "${state.ip}:alarmFire", label: "${sname} Fire Alarm", completedSetup: true])
                nd.sendEvent(name: "switch", value: "close", isStateChange: true, displayed: false)
            }

            // Add Panic alarm button if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:alarmPanic" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:alarmPanic", state.hub,
                [name: "${state.ip}:alarmPanic", label: "${sname} Panic Alarm", completedSetup: true])
                nd.sendEvent(name: "switch", value: "close", isStateChange: true, displayed: false)
            }

            // Add AUX alarm button if it does not exist.
            cd = state.devices.find { k, v -> k == "${state.ip}:alarmAUX" }
            if (!cd)
            {
                def nd = addChildDevice("alarmdecoder", "AlarmDecoder action button indicator", "${state.ip}:alarmAUX", state.hub,
                [name: "${state.ip}:alarmAUX", label: "${sname} AUX Alarm", completedSetup: true])
                nd.sendEvent(name: "switch", value: "close", isStateChange: true, displayed: false)
            }
        }
    }
}

/**
 * Configure subscriptions the virtual devices will send too.
 */
private def configureDeviceSubscriptions() {
    if (debug) log.debug("configureDeviceSubscriptions")
    def device = getChildDevice("${state.ip}")
    if (!device) {
        log.error("configureDeviceSubscriptions: Could not find primary device.")
        return
    }

    /* Handle events sent from the AlarmDecoder network appliance device
     * to update virtual zones when they change.
     */
    subscribe(device, "zone-on", zoneOn, [filterEvents: false])
    subscribe(device, "zone-off", zoneOff, [filterEvents: false])

    /* Subscribe to Smart Home Monitor(SHM) alarmStatus events
     */
    subscribe(device, "alarmStatus", alarmdecoderAlarmHandler, [filterEvents: false])

    // subscrib to smoke-set handler for updates
    subscribe(device, "smoke-set", smokeSet, [filterEvents: false])

    // subscribe to arm-away handler
    subscribe(device, "arm-away-set", armAwaySet, [filterEvents: false])

    // subscribe to arm-stay handler
    subscribe(device, "arm-stay-set", armStaySet, [filterEvents: false])

    // subscribe to chime handler
    subscribe(device, "chime-set", chimeSet, [filterEvents: false])

    // subscribe to bypass handler
    subscribe(device, "bypass-set", bypassSet, [filterEvents: false])

    // subscribe to alarm bell handler
    subscribe(device, "alarmbell-set", alarmBellSet, [filterEvents: false])

    // subscribe to ready handler
    subscribe(device, "ready-set", readySet, [filterEvents: false])

    // subscribe to CID handler
    subscribe(device, "cid-set", cidSet, [filterEvents: false])

}

/**
 * Parse local network messages.
 *
 * May be to UDP port 1900 for UPNP message or to TCP port 39500
 * for local network to hub push messages.
 *
 */
private def parseEventMessage(String description) {
    if (debug)
      log.debug "parseEventMessage: $description"
    def event = [:]
    def parts = description.split(',')
    parts.each { part ->
        part = part.trim()
        if (part.startsWith('devicetype:')) {
            def valueString = part.split(":")[1].trim()
            event.devicetype = valueString
        }
        else if (part.startsWith('mac:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                event.mac = valueString
            }
        }
        else if (part.startsWith('networkAddress:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                event.ip = valueString
            }
        }
        else if (part.startsWith('deviceAddress:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                event.port = valueString
            }
        }
        else if (part.startsWith('ssdpPath:')) {
            part -= "ssdpPath:"
            def valueString = part.trim()
            if (valueString) {
                event.ssdpPath = valueString
            }
        }
        else if (part.startsWith('ssdpUSN:')) {
            part -= "ssdpUSN:"
            def valueString = part.trim()
            if (valueString) {
                event.ssdpUSN = valueString
            }
        }
        else if (part.startsWith('ssdpTerm:')) {
            part -= "ssdpTerm:"
            def valueString = part.trim()
            if (valueString) {
                event.ssdpTerm = valueString
            }
        }
        else if (part.startsWith('headers')) {
            part -= "headers:"
            def valueString = part.trim()
            if (valueString) {
                event.headers = valueString
            }
        }
        else if (part.startsWith('body')) {
            part -= "body:"
            def valueString = part.trim()
            if (valueString) {
                event.body = valueString
            }
        }
    }

    event
}

/**
 * Send a request for the description.xml For every known AlarmDecoder
 * we have discovered that is not verified.
 */
def verifyAlarmDecoders() {
    def devices = getDevices().findAll { it?.value?.verified != true }

  if (devices) {
        log.warn "verifyAlarmDecoders: UNVERIFIED Decoders!: $devices"
  }

  devices.each {
        if (it?.value?.ssdpPath?.contains("xml")) {
            verifyAlarmDecoder((it?.value?.ip + ":" + it?.value?.port), it?.value?.ssdpPath)
        } else {
            log.warn("verifyAlarmDecoders: invalid ssdpPath not an xml file")
        }
    }
}

/**
 * Send a GET request from the HUB to the AlarmDecoder for its descrption.xml file
 */
def verifyAlarmDecoder(String deviceNetworkId, String ssdpPath) {
  String ip = getHostAddressFromDNI(deviceNetworkId)

  if (debug) log.debug("verifyAlarmDecoder: $deviceNetworkId ssdpPath: ${ssdpPath} ip: ${ip}")

  def result = new hubitat.device.HubAction([
  method: "GET",
        path: ssdpPath,
        headers: [Host: ip, Accept: "*/*"]],
        deviceNetworkId
  )
  sendHubCommand(result)
}

/**
 * Convert from internal format networkAddress:C0A8016F to a real IP address string 192.168.1.111
 */
private String convertHexToIP(hex) {
    [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}

/**
 * convert hex encoded string to integer
 */
private Integer convertHexToInt(hex) {
    Integer.parseInt(hex,16)
}

/**
 * build a URI host address of the AlarmDecoder web appliance for web requests.
 *  ex. XXX.XXX.XXX.XXX:XXXXX -> 192.168.1.1:5000
 */
private getHostAddressFromDNI(d) {
  def parts = d.split(":")
  def ip = convertHexToIP(parts[0])
  def port = convertHexToInt(parts[1])
  return ip + ":" + port
}

Smoke detectors is a defined capability, so if it is presenting that capability it should work.

It works in a way very similar to SHM, wrt to other apps integrating with it. Both use location events as the mechanism of integration. The specifics are slightly different.

For example, to receive SHM events one would subscribe like this:
subscribe(location, "alarmSystemStatus", handler)
While in Hubitat it would be this:
subscribe(location, "hsmStatus", handler)

For SHM the events reported for "alarmSystemStatus" were "away", "stay" and "off".
For HSM the corresponding events for "hsmStatus" are "armedAway", "armedHome" and "disarmed".

That status can be set in either system using location events with those values.

There are many more features available in HSM. Read about how to interface to them in this post:

What would be the equivalent of the below command for HSM?

settings.shmIntegration

That's simply a bool input through the UI:

section("Smart Home Monitor Integration") {
    input(name: "shmIntegration", type: "bool", defaultValue: true, title: titles("shmIntegration"))
    input(name: "shmChangeSHMStatus", type: "bool", defaultValue: true, title: titles("shmChangeSHMStatus"))
}

settings.shmIntegration is the value of that input setting. Actually that's an overly verbose way to refer to it, as it could be simply shmintegration in the code.

1 Like

I have added the code to the HE github repository if anyone can help with changes this would be much appreciated. Thanks.

So I started working on the code but I can arm/disarm from the HSM but if I arm/disarm from the AD2PI device it does not update HSM.

/**
 * Handle Smart Home Monitor App hsmStatus events and update the UI of the App.
 *
 */
def alertHandler(evt) {
    if (settings.hsmIntegration == false)
        return

    if (debug) log.debug("alertHandler -- ${evt.value}, lasthsmStatus ${state.lasthsmStatus}, lastAlarmDecoderStatus ${state.lastAlarmDecoderStatus}")

    if (state.lasthsmStatus != evt.value && evt.value != state.lastAlarmDecoderStatus)
    {
        getAllChildDevices().each { device ->
            if (!device.deviceNetworkId.contains(":switch"))
            {
                if (evt.value == "armedAway")
                    device.arm_away()
                else if (evt.value == "armedHome")
                    device.arm_stay()
                else if (evt.value == "disarmed")
                    device.disarm()
                else
                    log.debug "Unknown hsm alarm value: ${evt.value}"
            }
        }
    }

    state.lasthsmStatus = evt.value
}

/**
 * Handle Alarm events from the AlarmDecoder and
 * send them back to the Smart Home Monitor API to update the
 * status of the alarm panel
 */
def alarmdecoderAlarmHandler(evt) {
    if (settings.hsmIntegration == false || settings.hsmChangehsmStatus == false)
        return

    if (debug) log.trace("alarmdecoderAlarmHandler: ${evt.value}")

    if (state.lastAlarmDecoderStatus != evt.value && evt.value != state.lasthsmStatus) {
        if (debug) log.debug("alarmdecoderAlarmHandler: sendLocationEvent")
        sendLocationEvent(name: "hsmSetArm", value: evt.value)
    }

    state.lastAlarmDecoderStatus = evt.value
}

/*** Utility ***/

/**
 * Enable primary network and system subscriptions
 */
def initSubscriptions() {
    // subscribe to the Smart Home Manager api for alarm status events
    if (debug) log.debug("initialize: subscribe to hsm hsmStatus API messages")
    subscribe(location, "hsmStatus", statusHandler)

What is the log output? That's what you are sending to HSM, so what is it? You probably need to take that event, and transform it into the right one for HSM.

There is nothing in the log when I arm/disarm from the AD2PI Appliance. The below log is when I armed/disarmed it from HSM.

app:6132018-09-26 20:32:13.608:debugEvent data successfully posted to SharpTools.io
dev:8152018-09-26 20:32:13.565:trace--- update_state
dev:8152018-09-26 20:32:13.559:trace--- parse_xml
dev:8152018-09-26 20:32:13.557:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:13.555:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:d2d6, headers:Tk9USUZZIC9ub3RpZnkgSFRUUC8xLjENCk5UOiB1cG5wOmV2ZW50DQpIb3N0OiAxMC4xMC4yMC4xMDozOTUwMQ0KQWNjZXB0LUVuY29kaW5nOiBpZGVudGl0eQ0KQ29udGVudC1MZW5ndGg6IDk5MQ0KTlRTOiB1cG5wOnByb3BjaGFuZ2UNCkNvbnRlbnQtVHlwZTogdGV4dC94bWwNClNJRDogdXVpZDphMDk3YjBhNi1jMWI1LTExZTgtOTA0YS1iODI3ZWJiZTBkYjYNCg==, body:PGU6cHJvcGVydHlzZXQgeG1sbnM6ZT0idXJuOnNjaGVtYXMtdXBucC1vcmc6c2VydmljZTpBbGFybURlY29kZXI6MSI+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRpZD4xPC9ldmVudGlkPgogIDwvZTpwcm9wZXJ0eT4KICA8ZTpwcm9wZXJ0eT4KICAgIDxldmVudGRlc2M+ZGlzYXJtPC9ldmVudGRlc2M+CiAgPC9lOnByb3BlcnR5PgogIDxlOnByb3BlcnR5PgogICAgPGV2ZW50bWVzc2FnZT48IVtDREFUQVtUaGUgYWxhcm0gc3lzdGVtIGhhcyBiZWVuIGRpc2FybWVkLl1dPjwvZXZlbnRtZXNzYWdlPgogIDwvZTpwcm9wZXJ0eT4KPGU6cHJvcGVydHk+PHBhbmVsc3RhdGU+PHBhbmVsX2NoaW1lPkZhbHNlPC9wYW5lbF9jaGltZT48cGFuZWxfYWxhcm1pbmc+RmFsc2U8L3BhbmVsX2FsYXJtaW5nPjxwYW5lbF9vbl9iYXR0ZXJ5PkZhbHNlPC9wYW5lbF9vbl9iYXR0ZXJ5PjxwYW5lbF9yZWFkeT5UcnVlPC9wYW5lbF9yZWFkeT48cGFuZWxfZmlyZV9kZXRlY3RlZD5GYWxzZTwvcGFuZWxfZmlyZV9kZXRlY3RlZD48cGFuZWxfcG93ZXJlZD5UcnVlPC9wYW5lbF9wb3dlcmVkPjxwYW5lbF9wYW5pY2tlZD5GYWxzZTwvcGFuZWxfcGFuaWNrZWQ+PHBhbmVsX2FybWVkX3N0YXk+RmFsc2U8L3BhbmVsX2FybWVkX3N0YXk+PHBhbmVsX3R5cGU+QURFTUNPPC9wYW5lbF90eXBlPjxwYW5lbF9ieXBhc3NlZD5GYWxzZTwvcGFuZWxfYnlwYXNzZWQ+PHBhbmVsX2FybWVkPkZhbHNlPC9wYW5lbF9hcm1lZD48cGFuZWxfcmVsYXlfc3RhdHVzIC8+PHBhbmVsX3pvbmVzX2ZhdWx0ZWQgLz48bGFzdF9tZXNzYWdlX3JlY2VpdmVkPjwhLS0gLS0+PCFbQ0RBVEFbWzAxMDEwMjAxMDAwMDAwMDAzQS0tXSwwMDgsW2Y3MDIwMGZmMTAwODAyMGMwYzAyMDAwMDAwMDAwMF0sIkFSTUVEICoqKkFXQVkqKipZb3UgbWF5IGV4aXQgbm93Il1dPjwhLS0gLS0+PC9sYXN0X21lc3NhZ2VfcmVjZWl2ZWQ+PC9wYW5lbHN0YXRlPjwvZTpwcm9wZXJ0eT4KPC9lOnByb3BlcnR5c2V0Pg==
app:6132018-09-26 20:32:13.494:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:13.493:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:13.484:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:13.266:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:13.232:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:13.229:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:13.207:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
dev:8152018-09-26 20:32:13.125:trace--- update_state: new state ************** ready ************
dev:8152018-09-26 20:32:13.123:trace--- update_state
dev:8152018-09-26 20:32:13.117:trace--- parse_xml
dev:8152018-09-26 20:32:13.116:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:13.115:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:d2d4, headers:Tk9USUZZIC9ub3RpZnkgSFRUUC8xLjENCk5UOiB1cG5wOmV2ZW50DQpIb3N0OiAxMC4xMC4yMC4xMDozOTUwMQ0KQWNjZXB0LUVuY29kaW5nOiBpZGVudGl0eQ0KQ29udGVudC1MZW5ndGg6IDk5Ng0KTlRTOiB1cG5wOnByb3BjaGFuZ2UNCkNvbnRlbnQtVHlwZTogdGV4dC94bWwNClNJRDogdXVpZDphMDk3YjBhNi1jMWI1LTExZTgtOTA0YS1iODI3ZWJiZTBkYjYNCg==, body:PGU6cHJvcGVydHlzZXQgeG1sbnM6ZT0idXJuOnNjaGVtYXMtdXBucC1vcmc6c2VydmljZTpBbGFybURlY29kZXI6MSI+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRpZD4xNTwvZXZlbnRpZD4KICA8L2U6cHJvcGVydHk+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRkZXNjPnJlYWR5IGNoYW5nZWQ8L2V2ZW50ZGVzYz4KICA8L2U6cHJvcGVydHk+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRtZXNzYWdlPjwhW0NEQVRBW1JlYWR5IHN0YXR1cyBoYXMgY2hhbmdlZCB0byBUcnVlXV0+PC9ldmVudG1lc3NhZ2U+CiAgPC9lOnByb3BlcnR5Pgo8ZTpwcm9wZXJ0eT48cGFuZWxzdGF0ZT48cGFuZWxfY2hpbWU+RmFsc2U8L3BhbmVsX2NoaW1lPjxwYW5lbF9hbGFybWluZz5GYWxzZTwvcGFuZWxfYWxhcm1pbmc+PHBhbmVsX29uX2JhdHRlcnk+RmFsc2U8L3BhbmVsX29uX2JhdHRlcnk+PHBhbmVsX3JlYWR5PlRydWU8L3BhbmVsX3JlYWR5PjxwYW5lbF9maXJlX2RldGVjdGVkPkZhbHNlPC9wYW5lbF9maXJlX2RldGVjdGVkPjxwYW5lbF9wb3dlcmVkPlRydWU8L3BhbmVsX3Bvd2VyZWQ+PHBhbmVsX3Bhbmlja2VkPkZhbHNlPC9wYW5lbF9wYW5pY2tlZD48cGFuZWxfYXJtZWRfc3RheT5GYWxzZTwvcGFuZWxfYXJtZWRfc3RheT48cGFuZWxfdHlwZT5BREVNQ088L3BhbmVsX3R5cGU+PHBhbmVsX2J5cGFzc2VkPkZhbHNlPC9wYW5lbF9ieXBhc3NlZD48cGFuZWxfYXJtZWQ+RmFsc2U8L3BhbmVsX2FybWVkPjxwYW5lbF9yZWxheV9zdGF0dXMgLz48cGFuZWxfem9uZXNfZmF1bHRlZCAvPjxsYXN0X21lc3NhZ2VfcmVjZWl2ZWQ+PCEtLSAtLT48IVtDREFUQVtbMDEwMTAyMDEwMDAwMDAwMDNBLS1dLDAwOCxbZjcwMjAwZmYxMDA4MDIwYzBjMDIwMDAwMDAwMDAwXSwiQVJNRUQgKioqQVdBWSoqKllvdSBtYXkgZXhpdCBub3ciXV0+PCEtLSAtLT48L2xhc3RfbWVzc2FnZV9yZWNlaXZlZD48L3BhbmVsc3RhdGU+PC9lOnByb3BlcnR5Pgo8L2U6cHJvcGVydHlzZXQ+
dev:8372018-09-26 20:32:12.102:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8362018-09-26 20:32:12.083:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8352018-09-26 20:32:12.064:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8342018-09-26 20:32:12.042:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), dump(), any(), every() (disarm)
dev:8332018-09-26 20:32:12.024:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), dump(), any(), every() (disarm)
dev:8322018-09-26 20:32:12.005:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), dump(), any(), every() (disarm)
dev:8312018-09-26 20:32:11.983:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8302018-09-26 20:32:11.961:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8292018-09-26 20:32:11.941:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), push(), dump(), any() (disarm)
dev:8282018-09-26 20:32:11.898:errorgroovy.lang.MissingMethodException: No signature of method: dev15378334398151221975915.disarm() is applicable for argument types: () values: []
Possible solutions: isCase(java.lang.Object), is(java.lang.Object), wait(), dump(), any(), every() (disarm)
dev:8152018-09-26 20:32:11.817:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:11.816:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:50, headers:SFRUUC8xLjEgMjA0IE5PIENPTlRFTlQNClNlcnZlcjogbmdpbngvMS43LjQNCkRhdGU6IFRodSwgMjcgU2VwIDIwMTggMDA6MzI6MTEgR01UDQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb24NCkNvbnRlbnQtTGVuZ3RoOiAwDQpDb25uZWN0aW9uOiBrZWVwLWFsaXZlDQpBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW46ICoNCkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHM6IFBPU1QNCkFjY2Vzcy1Db250cm9sLU1heC1BZ2U6IDIxNjAwDQpBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzOiBDT05URU5ULVRZUEUsIEFQSV9LRVksIEFVVEhPUklaQVRJT04NClgtRnJhbWUtT3B0aW9uczogU0FNRU9SSUdJTg0KQ2FjaGUtQ29udHJvbDogbm8tY2FjaGUsIG5vLXN0b3JlLCBtdXN0LXJldmFsaWRhdGUsIHByaXZhdGUNClByYWdtYTogbm8tY2FjaGUNClgtWFNTLVByb3RlY3Rpb246IDE7IG1vZGU9YmxvY2sNClgtQ29udGVudC1UeXBlLU9wdGlvbnM6IG5vc25pZmYNCg==, body:
app:6132018-09-26 20:32:11.800:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:11.798:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:11.786:debugEvent data successfully posted to SharpTools.io
app:6122018-09-26 20:32:11.579:errorgroovy.lang.MissingMethodException: No signature of method: app1537999072322408744851.shmAlarmHandler() is applicable for argument types: (com.hubitat.hub.domain.Event) values: [com.hubitat.hub.domain.Event@5071334e] (shmAlarmHandler)
dev:8152018-09-26 20:32:11.566:trace--- hub_http_post: host=10.10.*.*, path=/api/v1/alarmdecoder/send?apikey=ZN6WR2ASJBXA
dev:8152018-09-26 20:32:11.563:trace--- send_keys
dev:8152018-09-26 20:32:11.560:trace--- disarm
app:6132018-09-26 20:32:11.530:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:11.510:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:11.504:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6482018-09-26 20:32:11.477:infoDisarmed
app:6482018-09-26 20:32:11.465:warnAlert Intrusion canceled
dev:8152018-09-26 20:32:07.839:trace--- update_state
dev:8152018-09-26 20:32:07.829:trace--- parse_xml
dev:8152018-09-26 20:32:07.828:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:07.826:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:d2ce, headers:Tk9USUZZIC9ub3RpZnkgSFRUUC8xLjENCk5UOiB1cG5wOmV2ZW50DQpIb3N0OiAxMC4xMC4yMC4xMDozOTUwMQ0KQWNjZXB0LUVuY29kaW5nOiBpZGVudGl0eQ0KQ29udGVudC1MZW5ndGg6IDk4NQ0KTlRTOiB1cG5wOnByb3BjaGFuZ2UNCkNvbnRlbnQtVHlwZTogdGV4dC94bWwNClNJRDogdXVpZDphMDk3YjBhNi1jMWI1LTExZTgtOTA0YS1iODI3ZWJiZTBkYjYNCg==, body:PGU6cHJvcGVydHlzZXQgeG1sbnM6ZT0idXJuOnNjaGVtYXMtdXBucC1vcmc6c2VydmljZTpBbGFybURlY29kZXI6MSI+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRpZD4wPC9ldmVudGlkPgogIDwvZTpwcm9wZXJ0eT4KICA8ZTpwcm9wZXJ0eT4KICAgIDxldmVudGRlc2M+YXJtPC9ldmVudGRlc2M+CiAgPC9lOnByb3BlcnR5PgogIDxlOnByb3BlcnR5PgogICAgPGV2ZW50bWVzc2FnZT48IVtDREFUQVtUaGUgYWxhcm0gc3lzdGVtIGhhcyBiZWVuIGFybWVkLl1dPjwvZXZlbnRtZXNzYWdlPgogIDwvZTpwcm9wZXJ0eT4KPGU6cHJvcGVydHk+PHBhbmVsc3RhdGU+PHBhbmVsX2NoaW1lPkZhbHNlPC9wYW5lbF9jaGltZT48cGFuZWxfYWxhcm1pbmc+RmFsc2U8L3BhbmVsX2FsYXJtaW5nPjxwYW5lbF9vbl9iYXR0ZXJ5PkZhbHNlPC9wYW5lbF9vbl9iYXR0ZXJ5PjxwYW5lbF9yZWFkeT5GYWxzZTwvcGFuZWxfcmVhZHk+PHBhbmVsX2ZpcmVfZGV0ZWN0ZWQ+RmFsc2U8L3BhbmVsX2ZpcmVfZGV0ZWN0ZWQ+PHBhbmVsX3Bvd2VyZWQ+VHJ1ZTwvcGFuZWxfcG93ZXJlZD48cGFuZWxfcGFuaWNrZWQ+RmFsc2U8L3BhbmVsX3Bhbmlja2VkPjxwYW5lbF9hcm1lZF9zdGF5PkZhbHNlPC9wYW5lbF9hcm1lZF9zdGF5PjxwYW5lbF90eXBlPkFERU1DTzwvcGFuZWxfdHlwZT48cGFuZWxfYnlwYXNzZWQ+RmFsc2U8L3BhbmVsX2J5cGFzc2VkPjxwYW5lbF9hcm1lZD5UcnVlPC9wYW5lbF9hcm1lZD48cGFuZWxfcmVsYXlfc3RhdHVzIC8+PHBhbmVsX3pvbmVzX2ZhdWx0ZWQgLz48bGFzdF9tZXNzYWdlX3JlY2VpdmVkPjwhLS0gLS0+PCFbQ0RBVEFbWzEwMDAwMDAxMDAwMDAwMDAzQS0tXSwwMDgsW2Y3MDIwMGZmMTAwODAwMWMwODAyMDAwMDAwMDAwMF0sIioqKipESVNBUk1FRCoqKiogIFJlYWR5IHRvIEFybSAgIl1dPjwhLS0gLS0+PC9sYXN0X21lc3NhZ2VfcmVjZWl2ZWQ+PC9wYW5lbHN0YXRlPjwvZTpwcm9wZXJ0eT4KPC9lOnByb3BlcnR5c2V0Pg==
app:6132018-09-26 20:32:07.814:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:07.771:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:07.765:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:07.749:debugEvent data successfully posted to SharpTools.io
app:6132018-09-26 20:32:07.549:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6482018-09-26 20:32:07.517:warnAlert Intrusion Alarm Ready open
app:6132018-09-26 20:32:07.515:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:07.500:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:07.495:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
dev:8152018-09-26 20:32:07.403:trace--- update_state: new state ************** armed ************
dev:8152018-09-26 20:32:07.399:trace--- update_state
dev:8152018-09-26 20:32:07.396:trace--- parse_xml
dev:8152018-09-26 20:32:07.395:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:07.394:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:d2cc, headers:Tk9USUZZIC9ub3RpZnkgSFRUUC8xLjENCk5UOiB1cG5wOmV2ZW50DQpIb3N0OiAxMC4xMC4yMC4xMDozOTUwMQ0KQWNjZXB0LUVuY29kaW5nOiBpZGVudGl0eQ0KQ29udGVudC1MZW5ndGg6IDk5Nw0KTlRTOiB1cG5wOnByb3BjaGFuZ2UNCkNvbnRlbnQtVHlwZTogdGV4dC94bWwNClNJRDogdXVpZDphMDk3YjBhNi1jMWI1LTExZTgtOTA0YS1iODI3ZWJiZTBkYjYNCg==, body:PGU6cHJvcGVydHlzZXQgeG1sbnM6ZT0idXJuOnNjaGVtYXMtdXBucC1vcmc6c2VydmljZTpBbGFybURlY29kZXI6MSI+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRpZD4xNTwvZXZlbnRpZD4KICA8L2U6cHJvcGVydHk+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRkZXNjPnJlYWR5IGNoYW5nZWQ8L2V2ZW50ZGVzYz4KICA8L2U6cHJvcGVydHk+CiAgPGU6cHJvcGVydHk+CiAgICA8ZXZlbnRtZXNzYWdlPjwhW0NEQVRBW1JlYWR5IHN0YXR1cyBoYXMgY2hhbmdlZCB0byBGYWxzZV1dPjwvZXZlbnRtZXNzYWdlPgogIDwvZTpwcm9wZXJ0eT4KPGU6cHJvcGVydHk+PHBhbmVsc3RhdGU+PHBhbmVsX2NoaW1lPkZhbHNlPC9wYW5lbF9jaGltZT48cGFuZWxfYWxhcm1pbmc+RmFsc2U8L3BhbmVsX2FsYXJtaW5nPjxwYW5lbF9vbl9iYXR0ZXJ5PkZhbHNlPC9wYW5lbF9vbl9iYXR0ZXJ5PjxwYW5lbF9yZWFkeT5GYWxzZTwvcGFuZWxfcmVhZHk+PHBhbmVsX2ZpcmVfZGV0ZWN0ZWQ+RmFsc2U8L3BhbmVsX2ZpcmVfZGV0ZWN0ZWQ+PHBhbmVsX3Bvd2VyZWQ+VHJ1ZTwvcGFuZWxfcG93ZXJlZD48cGFuZWxfcGFuaWNrZWQ+RmFsc2U8L3BhbmVsX3Bhbmlja2VkPjxwYW5lbF9hcm1lZF9zdGF5PkZhbHNlPC9wYW5lbF9hcm1lZF9zdGF5PjxwYW5lbF90eXBlPkFERU1DTzwvcGFuZWxfdHlwZT48cGFuZWxfYnlwYXNzZWQ+RmFsc2U8L3BhbmVsX2J5cGFzc2VkPjxwYW5lbF9hcm1lZD5UcnVlPC9wYW5lbF9hcm1lZD48cGFuZWxfcmVsYXlfc3RhdHVzIC8+PHBhbmVsX3pvbmVzX2ZhdWx0ZWQgLz48bGFzdF9tZXNzYWdlX3JlY2VpdmVkPjwhLS0gLS0+PCFbQ0RBVEFbWzEwMDAwMDAxMDAwMDAwMDAzQS0tXSwwMDgsW2Y3MDIwMGZmMTAwODAwMWMwODAyMDAwMDAwMDAwMF0sIioqKipESVNBUk1FRCoqKiogIFJlYWR5IHRvIEFybSAgIl1dPjwhLS0gLS0+PC9sYXN0X21lc3NhZ2VfcmVjZWl2ZWQ+PC9wYW5lbHN0YXRlPjwvZTpwcm9wZXJ0eT4KPC9lOnByb3BlcnR5c2V0Pg==
dev:8372018-09-26 20:32:06.561:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8362018-09-26 20:32:06.541:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8352018-09-26 20:32:06.520:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8342018-09-26 20:32:06.497:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8332018-09-26 20:32:06.477:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8322018-09-26 20:32:06.455:errorgroovy.lang.MissingMethodException: No signature of method: dev1537833440111152953315.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8312018-09-26 20:32:06.426:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8302018-09-26 20:32:06.405:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8292018-09-26 20:32:06.375:errorgroovy.lang.MissingMethodException: No signature of method: dev15379853345251724392025.arm_away() is applicable for argument types: () values: [] (arm_away)
app:6132018-09-26 20:32:06.368:debugEvent data successfully posted to SharpTools.io
dev:8282018-09-26 20:32:06.345:errorgroovy.lang.MissingMethodException: No signature of method: dev15378334398151221975915.arm_away() is applicable for argument types: () values: [] (arm_away)
dev:8152018-09-26 20:32:06.270:info---  parse: mac: B827EBBE0DB6 requestId: 000000000000
dev:8152018-09-26 20:32:06.269:debugparsing description: mac:B827EBBE0DB6, ip:0a0a1405, port:50, headers:SFRUUC8xLjEgMjA0IE5PIENPTlRFTlQNClNlcnZlcjogbmdpbngvMS43LjQNCkRhdGU6IFRodSwgMjcgU2VwIDIwMTggMDA6MzI6MDYgR01UDQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb24NCkNvbnRlbnQtTGVuZ3RoOiAwDQpDb25uZWN0aW9uOiBrZWVwLWFsaXZlDQpBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW46ICoNCkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHM6IFBPU1QNCkFjY2Vzcy1Db250cm9sLU1heC1BZ2U6IDIxNjAwDQpBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzOiBDT05URU5ULVRZUEUsIEFQSV9LRVksIEFVVEhPUklaQVRJT04NClgtRnJhbWUtT3B0aW9uczogU0FNRU9SSUdJTg0KQ2FjaGUtQ29udHJvbDogbm8tY2FjaGUsIG5vLXN0b3JlLCBtdXN0LXJldmFsaWRhdGUsIHByaXZhdGUNClByYWdtYTogbm8tY2FjaGUNClgtWFNTLVByb3RlY3Rpb246IDE7IG1vZGU9YmxvY2sNClgtQ29udGVudC1UeXBlLU9wdGlvbnM6IG5vc25pZmYNCg==, body:
app:6132018-09-26 20:32:06.239:debugEvent data successfully posted to SharpTools.io
app:6122018-09-26 20:32:06.041:errorgroovy.lang.MissingMethodException: No signature of method: app1537999072322408744851.shmAlarmHandler() is applicable for argument types: (com.hubitat.hub.domain.Event) values: [com.hubitat.hub.domain.Event@25b65a4b] (shmAlarmHandler)
dev:8152018-09-26 20:32:06.037:trace--- hub_http_post: host=10.10.*.*, path=/api/v1/alarmdecoder/send?apikey=ZN6WR2ASJBXA
dev:8152018-09-26 20:32:06.018:trace--- send_keys
dev:8152018-09-26 20:32:06.016:trace--- arm_away
app:6132018-09-26 20:32:06.004:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6132018-09-26 20:32:05.974:infoSending event for hub 45990d64-f80a-4cf6-b2d5-bfba7ce5581d
app:6482018-09-26 20:32:05.907:infoArmed Away

With all of those errors, I'd say you have quite a distance to cover to get that thing to work. You need someone to take it on as a project.

I believe some of the errors are similar just with different devices. This seems to be consistent.

groovy.lang.MissingMethodException: No signature of method: dev153800916566526524754.arm_away() is applicable for argument types: () values: [] (arm_away)