LAN Driver ARP/Network Refresh?


I have a written a LAN device driver that basically acts as a watchdog timer for an external Raspberry PI. Essentially the Pi has a cron job that runs every 10 minutes which triggers a python script to post data which is handled by the LAN device driver that I have written.

This has worked perfectly for the last 2 years until I moved the PI within my network so it could be powered by a UPS. At this point the LAN device no longer received any data at the LAN device driver parse method even though the IP address is the same which is what I use as the DNI.

If I trigger the script on the PI I get a 200 response indicated suggesting that the Hub has received the http request but no indications are in the hub logs that anything has happened.

If I change the MAC address of the raspberry pi temporarily the Hub logs show that the request has been received and everything returns to normal which seems to suggest to me that this might be a stale address resolution within the hub causing the issue however I cant seem to find a way to refresh the internal network tables (databases etc). I have tried to shutdown and remove power from the hub for >30 seconds but the issues persists.

If I do a soft reset of the hub it works as expected until I restore the database and at that point I have the same issue.

Is there a secret endpoint or someway of forcing the LAN ARP tables etc to refresh without having to install all my devices from scratch. At the moment the only way around this I have found is to do a soft reset without restoring the database which would be a pain.

In case it matters I am running this on a C8 Hub with the latest 2.5.1.142 firmware.

Thanks


If you are getting this far, ARP has to be working. TBF you have confirmed OSI layer 1 - 7 works. Also ARP tables don't survive reboots on anything, so focusing on layer 2 after the troubleshooting you have done, would not be advisable.

Since this is a driver you have written, you may want to add additional debug logging to it to see where it fails.

It will be very difficult for the community to help with this issue w/o links to the driver and the device and hub logs.

upgrade to .143 that came out today there was an http parser error in the previous version I found. that was the exact behavior 200 status but nothing returned.

Damn. Was hoping it was going to be an easy fix but unfortunately still seeing the same behavior despite upgrading to .143

@ sidjohn1

Thanks for the suggestion and too be honest I was grasping at straws when I mentioned the ARP tables but I am sure there is something persisting within the Hubitat internals that is causing the behavior I am seeing.

To remove the possibility of my device driver causing the issue I have removed the devices that I created with my device driver and just relied on the behavior that I witness when I ping the hub and looking at the hubs main logs.

When a raise a request from the Pi with its original MAC address I get the following response from the hub (at the pi) indicating that the request has been received at the hub but there are no entries within the hubs log

If I change the PI to its wireless adaptor (which changes the IP and MAC address) and rerun the test I get the same 200 response from the hub but this time the logs show that it processed the request but generates an error as they is no matching device as I have deleted the device to narrow down troubleshooting

image

If I switch back to the wired adaptor but change(spoof) the MAC address of the Pi and rerun the same test. I get the same 200 response but again this time I also get the following entry in the log

image

If I reboot the PI which changes the MAC address back to its original value and rerun the test I get the same 200 response from the hub but again there is no log to show the the request has been processed.

It looks to me that even though I have deleted the devices something within the Hubitat internals/databases etc have retained the MAC address. Because it appears to recognize the MAC Address hence why it doesn't throw the matching device warning however because the device no longer exists nothings happens. If I recreate the device using my device driver and set it up using the original IP address it still doesn't react to the pi request.

I am happy to share my device driver code if you think it would help but given the info above and the fact that device driver is no longer in play I not sure what help it would be.

It feels like there is something internal to the device that needs to be cleared/refresh hence my request if anyone knows of a endpoint that may/would help.

you see in the error where is says "no matching device found"?
hub > devices > in the search bar, search for those values. If nothing comes back, then the error is correct you don't have a device with a proper DNI for the update to be delivered to. If you have a device that you expect to receive the update, you can always update it's DNI.

Yes I totally get that. But why don't I get that error when I send the request from the pi with the original IP address? The IP address of the PI in its original state is 192.168.55.130 (c0.a8.37.7a)

If I search as you suggested no device with that address exists

Therefore if I send the request from this IP address. I should see the the "no matching device" error in the logs but I don't.

The PI reports a 200 response but there's absolutely nothing in the Hubitat logs.

I have a workaround by spoofing the PI's MAC address. If i do this then everything work as expected but I'm worried that this might manifest as another issue further down the line hence why I am trying to understand/eliminate it.

Thanks for your patience

Then you have a matching device DNI some where. Depending on how the driver is written and what logging level the hubitat device is set to, this is typically expected behavior.

