Nice project.
As an exercise to regain memory, maybe you could try removing all the "OO" layering ?
I'm not sure how efficient the compiler does that, but if you don't define buttons and relays etc. as objects,
but simply as functions, it should spare memory space.
Or am I just an ignorant of modern C++ compilers ? (which I am
)
In your button function/object, you don't need to check twice for "if(digitalRead(_pin1) == LOW)" and you don't need to repeat debounce calls (should be const BTW), but that won't spare much. Probably moving all stuff into buttons.ino will bring a sensible memory gain.
My 2 cents, I don't program much either.