IP scan then do certain job and play MP3 only once
Question: I have the following code here. What I want to do is I want to script to scan
my ip address then do certain job.

My problem is the MP3 is always repeating every 30 seconds even if I did not
turn off my phone WIFI which is carrying the IP of the hostname.

I'd like the MP3 to only play once after the script found the IP but the scan
will continue scanning.

Is that possible? Any help will be very much appreciated

    
    
    #!/usr/bin/env python
    import os, sys
    import sched, time
    s = sched.scheduler(time.time, time.sleep)
    def do_something(sc):
            import os
            hostname = "192.168.254.102" #example
            response = os.system("ping -c 1 " + hostname)
            if response == 0:
                    print(hostname, 'is up!')
                    os.system("sudo python ../aquarium/nightlight_on.py 1")
                    os.system("omxplayer -o local ../music/jarvis_on.mp3 1")
            else:
                    print(hostname, 'is down!')
                    os.system("sudo python ../aquarium/nightlight_off.py 1")
            sc.enter(160, 1, do_something, (sc,))
    s.enter(30, 1, do_something, (s,))
    s.run()
    


Answer: If I understand what you're trying to do correctly, you want to run certain
behaviors when the availability of the IP adderss you're scanning for changes.
That is, you want to do one action when it becomes available (after having
been down) and a different action when it goes down (after having been
available). You don't want to do anything if it keeps doing the same thing it
was doing before.

This requires that you make your function access some kind of `state`
variable. A local variable won't work because you call the function repeatedly
from the scheduler (rather than writing your own loop). I think there are
three reasonable alternatives.

The simplest (from the perspective of how much your current code will need to
change) is to use a global variable. Something like this will work:

    
    
    import os, sys
    import sched, time
    
    state = None
    
    def do_something(sc):
            global state    # global statement is required since we write to the global state
            hostname = "192.168.254.102" #example
            response = os.system("ping -c 1 " + hostname)
            if response == 0 and state != "up":
                    state = "up"
                    print(hostname, 'is up!')
                    os.system("sudo python ../aquarium/nightlight_on.py 1")
                    os.system("omxplayer -o local ../music/jarvis_on.mp3 1")
            elif state != "down":
                    state = "down"
                    print(hostname, 'is down!')
                    os.system("sudo python ../aquarium/nightlight_off.py 1")
            sc.enter(160, 1, do_something, (sc,))
    
    s = sched.scheduler(time.time, time.sleep)
    s.enter(30, 1, do_something, (s,))
    s.run()
    

Global variables are often considered bad programming practice, since they
clutter up the global namespace, especially in a situation like this when you
won't be accessing that `state` variable from any other code. It's not really
a big deal for a small script like this, but it's a bad idea to do this in any
more complicated code.

The next alternative uses a closure to hold the state variable. A closure is
the namespace of an outer function that an inner function can access. To write
to a variable in a closure, you'll need to use a `nonlocal` statement, just
like the `global` statement in the previous version of the code (this is only
available in Python 3, which it looks like you're using):

    
    
    import os, sys
    import sched, time
    
    def do_something_factory():
        state = None  # state is a local variable in the factory function's namespace
    
        def do_something(sc):
            nonlocal state    # nonlocal statement lets us write to the outer scope
            hostname = "192.168.254.102" #example
            response = os.system("ping -c 1 " + hostname)
            if response == 0 and state != "up":
                    state = "up"
                    print(hostname, 'is up!')
                    os.system("sudo python ../aquarium/nightlight_on.py 1")
                    os.system("omxplayer -o local ../music/jarvis_on.mp3 1")
            elif state != "down":
                    state = "down"
                    print(hostname, 'is down!')
                    os.system("sudo python ../aquarium/nightlight_off.py 1")
            sc.enter(160, 1, do_something, (sc,))
        return do_something # return inner function to caller
    
    s = sched.scheduler(time.time, time.sleep)
    s.enter(30, 1, do_something_factory(), (s,))  # call factory function here!
    s.run()
    

The final approach is to use a `class` to contain the function (as a method).
The state will be stored in an instance variable, accessed via `self`. The
main script code will create the object and then pass the bound method to the
scheduler. You could probably move some or all of that code into the class if
you wanted to, but I'll leave the design more or less the same for now.

    
    
    import os, sys
    import sched, time
    
    class Doer(object):
        def __init__(self):
            self.state = None
    
        def do_something(self, sc):
            hostname = "192.168.254.102" #example
            response = os.system("ping -c 1 " + hostname)
            if response == 0 and self.state != "up":
                    self.state = "up"
                    print(hostname, 'is up!')
                    os.system("sudo python ../aquarium/nightlight_on.py 1")
                    os.system("omxplayer -o local ../music/jarvis_on.mp3 1")
            elif self.state != "down":
                    self.state = "down"
                    print(hostname, 'is down!')
                    os.system("sudo python ../aquarium/nightlight_off.py 1")
            sc.enter(160, 1, self.do_something, (sc,))
    
    s = sched.scheduler(time.time, time.sleep)
    d = Doer()
    s.enter(30, 1, d.do_something, (s,))
    s.run()
    

