Gpiozero: Energenie socket activation behaviour

Created on 10 May 2016  路  12Comments  路  Source: gpiozero/gpiozero

This issue concerns the Energenie class added in v1.2.0.

Let's say we're using an instance of this class to activate or deactivate a socket based on a condition, via a script which runs frequently on a schedule, using socket.on() / socket.off(). If the condition for activation is met and the socket in question is already on, the socket switches off then on in quick succession, with two clicks.

This is in contrast to the standalone Energenie module - when an already-activated socket is switched on with energenie.switch_on(1), it simply stays on. In cases where the script needs to run frequently, the unnecessary off/on cycles caused by this behaviour may reduce the longevity of the device.

I've tested this issue thoroughly with more than one physical Energenie socket. The problem does not happen in reverse (i.e. deactivating a socket which is off does not cause an on/off cycle, it just stays off). In case it's useful for anyone, the script which first alerted me to this behaviour is available here.

I suspect this issue stems from the fact that these sockets provide no means of reading their state, as mentioned in the documentation. Either way, the ideal outcome would be for activation of a socket which is already on to leave the state of the device unchanged.

Any input / suggestions much appreciated. Thanks.

question

Most helpful comment

The reason for this is related to the fact that the sockets provide no means of reading their state. In order to ensure that the state of the socket is "known" at construction time there's an initial_state value in the constructor which defaults to False (i.e. off) which is applied at construction time.

So, if you're running your script every time to set a state (as compared to a long-running script which uses the same object) you probably want to just set the initial_state (rather than calling on/off), e.g.

from gpiozero import Energenie
lamp = Energenie(1, initial_value=True)

All 12 comments

The reason for this is related to the fact that the sockets provide no means of reading their state. In order to ensure that the state of the socket is "known" at construction time there's an initial_state value in the constructor which defaults to False (i.e. off) which is applied at construction time.

So, if you're running your script every time to set a state (as compared to a long-running script which uses the same object) you probably want to just set the initial_state (rather than calling on/off), e.g.

from gpiozero import Energenie
lamp = Energenie(1, initial_value=True)

Incidentally, there is a later version of these sockets (rather more expensive) which do permit reading their state. We haven't got support for those (yet) though.

Thanks Dave, that makes perfect sense now.

Maybe I'm alone here, but this wasn't completely clear to me from the documentation. Perhaps it's worth spelling out that for scripts which run frequently (as opposed to long-running scripts), it's possible to just use the constructor alone, rather than calling on/off? This example provided doesn't make this abundantly clear:

lamp = Energenie(1)
lamp.on()

I reckon users might benefit from a little extra clarification in the docs, maybe under the description of the initial_value parameter, or even the general class description. Unless, of course, I'm the only one who somehow missed this point!

Doh - just realized I mentioned initial_state instead of initial_value in my comment -- oops!

So ... arguments against are that initial_value is actually present in most components, and defaults to False too (so if you've got an LED lit and you construct an LED component on top of that it'll go off by default), so Energenie isn't a special case in that sense (it's entirely typical of components in gpiozero).

However, in most other components, while initial_value defaults to False it is capable of taking the value None (meaning "leave the component in whatever state it's in now"). None is explicitly _not_ permitted with the Energenie class so we know what state it's in (because unlike other things we can't just read it when the script asks).

Furthermore, Energenie is likely to be a class where you want to set something and leave it in a given state when the script ends (with everything else we tend to run clean-up to shut down things at script end so that we don't leave motors running and what-not).

Yeah, I'm more or less convinced we should add a note to the API class (or at least an example snippet of code).

However, in most other components, while initial_value defaults to False it is capable of taking the value None (meaning "leave the component in whatever state it's in now"). None is explicitly _not_ permitted with the Energenie class

Actually, that's not quite true. Currently None will just get treated the same as False.
https://github.com/RPi-Distro/python-gpiozero/blob/master/gpiozero/boards.py#L860

I guess it shouldn't be too hard to change it so that using initial_value=None raises a ValueError?

Furthermore, Energenie is likely to be a class where you want to set something and leave it in a given state when the script ends

I wonder if it's worth adding another optional init-arg to specify whether the device should be returned to its initial_value during close() / shut-down?

Actually, that's not quite true. Currently None will just get treated the same as False.
https://github.com/RPi-Distro/python-gpiozero/blob/master/gpiozero/boards.py#L860

Doh - yes it should raise a ValueError. I must be thinking of something else where we don't permit None but I'm sure I meant to port that to Energenie - must've forgotten it. Anyway, that is something that ought to be corrected at the same time.

I wonder if it's worth adding another optional init-arg to specify whether the device should be returned to its initial_value during close() / shut-down?

Hmmm ... possibly. There are use-cases where in the case of unintentional script termination, you'd want the device to stay off. For example, one of the use-cases for Energenie in GPIO Zero which came up at picademy was a science teacher who wanted to use a temperature sensor to drive a mains-powered heating pad under some sea-monkeys (or some similar common organism; incidentally that's one of the conversations that triggered me thinking about hysteresis in the source tools module). It'd be a long-running script and obviously in the event of a script crash it's better to leave the heating pad off (and potentially freeze the critters) than leave it on (and potentially burn down the science lab :-). So, yes - there's potential for adding that too although I'd suggest it should be off by default (I get the impression Energenies are frequently used in transient scripts).

In that case, rather than having a boolean return_to_initial_value parameter defaulting to False, perhaps a better option would be a final_value parameter defaulting to None (indicating don't-change-value-on-shutdown)?
final_value is nicely 'symmetrical' with initial_value :)

Does Energenie's close() method return it to its original state? Because I imagine in this case, people would expect it to stay in the state they left it. My old web app example would depend on this behaviour.

Sorry, original state is not the right term in this case. I mean does it turn it off?

Just checked the source, and AFAICS closing an Energenie doesn't change its current state. If it's on, it'll stay on; if it's off, it'll stay off.

So I think the question part of this issue has been answered, but the idea that maybe devices should have the option not to cleanup pin state is still a valid suggestion.

How about:

device.close(cleanup=False)

where the default is cleanup=True.

Closing in favour of #707 which is a proposal for a general solution to the problem raised here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pcs3rd picture pcs3rd  路  6Comments

lurch picture lurch  路  5Comments

bootsmann picture bootsmann  路  8Comments

lurch picture lurch  路  12Comments

driesdepoorter picture driesdepoorter  路  9Comments