luup.watch_variable and unwatching

So http://bugs.micasaverde.com/view.php?id=1302 describes the defect of not being able to “unwatch” a variable, and Im looking into ways to work around this until it gets fixed properly. I came up with the following notion:

local watch_t = {}
local watch_id = 1

function watch_register(function_name, service, variable, device)
  local req = {}
  req.n = 5
  req["function_name"] = function_name
  req["service"] = service
  req["variable"] = variable
  req["device"] = device
  req["id"] = watch_id
  watch_t[req.id] = req
  watch_id = watch_id + 1
  return req.id
end

function watch_unregister(id)
  watch_t[id] = nil
end

function watch_callback(lul_device, lul_service, lul_variable, lul_value_old, lul_value_new)
  for id,req in pairs(watch_t) do
    if((not req.device   or (req.device   == lul_device)) and
       (not req.service  or (req.service  == lul_service)) and
       (not req.variable or (req.variable == lul_variable))) then
      req.function_name(lul_device, lul_service, lul_variable, lul_value_old, lul_value_new)
    end
  end
end

luup.variable_watch("watch_register", nil, nil, nil)

Ive not actually tested this yet, but the Lua is good. The biggest limitation I have is the device argument to watch_register must be a device, and cannot be a UDN (I suppose I can do a lookup, but thats more complex than I need for now). Basic usage is the same idea as luup.watch_variable, but instead of passing a string of the function name, you pass the function itself, and you are returned an id that can be used to unregister it at a later time.

A question of scope: if this gets put in the Lua Startup section, will plugins, etc be able to see the defined functions? If not, is there some global scoping I can use?

A question of threads: There does not seem to be a good way to put a semaphore/mutex in place to protect watch_id from simultaneous access with multiple threads. Assuming multiple plugins use this in their startup routines, is there any assurance they do not run all at once? Is there some better way to provide the mutex?

luup.variable_watch("watch_register", nil, nil, nil)

IIUC, you are trying luup-watching all variables for all services for all devices - but are you sure that the second parameter (service) and the last parameter (device) are allowed to be [tt]nil[/tt]?

When a variable is changed, your callback handler [tt]watch_register[/tt] gets called with the following parameters:

lul_device, lul_service, lul_variable, lul_value_old, lul_value_new 

… but these parameters don’t match the signature of your callback function:

function watch_register(function_name, service, variable, device)

Edit:
IMHO, you would have to replace

luup.variable_watch("watch_register", nil, nil, nil)

with

[code]luup.variable_watch("watch_callback", ... )

and to iterate the call to [tt]luup.variable_watch[/tt] over all devices and over all services, see the source code of the [tt]GDL[/tt] plugin for an example.

[quote=“Ap15e, post:2, topic:171088”]luup.variable_watch("watch_register", nil, nil, nil)

IIUC, you are trying luup-watching all variables for all services for all devices - but are you sure that the second parameter (service) and the last parameter (device) are allowed to be [tt]nil[/tt]?[/quote]

No, Im not sure. (As I said, untested at this point) If I cant register for everything like that, then Id make the watch_register function call luup.variable_watch for each thing it sees.

[quote=“Ap15e, post:2, topic:171088”]When a variable is changed, your callback handler [tt]watch_register[/tt] gets called with the following parameters:

lul_device, lul_service, lul_variable, lul_value_old, lul_value_new 

… but these parameters don’t match the signature of your callback function:

function watch_register(function_name, service, variable, device) [/quote]

Oops, minor typo, I meant to register the watch_callback function, thats what the actual callback goes to. So the logic would be:

  • global startup registers watch_callback for all variables/devices/services (if possible)
  • user calls register_watch for some combination of variables/devices/services
  • register_watch records function in global(?) table
    (register_watch might also need to watch_callback for the specific combination)
  • luup calls watch_callback when any variable changes
  • watch_callback searches for registered functions that match, calls that function
  • unregister_watch will remove the specified function from the global(?) table

My hope is to create a “generic” replacement for the luup.variable_watch that could be used by all plugins (code saving for me), but possibly even hint at some solutions that MCV might take up for an official fix.

Hi

I was just curious if this piece of work turned into anything?

I’d doing some research as id like to watch some variables so that they can be checked upon any change, so that depending on the result I can use them to act as a trigger.

E.g if a device last update is not within xxx seconds etc. then do xyz

Hi

Please can someone let me know if there is a work around to unwatching something?

I can see in the bug report that this is a minor issue and 2 years old =http://bugs.micasaverde.com/view.php?id=1302) But I was wondering if I wanted to submit a luup.variable_watch request to run just the once, is my only option to reboot to stop it looking for it to run again.

There’s no change in behaviour. A Luup restart is still needed. The best you can do is set a flag that you check in your watch callback. Flag not set? Do your code and then set the flag. Flag set? Skip the entire body of your function.