DrMemCS / drmem

Full source tree for the DrMem control system
MIT License
3 stars 4 forks source link

Add time expressions to logic blocks #69

Closed rneswold closed 1 year ago

rneswold commented 1 year ago

Although DrMem can be used for control loops, another common use would be for home automation. This includes controlling devices based on time-of-day. To support this, we need to add time expressions to the logic block grammar.

Time-of-day Task

An async task will be created that sends the UTC and local time over a broadcast channel once a second. Logic blocks can receive these broadcasts to use in their expression(s).

Grammar Additions

These variables will be added to the logic block grammar:

All these variables are unsigned integers and can be used in expressions and comparisons.

The parser could recognize MONDAY as 0, TUESDAY as 1, etc. And it could do the same for month names, so the expressions are more readable.

Alternative

Another way this could be achieved is to have a built-in driver that provides time-of-day devices. This would be much simpler to implement. I find it a bit distasteful, however, because it'll be writing uninteresting time values to the storage layer. Maybe forcing the max_history to 1 would make it feel less distasteful.

In its defense, this would remove some complications with logic block support; we would have to conditionally add the time-of-day channel only if any expression used a time value.

rneswold commented 1 year ago

I'm starting to like the alternative.

The driver would provide seven devices: year, month, day, hour, minute, second, day-of-week. Its configuration would specify the timezone so you could have several instances; one for UTC and one for local time, for instance.

rneswold commented 1 year ago

Or, should we define a new device data type, Time. The time driver would only create one device, time. Then the logic block grammar could have operators/functions to extract the various pieces of information.

Or, the time driver provides the broken out values AND the combined.

So many choices.

beauremus commented 1 year ago

This seems like it could be a very commonly used driver. So, leaning toward the most general implementation would be my suggestion.

The Time driver providing a single time device with functions for manipulation seems very flexible. The device shouldn't ever have to change and adding functionally with new methods should avoid disruption.

beauremus commented 1 year ago

Possibly tangential question, do you see value in making a guarantee about determining or synchrony with this driver?

I imagine there are better ways to do this but I could imagine someone wanting to use this to trigger at some frequency. What precision will this provide?

Thinking about synchrony, if I need many things to happen at once, can I use this to tell them all to trigger at a time without delay? Imagine lights coming on but if they aren't synchronized then they come on in a wave.

beauremus commented 1 year ago

To argue against my own opinion 😁, I think the multiple device model for this driver is more transparent and possibly easier to get started using because of that.

rneswold commented 1 year ago

Possibly tangential question, do you see value in making a guarantee about determining or synchrony with this driver?

I imagine there are better ways to do this but I could imagine someone wanting to use this to trigger at some frequency. What precision will this provide?

This is a time-of-day value and it updates once a second. So, for timing at 1Hz or slower, this device could be used. If a driver needs to sample faster, it should use the tokio::interval timer in the driver.

There are a couple of drawbacks for using drivers for "utility" values (like time-of-day):

Thinking about synchrony, if I need many things to happen at once, can I use this to tell them all to trigger at a time without delay? Imagine lights coming on but if they aren't synchronized then they come on in a wave.

Yes. If you used a logic block to use this driver to control lights, for instance, they would all be changed at nearly the same time. As an example:

# Define a Wyze bulb (not an actual driver, at the moment!)

[[driver]]
name = "wyze"
path = "bulb-1"

# Define another Wyze bulb (still not an actual driver, at the moment!)

[[driver]]
name = "wyze"
path = "bulb-2"

# Proposed time driver which makes time-related devices.

[[driver]]
name = "time"
path = "local"
cfg = { tz = "cstcdt" }

# Turn both bulbs ON from 7pm to midnight. ('defs' is not part of
# logic blocks, yet.)

[[logic]]
inputs = { hour = "local:hour" }
outputs = { bulb_1 = "bulb-1:state", bulb_2 = "bulb-2:state" }
defs = { on_time = "{hour} >= 19" }
exprs = [ "{on_time} -> {bulb_1}",
          "{on_time} -> {bulb_2}" ]
rneswold commented 1 year ago

For now, making a driver seems to be the simplest solution. This feature will be tracked in #74.