IDK why you are using this as a work around or as a part of your troubleshooting, it seams like you maybe over complicating the situation.

This is not a layer 2 issue, you are not going to find much success if you keep treating it like it could be. I'm not sure if you or AI wrote the driver, but that is where i would recommend looking to resolve your concern.

Totally agree with you that this is not a Layer 2 issue but it does seem to me that the DNI has persisted somewhere in the database even though the device has been removed and this is supported by the fact that if I search by the DNI no device shows up.

Anyhow perhaps you right and I am over complicating things so heres my device driver code

import java.text.SimpleDateFormat
import groovy.transform.Field
import groovy.json.JsonSlurper

metadata {
definition (name: "Tony's Generic Heartbeat V2", namespace: "gashd1889", author: "Tony Leach") {
capability "Configuration"
capability "Switch"

    command "Recheck"
          
    attribute "Time", "number"
    attribute "Last Checked", "number"
    attribute "Server Heartbeat Recieved", "number"
    attribute "Server Difference", "number"
}

preferences {
    input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
    input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
    input("recheck", "number", title:"Time to Recheck", description:"Time in mins to run the check function", defaultValue:5)
    input("alertTime", "number", title:"Alert Time", description:"If no heartbeat is recieved within this time. Time in mins", defaultValue:5)
    input("ip", "text", title:"IP Address", description:"IP Address of system sending Heartbeat Signal", required: true)
    input("Server", "text", title:"ID In Request Body", description:"ID of system sending Heartbeat Signal", required: true)
}

}

void logsOff(){
log.warn "debug logging disabled..."
device.updateSetting("logEnable",[value:"false",type:"bool"])
}

void updated(){
if (logEnable) runIn(1800,logsOff)
def currentTimeMillis = now()
def iphex = convertIPtoHex(ip)
device.setDeviceNetworkId("${iphex}") // Set the device DNI based on IP set in preferences page
sendEvent(name: "Server Heartbeat Recieved", value: "${currentTimeMillis}", isStateChange: true)
sendEvent(name: "Last Checked", value: "${currentTimeMillis}", isStateChange: true)
sendEvent(name: "Server Difference", value: 0 , isStateChange: true)
}

void parse(String description) {
log.info ("we hit the parse")
log.debug "Received data: ${description}"
def result = parseLanMessage(description)
respData = parseJson(result.body)
def currentTimeMillis = now()
switch (respData.id) { // Only one case at this moment but will expand so I can have multiple heartbearts from the same IP using a parent/Child structure
case Server:
sendEvent(name: "Server Heartbeat Recieved", value: "${currentTimeMillis}", isStateChange: true)
log.debug "We have recieved data from ${Server}"
break
default :
log.warn "unable to set initial values for type:${type}"
break
}
Recheck()
}

List configure() {
log.warn "configure..."
runIn(1800,logsOff)
}

def Recheck () {
def delay = settings.recheck * 60
def currentTimeMillis = now()
sendEvent(name: "Last Checked", value: "${currentTimeMillis}", isStateChange: true)
def lastHeartbeat = device.currentValue("Server Heartbeat Recieved") as Long
def difference = (currentTimeMillis - lastHeartbeat) / 1000
//def difference = (currentTimeMillis - (device.currentValue("Server Heartbeat Recieved")))/1000 // difference in seconds
sendEvent(name: "Server Difference", value: "${difference}", isStateChange: true)
def alert = alertTime*60 // alertTime in mins so convert to secs
if (difference < alert) {
log.debug ("Switch on condition met")
on()
}
else {
log.debug ("Switch off condition met")
off()
}

runIn(delay,Recheck)

}

def installed(){
initialize()
}

def initialize(){
Recheck ()
}

private String convertIPtoHex(String ipAddress) {
return ipAddress.tokenize('.').collect { String.format('%02x', it.toInteger()) }.join().toUpperCase()
}
def on (){
sendEvent(name: "switch", value: "on" , isStateChange: true)
}

def off (){
sendEvent(name: "switch", value: "off" , isStateChange: true)
}

def convertor (string){
double dtimestamp = (Double.parseDouble(string)) // need to convert to double to get it into milliseconds
def long milliseconds = (dtimestamp * 1000) // finally convert into milliseconds
def date = new Date( milliseconds ).toString() // convert milliseconds into a date strin
return date
}

I have created 2 devices:

(1) using the wired IP adaptor IP address (192.168.55.130) - doesnt respond when I ping it from the PI

(2) using the wireless adaptor ip Address (192.168.55.140) - Works as expected when the PI sends a heartbeat request