Does the HE Groovy support a FIFO. I see Java has queue and linked list which implements a FIFO.
LinkedList abc = []
abc.add('zyx')
abc.add('vut')
abc.add('abc')
abc.add('nop')
paragraph "$abc"
firstItem = abc.remove(0)
paragraph "$abc"
Thank you. Just wasnāt sure it was implemented in HEās Groovy.
OK, That works.
Now, how do I make the FIFO a state variable? I tried state.LinkedList = abc, and LinkedList state.abc = . Neither worked.
Or is it even possible?
Not where I can test at the moment, but IIRC state variables are stored as String. If so something like this might work:
state.abc = abc
...
LinkedList newAbc = state.abc.split(',')
The split just gives an error.
If I do
state.abc =
LinkedList newAbc = state.abc
newAbc.add("1")
newAbc.add("2")
I don't get any errors but then recall to do the remove gives me an o error that it doesn't exist. So basically it's not saving in a state variable.
No big deal. No hurry. I was just playing around with an idea. Got to leave so no more playing now.
If you save the state as a List, the state variable recognizes it as an ArrayList. When you retrieve, you cast it to List. List abc = (List)state.abc
I just use an array and keep it within a size parameter.
state.luxArray = [0,0,0,0,0]
// add to lux list
state.luxArray.push(lux)
if (state?.luxArray.size() > 5) state.luxArray.removeAt(0)
To jump in this fun, and maybe to learn something better. I found that state is slow and you can have consistency problems. Here is a dynamic solution I use from time to time to queue a Map.
// needed a queue to manage publish messages, older than 30 seconds get tossed.
@Field volatile static Map<Long,Map> qQueue = [:]
private List qGet() {
if(!qQueue[device.getIdAsLong()]) qQueue[device.getIdAsLong()] = []
return qQueue[device.getIdAsLong()]
}
void qPush(Map map) {
qGet().removeIf { now() > it?.ts + 30000 } //remove anything older than 30 seconds
map.ts = now() // Add timestamp
qGet() << map // Append map to the end of the list
}
void qClear() {
qGet().clear()
}
Map qPop() {
if(qGet().size() > 0) {
return qGet().remove(0) // Remove and return the first element
}
return null
}
Map qPeek() {
return qGet().isEmpty() ? null : qGet()[0]
}
Boolean qIsEmpty() {
return qGet().isEmpty()
}
Integer qSize() {
return qGet().size()
}
WOW! Not even sure I can understand that one. My programming abilities aren't that good... ![]()
I had a situation that I thought a FIFO might solve. But I think I got it working another way. Thanks everyone for the help. Gonna keep this info available in case I do need it.
Yeah, the trick to the queue is something I picked up from webCoRE and Echo Speaks after having problems with state.
@Field volatile static Map<Long,Map> qQueue = [:]
private List qGet() {
if(!qQueue[device.getIdAsLong()]) qQueue[device.getIdAsLong()] = []
return qQueue[device.getIdAsLong()]
}
- @Field is static so it makes
qQueuelive at the driver class level, not inside a single execution of the driver. So it sticks around between calls and schedules. - The key is
device.getIdAsLong(). That means each device instance gets its own entry in the map, so devices do not step on each other. qGet()is a helper that always returns āmy deviceās queueā. If the queue does not exist yet, it creates an empty list first, then returns it.volatilehelps make sure if different threads touch this (scheduled jobs, callbacks), they see the latest reference to the map rather than a stale copy.- to note, this complete data structure will be deleted upon hub reboot OR when you update the driver source code, so you need another way to hold perisitance if needed.
Glad you found a way past needing a FIFO.
So a 'state' variable keeps it's value between instances of an app running. Kinda like a global variable but only of use to a particular app. Your saying the '@Field' does basically the same thing but at a higher level so to speak.
One thing I really struggle with. I have been programming for 50 years or so. But the last 20 were kinda specialized so I didn't keep up with the latest languages. So understanding logic is no problem, understanding some of the commands or syntax has been a real learning experience. And my feeble brain doesn't learn as fast as it used to.
āstateā and in apps āatomicStateā are exactly as you say, they are specific to that device or app and maintain persistence via the Hubitat database. That call to the database can be stale across fast threading.
@Field lives in shared, non-persistent memory. That shared space is driver-level (not per device), so the trick is using the device ID as the key to keep the data specific to each device (or app). It isnāt stored anywhere permanent, and itāll get cleared on a hub reboot or any driver code change.
OH and yes, I am an āoldā C/C++ guy so this stuff is worlds different. I understand the struggle!!
I might explain what I was trying to accomplish.
I have several Matter Light Switches. One in particular seems to want to get hung up and I have to issue an Initialize command to get it back in sync.
The one that messes up is an outside light. Motion, or a door opening, at night turns it on. Then 5 minutes later it's set to go off. I would take the dog out at bedtime for his last, hopefully, wee-wee so he won't get me up in the middle of the night. After we come back in I make the coffee, etc. Then issue the Bedtime command which turns on the alarm, turns on some lights, turns off some other lights one of which is the outside light. Surprisingly enough a lot of times that happens at almost the exact time the 5 minute timer times out.
Investigating it I determined, or so it seemed, that when 2 commands get sent to the light almost simultaneously it causes it to hang. That light going off also triggered another light to go off. And that never happened. No big deal except the light ends up staying on all night if I don't catch it.
So my thought was to queue the commands and issue them a second or so apart. Thus the thought of using a FIFO.
Anyway, I ended up triggering the second light independently from the first one to solve that issue. Also decided I didn't need to tell the first light to go off at bedtime, just wait for the time to time out if it hand't already. Time will tell.
Sorry for the long explanation, but the crux of the issue is why multiple quick commands would cause a lock up???
You probably could of started this thread with that...![]()
Not sure if that is Matter over WiFi or Thread, but you can read on the forum that matter is another matter. I have several blubs and a couple of switches. They 'work' but when you have any inconsistency between what Hubitat thinks and the actual state of the bulb, you can have issues.
Not pushing it, but you might try this to track state and alert. Determine matter device status
here is an implementation of my arrays in state variables i use to keep track of monthly and daily snow depth in a driver
def initializeGlobals(force)
{
log.info "In init globals - force init = $force"
def gi = state.globalsInitialized
if ((gi != true) || (force == true))
{
log.info "Initializing globals for the first time"
state.globalDays = [:]
state.globalMonths = [:]
state.lastRawDepth = 0.00
state.dayDepth = 0.00
state.currentMonthDepth = 0.00
state.currentYearDepth = 0.00
state.rawDepth = 0.00
state.lastHourlyDepth = 0.00
if (useWh54ForSnowDepthCalculations)
{
sendEvent([name: 'snowHourly', value: 0.00, isStateChange: true])
sendEvent([name: 'snowDaily', value: 0.00, isStateChange: true])
sendEvent([name: 'snowMonthly', value: 0.00, isStateChange: true])
sendEvent([name: 'snowYearly', value: 0.00, isStateChange: true])
for (i in 1..31)
{
addToDays(i,0.0.toFloat())
}
for (j in 1..12)
{
addToMonths(j,0.0.toFloat())
}
} //use for snow depth
state.globalsInitialized = true
} // FIRST TIME
else
{
log.info "Globals already intialized"
}
// schedule daily and monthly jobs do this regardless of initialized or not in case they get removed.
if (useWh54ForSnowDepthCalculations)
{
unschedule("storeDailyDepth")
schedule("0 55 23 ? * * *", "storeDailyDepth")
unschedule("storeMonthlyDepth")
schedule("0 2 0 1 1-12 ? *","storeMonthlyDepth")
unschedule("storeHourlyDepth")
schedule(" 0 5 * ? * * *","storeHourlyDepth")
}
else
{
unschedule("storeDailyDepth")
unschedule("storeMonthlyDepth")
unschedule("storeHourlyDepth")
}
}
void addToDays(id, type)
{
//if (debugDepthStatisics) log.debug "in add to day list: [$id, $type]"
if (state.globalDays != null)
{
globalDays = state.globalDays
}
globalDays.put(id.toString(), type)
state.globalDays = globalDays
}
void listDays()
{
if (debugDepthStatisics) log.debug "In list days"
globalDays = state.globalDays
if (debugDepthStatisics) log.debug "days = $globalDays"
}
void addToMonths(id, type)
{
// if (debugDepthStatisics) log.debug "in add to device list: [$id, $type]"
if (state.globalMonths != null)
{
globalMonths = state.globalMonths
}
globalMonths.put(id.toString(), type)
state.globalMonths = globalMonths
}
void listMonths()
{
log.info "In list Months"
globalMonths = state.globalMonths
log.info "months = $globalMonths"
}
looks like this
this implementation does last over reboots etc.
