コード例 #1
0
def run_sender():
    """
    Request a reading every 30 seconds
    """

    global last_send_time

    sender_log = logging.data_logging()
    sender_log.log_filename = "shower_sender-" + todays_date_string() + ".csv"
    sender_log.auto_add_date = True
    sender_log.add_log("Starting Sender thread")

    while True:

        sender_log.log_filename = "shower_sender-" + todays_date_string() + ".csv"

        controllable = check_controller_state()['Controlled']  # Controlled is always a subset of monitored
        monitoring = check_controller_state()['Monitored']

        sender_log.add_log(controllable)

        #if controllable or monitoring:
        #    request_reading()

        time.sleep(reading_period)
        last_send_time = datetime.datetime.now()  # For the watchdog
コード例 #2
0
def run_check_pump_turn_on():
    """
    Wait until the time has elapsed, and send the on signal to the pump relay
    """

    global current_pump_state
    global next_on_time

    pump_on_log = logging.data_logging()
    pump_on_log.auto_add_date = True
    pump_on_log.log_filename = "shower_pump_control-" + todays_date_string() + ".csv"

    pump_on_log.add_log("Starting pump on monitor thread")

    while True:
        current_time = datetime.datetime.now()

        # pump_on_log.add_log(str(current_time) + "," + str(next_on_time) + "," + str(current_pump_state))

        if (current_time > next_on_time) and not current_pump_state:
            set_pump_state(True)
            pump_on_log.add_log("Turned pump on")
            sendmail.send("Pump turned back on", "")

        time.sleep(30)
コード例 #3
0
def run_sender():
    """
    Request a reading every 30 seconds
    """

    global last_send_time

    sender_log = logging.data_logging()
    sender_log.log_filename = "shower_sender-" + todays_date_string() + ".csv"
    sender_log.auto_add_date = True
    sender_log.add_log("Starting Sender thread")

    while True:

        sender_log.log_filename = "shower_sender-" + todays_date_string(
        ) + ".csv"

        controllable = check_controller_state()[
            'Controlled']  # Controlled is always a subset of monitored
        monitoring = check_controller_state()['Monitored']

        sender_log.add_log(controllable)

        #if controllable or monitoring:
        #    request_reading()

        time.sleep(reading_period)
        last_send_time = datetime.datetime.now()  # For the watchdog
コード例 #4
0
def run_check_pump_turn_on():
    """
    Wait until the time has elapsed, and send the on signal to the pump relay
    """

    global current_pump_state
    global next_on_time

    pump_on_log = logging.data_logging()
    pump_on_log.auto_add_date = True
    pump_on_log.log_filename = "shower_pump_control-" + todays_date_string(
    ) + ".csv"

    pump_on_log.add_log("Starting pump on monitor thread")

    while True:
        current_time = datetime.datetime.now()

        # pump_on_log.add_log(str(current_time) + "," + str(next_on_time) + "," + str(current_pump_state))

        if (current_time > next_on_time) and not current_pump_state:
            set_pump_state(True)
            pump_on_log.add_log("Turned pump on")
            sendmail.send("Pump turned back on", "")

        time.sleep(30)
コード例 #5
0
def set_pump_state(setting):
    """
    Turn the shower pump on or off
    """

    pump_log = logging.data_logging()
    pump_log.auto_add_date = True
    pump_log.log_filename = "shower_pump-" + todays_date_string() + ".csv"

    global current_pump_state
    global transaction_number

    log_message = "pump off"
    switch_message = str(transaction_number) + ',' + RELAY_OFF_MESSAGE

    if setting:
        log_message = "pump on"
        switch_message = str(transaction_number) + ',' + RELAY_ON_MESSAGE

    print('Switch message ' + switch_message)

    try:
        send_socket.sendto(switch_message, LW_IP_ADDRESS)
        current_pump_state = setting
        print("Set pump to " + str(current_pump_state))

        # Should get a reply with the transaction number

        transaction_number += 1
        f = open('logs/transaction.txt', 'w')
        f.write(str(transaction_number) + '\n')
        f.close()

        # Send again, sometimes the signal doesn't get through
        time.sleep(3)
        send_socket.sendto(switch_message, LW_IP_ADDRESS)

        # Send again, sometimes the signal doesn't get through
        #time.sleep(3)
        #send_socket.sendto(switch_message, LW_IP_ADDRESS)

    except Exception as err:
        sendmail.send("Exception LightwaveRF UDP Send pump switch", str(err))

    pump_log.add_log("Turned " + log_message)
コード例 #6
0
def set_pump_state(setting):
    """
    Turn the shower pump on or off
    """

    pump_log = logging.data_logging()
    pump_log.auto_add_date = True
    pump_log.log_filename = "shower_pump-" + todays_date_string() + ".csv"

    global current_pump_state
    global transaction_number

    log_message = "pump off"
    switch_message = str(transaction_number) + ',' + RELAY_OFF_MESSAGE

    if setting:
        log_message = "pump on"
        switch_message = str(transaction_number) + ',' + RELAY_ON_MESSAGE

    print('Switch message ' + switch_message)

    try:
        send_socket.sendto(switch_message, LW_IP_ADDRESS)
        current_pump_state = setting
        print("Set pump to " + str(current_pump_state))

        # Should get a reply with the transaction number

        transaction_number += 1
        f = open('logs/transaction.txt', 'w')
        f.write(str(transaction_number) + '\n')
        f.close()

        # Send again, sometimes the signal doesn't get through
        time.sleep(3)
        send_socket.sendto(switch_message, LW_IP_ADDRESS)

        # Send again, sometimes the signal doesn't get through
        #time.sleep(3)
        #send_socket.sendto(switch_message, LW_IP_ADDRESS)

    except Exception as err:
        sendmail.send("Exception LightwaveRF UDP Send pump switch", str(err))

    pump_log.add_log("Turned " + log_message)
コード例 #7
0
def run_listener():
    """
    The main program to listen for sockets data
    """

    global current_pump_state
    global next_on_time
    global max_on_time
    global last_receive_time
    global accumulated_on_time
    global break_count
    global reset_period
    global do_electricity_check
    global electricity_check_time

    listener_log = logging.data_logging()
    listener_log.auto_add_date = True
    listener_log.add_log("Starting Shower Listener")

    debug_log = logging.data_logging()
    debug_log.auto_add_date = True

    trace_log = logging.data_logging()
    trace_log.auto_add_date = True

    # The new protocol is that the wifi link sends the energy reading every 15 seconds. It is not necessary
    # to request a reading
    receive_socket.bind(('0.0.0.0', UDP_LISTEN_PORT))
    receive_socket.settimeout(20)

    shower_state = False
    start_time = datetime.datetime.now()

    current_pump_state = False

    accumulated_on_time = 0  # Total number of seconds the shower is on for
    break_count = 0  # Keep track of break periods, if the shower is turned off

    reading_time = datetime.datetime.now()

    sent_old_message = False                # Old format reading - something is wrong
    sent_no_use_message = False             # no usage today

    last_string = ''

    data = ''

    while True:
        try:
            listener_log.log_filename = "shower_listener-" + todays_date_string() + ".csv"
            debug_log.log_filename = "pump_state-" + todays_date_string() + '.csv'
            trace_log.log_filename = 'data_trace-' + todays_date_string() + '.csv'

            # A socket is only received when the sender requests one

            listener_log.log_filename = "shower_listener-" + todays_date_string() + ".csv"
            data, address = receive_socket.recvfrom(1024)  # Parameter is buffer size

            print(data)

            got_data = False

            current_time = datetime.datetime.now()

            if current_time.hour == 0:
                sent_no_use_message = False
                sent_old_message = False            # Ensure sent every day until fixed

            if str(data).find('*!') >= 0:
                # Complex format
                got_data = True

                # The data contains the *! string before the main data.
                # We also get replies from the device such as 100,OK
                print('Received: ')
                print(data)

                #string format:
                # *!{"trans":23368,"mac":"03:34:7A","time":1415084916,"prod":"pwrMtr","serial":"8A20FE","signal":79,"type":"energy","cUse":0,"maxUse":342,"todUse":22,"yesUse":81}

                #region data parsing

                last_string = "Receive: " + data + " end"

                #new format is a binary representation of a dictionary
                #Convert from binary
                val = data.decode()
                val = val.replace('*!', '')  #replace the header

                d = ast.literal_eval(val)  #get the data as a dictionary

                out_string = current_time.strftime("%d-%m-%Y %h:%M%s") + "," + data
                print(out_string)

                # With the current firmware (2.91Y), once an hour the hub itself will broadcast its details:
                # Data: *!{"trans":51877,"mac":"03:34:7A","time":1442299077,"type":"hub","prod":"wfl","fw":"U2.91Y","uptime":781402,"timeZone":0,"lat":0.00,"long":-1.86,"duskTime":1442340358,"dawnTime":1442296750,"tmrs":1,"evns":0,"run":0,"macs":9,"ip":"192.168.1.40","devs":1}

                # There is no current usage in this as it is from the hub rather than the energy monitor

                current_power_reading = 0

                try:
                    current_power_reading = d['cUse']

                    today_use = d['todUse']
                    if current_time.hour == 23 and int(today_use) == 0 and not sent_no_use_message:
                        sendmail.send('Shower controller problem', 'No apparent usage today, check it out')
                        sent_no_use_message = True

                except KeyError as e:
                    got_data = False

                last_string = "split_measurement: " + str(current_power_reading) + " end"

                #endregion

            elif str(data).find('=') > 0:
                # Simple format - 123,?W=0,420,32,100;
                trace_log.add_log('Old format data:' + str(data))

                out_string = data.split('=')

                if len(out_string) > 1:
                    out_string = out_string[1].split(',')
                    trace_log.add_log('Split data reading: ' + str(out_string[0]))
                    current_power_reading = float(out_string[0])
                    got_data = True

                if not sent_old_message:
                    # sendmail.send('Shower problem', 'Getting old format readings from lightwave. Check batteries or link.\n\nIf link is not showing any readings, then the comms is broken.')
                    sent_old_message = True

            # Turns out the wifi will send out a packet every 15 seconds, but sends 2 in succession. This is due to a bradcast and a unicast
            #if str(data).find('*!') != -1:

            if got_data:
                current_time = datetime.datetime.now()  # Time the reading was taken
                last_receive_time = current_time  # For the watchdog

                time_since_last_reading = (current_time - reading_time).seconds
                reading_time = current_time

                print (current_time),
                print('time since last reading {0}'.format(time_since_last_reading))

                print("Current power " + str(current_power_reading))

                uploadCOSM.SendToCOSM(str(current_power_reading), "YcfzZVxtMMRdD-d_GIgNinJ1x_qBh963fcORnVu_dMQ",
                                      "41189",
                                      "0002")

                listener_log.add_log(str(current_power_reading) + "," + str(max_on_time) + "," + str(no_repeat_period))

                if check_controller_state()['Controlled']:
                    # Controlled, not just monitored
                    if current_power_reading > 30:
                        #Shower is on

                        # The shower pump when on is 340 - 342 watts. If we have a partial period where the shower was turned on
                        # or off part way through the period, then we can use this to proportion the time based on
                        # the energy in that period

                        debug_log.add_log(str(current_power_reading))

                        time_ratio = current_power_reading / 340.0  # force float

                        if time_ratio > 0.95:
                            time_ratio = 1

                        actual_seconds = time_since_last_reading * time_ratio

                        accumulated_on_time += actual_seconds
                        break_count = 0

                        print("Current power: " + str(current_power_reading))
                        current_pump_state = True  # Just in case overridden the setting manually

                        if not shower_state:
                            #Just turned it on
                            #sendmail.send("Shower on", "")
                            # Don't use time now, as there has been some elapsed time since getting current_time
                            start_time = current_time
                            listener_log.add_log("Pump On")

                        shower_state = True

                        pump_on_period = (datetime.datetime.now() - start_time)

                        log_data = 'On Period,{0},accumulated,{1:.1f}'.format(pump_on_period.seconds,
                                                                              accumulated_on_time)
                        listener_log.add_log(log_data)

                        if accumulated_on_time > max_on_time:
                            #if pump_on_period.seconds > max_on_time:
                            sendmail.send("Shower controller",
                                          "Turning pump off. On for accumulated {0:.1f}, Max time {1}".format(
                                              accumulated_on_time, max_on_time))

                            set_pump_state(False)

                            # Tuesday to friday don't turn on until 1800
                            #if 1 <= current_time.weekday() <= 4 and current_time.hour < 17:
                            #    new_on_time = current_time.replace(hour=18, minute=0, second=0)
                            #    next_on_time = new_on_time
                            #else:
                            next_on_time = current_time + timedelta(seconds=no_repeat_period)

                            sendmail.send("Shower controller next on", str(next_on_time))
                            listener_log.add_log("Killing pump")

                    else:  # energy < 30
                        if shower_state:
                            # Was on, now gone to off
                            #time_on = (datetime.datetime.now() - start_time)
                            sendmail.send("Shower off", "Number seconds on: {0:.1f}".format(accumulated_on_time))  # time_on.seconds))
                            listener_log.add_log(
                                "Pump turned off,{0:.1f}".format(accumulated_on_time))  # time_on.seconds))
                            electricity_check_time = current_time + timedelta(minutes=10)
                            do_electricity_check = True

                        shower_state = False
                        break_count += time_since_last_reading

                    #listener_log.add_log('Break {0:.1f},Accumulated {1:.1f}'.format(break_count, accumulated_on_time))
                    #print('Break count ' + str(break_count))
                    #print('Accumulated ' + str(accumulated_on_time))

                    if break_count > reset_period and accumulated_on_time > 0:
                        print('Resetting break count')
                        accumulated_on_time = 0
                        break_count = 0

                        if current_pump_state:
                            #sendmail.send("Shower controller Reset break", "")
                            pass

                else:  # not controlled
                    accumulated_on_time = 0
                    break_count = 0

        except Exception as listener_exception:
            message = "Exception details:\n"
            message += "Last string: " + last_string
            message += "\n"
            message += "Data: "
            message += data
            message += str(type(listener_exception))
            message += str(listener_exception.args)
            message += str(listener_exception)
            message += "\n\n"
            message += str(traceback.print_exc())

            # sendmail.send("Shower control - Exception in listener thread", message)

            f = open ('/home/pi/scripts/shower/logs/exceptions.txt', 'a')
            f.write (str(datetime.datetime.now()) + message + '\n')
            f.close()
コード例 #8
0
def run_watchdog():
    """
    Monitor the other threads to make sure they are still running

    No longer in use

    """

    global last_send_time
    global last_receive_time

    watchdog_log = logging.data_logging()
    watchdog_log.log_filename = "shower_watchdog-" + todays_date_string() + ".csv"
    watchdog_log.auto_add_date = True
    watchdog_log.add_log("Starting shower watchdog")

    missed_count = 0

    next_check_time = datetime.datetime.now()

    while True:
        watchdog_log.log_filename = "shower_watchdog-" + todays_date_string() + ".csv"

        if datetime.datetime.now() > next_check_time:
            if check_controller_state()['Monitored']:
                reading_request_age = datetime.datetime.now() - last_send_time

                if reading_request_age.seconds > 120:
                    print("Sender has stopped")
                    sendmail.send("Shower monitor problem", "Sender has stopped sending")

                # and check the last time the listener responded

                # Allow a short delay for the listener socket to receive the data

                time.sleep(3)

                if last_receive_time < last_send_time:
                    # Receive time is before send time, so the data has not been received

                    # The occasional packet will get missed, so give a little tolerance to avoid too many emails

                    missed_count += 1

                    if missed_count > 1:
                        print("Listener has stopped")
                        data = "Listener has stopped listening:\n\n"
                        data += "Sender last time " + str(last_send_time) + "\n"
                        data += "Listener last time " + str(last_receive_time)

                        sendmail.send("Shower monitor problem", data)

                        next_check_time = datetime.datetime.now() + datetime.timedelta(0, 0, 0, 0,
                                                                                       30)  # Try again in 30 minutes
                else:
                    missed_count = 0

        # Send a message to the other Pi

        try:
            send_socket.sendto("Hello", PI_IP_ADDRESS)
        except Exception as err:
            sendmail.send("Exception LightwaveRF Watchdog Send", str(err))

        time.sleep(15)
        watchdog_log.add_log("Watchdog checked," + str(check_controller_state()))
コード例 #9
0
def check_controller_state():
    """
    See if we are in a controllable period
    """

    global max_on_time
    global no_repeat_period

    controller_log = logging.data_logging()
    controller_log.log_filename = "controller-times.csv"
    controller_log.auto_add_date = True

    current_time = datetime.datetime.now()

    controllable = False

    # If the pump is not controlled, it should always be monitored, so that the readings can be uploaded to Xively
    # The monitor runs between 0600 and 2300 every day

    monitoring = False

    if 6 <= current_time.hour < 23:
        monitoring = True

    # Special file which is available to control from a web page

    override_filename = 'home/pi/scripts/shower/shower-override.txt'
    # override_filename = 'shower-override.txt'
    special_override = False

    print('checking pump state\n')
    print('isfile '),
    print(os.path.isfile(override_filename))

    if os.path.isfile(override_filename):
        override_file = open(override_filename, 'r')
        override_data = override_file.readline()
        override_file.close()

        print('Override file contents ' + override_data)

        if override_data.startswith('On'):
            #Override is set on, doesn't matter about the rest
            controllable = False
            special_override = True
            print('override set by web')
        else:
            print('override not set by web')
    else:
        print('no override file')

    if not special_override:
        # Check the days of the week and times for enabled

        # Read the config file to get the settings

        config_file = open('/home/pi/scripts/shower/shower-times.csv', 'r')
        # config_file = open('shower-times.csv', 'r')
        config_data = config_file.readlines()

        line_count = 0

        break_loop = False

        on_time = "05:30"  # Need this declared, as it is used in a different iteration of the loop

        for line in config_data:
            if break_loop:
                break

            line_data = line.split(",")
            line_count += 1

            try:
                setting = line_data[0].lower().strip()
                value = line_data[1].lower().strip()

                if setting == "override" and value == "on":
                    controllable = False
                    controller_log.add_log("Override is on")
                    break_loop = True
                    # If override is on, doesn't matter what the rest of the settings are
                    break

                if setting == "max_on_time":
                    max_on_time = int(value)
                    continue

                if setting == "no_repeat_period":
                    no_repeat_period = int(value)
                    continue

                if setting == 'reset_period':
                    reset_period = int(value) * 60
                    continue

                time_setting = line_data[2].strip()

                # Find the day setting which is today

                weekday_name = current_time.strftime("%A").lower()

                if weekday_name == setting:
                    # There can be more than 1 time zone setting in a day, so do a compare after reading each off time
                    # controller_log.add_log(weekday_name + "," + value + "," + time_setting)
                    if value == "on":
                        on_time = time_setting
                    if value == "off":
                        off_time = time_setting
                        # See if the current time is between the last on and off time

                        start_monitor_time = on_time.split(":")
                        end_monitor_time = off_time.split(":")

                        # Check the end time first
                        if current_time.hour > int(end_monitor_time[0]):
                            # Definitely not in the zone
                            # But there could be more time periods for today

                            controllable = False
                            continue

                        if current_time.hour == int(end_monitor_time[0]) and current_time.minute >= int(
                                end_monitor_time[1]):
                            controllable = False
                            continue

                        # Now the start time

                        if current_time.hour < int(start_monitor_time[0]):
                            controllable = False
                            continue

                        if current_time.hour == int(start_monitor_time[0]) and current_time.minute < int(
                                start_monitor_time[1]):
                            controllable = False
                            continue

                        # It must be within the time if it gets here

                        controllable = True
                        break_loop = True
                        break

            except Exception:
                pass  # Every parse of the file will throw an exception as the fields are not consistent

        config_file.close()

    return {'Controlled': controllable, 'Monitored': monitoring}
コード例 #10
0
def run_listener():
    """
    The main program to listen for sockets data
    """

    global current_pump_state
    global next_on_time
    global max_on_time
    global last_receive_time
    global accumulated_on_time
    global break_count
    global reset_period
    global do_electricity_check
    global electricity_check_time

    listener_log = logging.data_logging()
    listener_log.auto_add_date = True
    listener_log.add_log("Starting Shower Listener")

    debug_log = logging.data_logging()
    debug_log.auto_add_date = True

    trace_log = logging.data_logging()
    trace_log.auto_add_date = True

    # The new protocol is that the wifi link sends the energy reading every 15 seconds. It is not necessary
    # to request a reading
    receive_socket.bind(('0.0.0.0', UDP_LISTEN_PORT))
    receive_socket.settimeout(20)

    shower_state = False
    start_time = datetime.datetime.now()

    current_pump_state = False

    accumulated_on_time = 0  # Total number of seconds the shower is on for
    break_count = 0  # Keep track of break periods, if the shower is turned off

    reading_time = datetime.datetime.now()

    sent_old_message = False  # Old format reading - something is wrong
    sent_no_use_message = False  # no usage today

    last_string = ''

    data = ''

    while True:
        try:
            listener_log.log_filename = "shower_listener-" + todays_date_string(
            ) + ".csv"
            debug_log.log_filename = "pump_state-" + todays_date_string(
            ) + '.csv'
            trace_log.log_filename = 'data_trace-' + todays_date_string(
            ) + '.csv'

            # A socket is only received when the sender requests one

            listener_log.log_filename = "shower_listener-" + todays_date_string(
            ) + ".csv"
            data, address = receive_socket.recvfrom(
                1024)  # Parameter is buffer size

            print(data)

            got_data = False

            current_time = datetime.datetime.now()

            if current_time.hour == 0:
                sent_no_use_message = False
                sent_old_message = False  # Ensure sent every day until fixed

            if str(data).find('*!') >= 0:
                # Complex format
                got_data = True

                # The data contains the *! string before the main data.
                # We also get replies from the device such as 100,OK
                print('Received: ')
                print(data)

                #string format:
                # *!{"trans":23368,"mac":"03:34:7A","time":1415084916,"prod":"pwrMtr","serial":"8A20FE","signal":79,"type":"energy","cUse":0,"maxUse":342,"todUse":22,"yesUse":81}

                #region data parsing

                last_string = "Receive: " + data + " end"

                #new format is a binary representation of a dictionary
                #Convert from binary
                val = data.decode()
                val = val.replace('*!', '')  #replace the header

                d = ast.literal_eval(val)  #get the data as a dictionary

                out_string = current_time.strftime(
                    "%d-%m-%Y %h:%M%s") + "," + data
                print(out_string)

                # With the current firmware (2.91Y), once an hour the hub itself will broadcast its details:
                # Data: *!{"trans":51877,"mac":"03:34:7A","time":1442299077,"type":"hub","prod":"wfl","fw":"U2.91Y","uptime":781402,"timeZone":0,"lat":0.00,"long":-1.86,"duskTime":1442340358,"dawnTime":1442296750,"tmrs":1,"evns":0,"run":0,"macs":9,"ip":"192.168.1.40","devs":1}

                # There is no current usage in this as it is from the hub rather than the energy monitor

                current_power_reading = 0

                try:
                    current_power_reading = d['cUse']

                    today_use = d['todUse']
                    if current_time.hour == 23 and int(
                            today_use) == 0 and not sent_no_use_message:
                        sendmail.send('Shower controller problem',
                                      'No apparent usage today, check it out')
                        sent_no_use_message = True

                except KeyError as e:
                    got_data = False

                last_string = "split_measurement: " + str(
                    current_power_reading) + " end"

                #endregion

            elif str(data).find('=') > 0:
                # Simple format - 123,?W=0,420,32,100;
                trace_log.add_log('Old format data:' + str(data))

                out_string = data.split('=')

                if len(out_string) > 1:
                    out_string = out_string[1].split(',')
                    trace_log.add_log('Split data reading: ' +
                                      str(out_string[0]))
                    current_power_reading = float(out_string[0])
                    got_data = True

                if not sent_old_message:
                    # sendmail.send('Shower problem', 'Getting old format readings from lightwave. Check batteries or link.\n\nIf link is not showing any readings, then the comms is broken.')
                    sent_old_message = True

            # Turns out the wifi will send out a packet every 15 seconds, but sends 2 in succession. This is due to a bradcast and a unicast
            #if str(data).find('*!') != -1:

            if got_data:
                current_time = datetime.datetime.now(
                )  # Time the reading was taken
                last_receive_time = current_time  # For the watchdog

                time_since_last_reading = (current_time - reading_time).seconds
                reading_time = current_time

                print(current_time),
                print('time since last reading {0}'.format(
                    time_since_last_reading))

                print("Current power " + str(current_power_reading))

                uploadCOSM.SendToCOSM(
                    str(current_power_reading),
                    "YcfzZVxtMMRdD-d_GIgNinJ1x_qBh963fcORnVu_dMQ", "41189",
                    "0002")

                listener_log.add_log(
                    str(current_power_reading) + "," + str(max_on_time) + "," +
                    str(no_repeat_period))

                if check_controller_state()['Controlled']:
                    # Controlled, not just monitored
                    if current_power_reading > 30:
                        #Shower is on

                        # The shower pump when on is 340 - 342 watts. If we have a partial period where the shower was turned on
                        # or off part way through the period, then we can use this to proportion the time based on
                        # the energy in that period

                        debug_log.add_log(str(current_power_reading))

                        time_ratio = current_power_reading / 340.0  # force float

                        if time_ratio > 0.95:
                            time_ratio = 1

                        actual_seconds = time_since_last_reading * time_ratio

                        accumulated_on_time += actual_seconds
                        break_count = 0

                        print("Current power: " + str(current_power_reading))
                        current_pump_state = True  # Just in case overridden the setting manually

                        if not shower_state:
                            #Just turned it on
                            #sendmail.send("Shower on", "")
                            # Don't use time now, as there has been some elapsed time since getting current_time
                            start_time = current_time
                            listener_log.add_log("Pump On")

                        shower_state = True

                        pump_on_period = (datetime.datetime.now() - start_time)

                        log_data = 'On Period,{0},accumulated,{1:.1f}'.format(
                            pump_on_period.seconds, accumulated_on_time)
                        listener_log.add_log(log_data)

                        if accumulated_on_time > max_on_time:
                            #if pump_on_period.seconds > max_on_time:
                            sendmail.send(
                                "Shower controller",
                                "Turning pump off. On for accumulated {0:.1f}, Max time {1}"
                                .format(accumulated_on_time, max_on_time))

                            set_pump_state(False)

                            # Tuesday to friday don't turn on until 1800
                            #if 1 <= current_time.weekday() <= 4 and current_time.hour < 17:
                            #    new_on_time = current_time.replace(hour=18, minute=0, second=0)
                            #    next_on_time = new_on_time
                            #else:
                            next_on_time = current_time + timedelta(
                                seconds=no_repeat_period)

                            sendmail.send("Shower controller next on",
                                          str(next_on_time))
                            listener_log.add_log("Killing pump")

                    else:  # energy < 30
                        if shower_state:
                            # Was on, now gone to off
                            #time_on = (datetime.datetime.now() - start_time)
                            sendmail.send(
                                "Shower off",
                                "Number seconds on: {0:.1f}".format(
                                    accumulated_on_time))  # time_on.seconds))
                            listener_log.add_log(
                                "Pump turned off,{0:.1f}".format(
                                    accumulated_on_time))  # time_on.seconds))
                            electricity_check_time = current_time + timedelta(
                                minutes=10)
                            do_electricity_check = True

                        shower_state = False
                        break_count += time_since_last_reading

                    #listener_log.add_log('Break {0:.1f},Accumulated {1:.1f}'.format(break_count, accumulated_on_time))
                    #print('Break count ' + str(break_count))
                    #print('Accumulated ' + str(accumulated_on_time))

                    if break_count > reset_period and accumulated_on_time > 0:
                        print('Resetting break count')
                        accumulated_on_time = 0
                        break_count = 0

                        if current_pump_state:
                            #sendmail.send("Shower controller Reset break", "")
                            pass

                else:  # not controlled
                    accumulated_on_time = 0
                    break_count = 0

        except Exception as listener_exception:
            message = "Exception details:\n"
            message += "Last string: " + last_string
            message += "\n"
            message += "Data: "
            message += data
            message += str(type(listener_exception))
            message += str(listener_exception.args)
            message += str(listener_exception)
            message += "\n\n"
            message += str(traceback.print_exc())

            # sendmail.send("Shower control - Exception in listener thread", message)

            f = open('/home/pi/scripts/shower/logs/exceptions.txt', 'a')
            f.write(str(datetime.datetime.now()) + message + '\n')
            f.close()
コード例 #11
0
def run_watchdog():
    """
    Monitor the other threads to make sure they are still running

    No longer in use

    """

    global last_send_time
    global last_receive_time

    watchdog_log = logging.data_logging()
    watchdog_log.log_filename = "shower_watchdog-" + todays_date_string(
    ) + ".csv"
    watchdog_log.auto_add_date = True
    watchdog_log.add_log("Starting shower watchdog")

    missed_count = 0

    next_check_time = datetime.datetime.now()

    while True:
        watchdog_log.log_filename = "shower_watchdog-" + todays_date_string(
        ) + ".csv"

        if datetime.datetime.now() > next_check_time:
            if check_controller_state()['Monitored']:
                reading_request_age = datetime.datetime.now() - last_send_time

                if reading_request_age.seconds > 120:
                    print("Sender has stopped")
                    sendmail.send("Shower monitor problem",
                                  "Sender has stopped sending")

                # and check the last time the listener responded

                # Allow a short delay for the listener socket to receive the data

                time.sleep(3)

                if last_receive_time < last_send_time:
                    # Receive time is before send time, so the data has not been received

                    # The occasional packet will get missed, so give a little tolerance to avoid too many emails

                    missed_count += 1

                    if missed_count > 1:
                        print("Listener has stopped")
                        data = "Listener has stopped listening:\n\n"
                        data += "Sender last time " + str(
                            last_send_time) + "\n"
                        data += "Listener last time " + str(last_receive_time)

                        sendmail.send("Shower monitor problem", data)

                        next_check_time = datetime.datetime.now(
                        ) + datetime.timedelta(0, 0, 0, 0,
                                               30)  # Try again in 30 minutes
                else:
                    missed_count = 0

        # Send a message to the other Pi

        try:
            send_socket.sendto("Hello", PI_IP_ADDRESS)
        except Exception as err:
            sendmail.send("Exception LightwaveRF Watchdog Send", str(err))

        time.sleep(15)
        watchdog_log.add_log("Watchdog checked," +
                             str(check_controller_state()))
コード例 #12
0
def check_controller_state():
    """
    See if we are in a controllable period
    """

    global max_on_time
    global no_repeat_period

    controller_log = logging.data_logging()
    controller_log.log_filename = "controller-times.csv"
    controller_log.auto_add_date = True

    current_time = datetime.datetime.now()

    controllable = False

    # If the pump is not controlled, it should always be monitored, so that the readings can be uploaded to Xively
    # The monitor runs between 0600 and 2300 every day

    monitoring = False

    if 6 <= current_time.hour < 23:
        monitoring = True

    # Special file which is available to control from a web page

    override_filename = 'home/pi/scripts/shower/shower-override.txt'
    # override_filename = 'shower-override.txt'
    special_override = False

    print('checking pump state\n')
    print('isfile '),
    print(os.path.isfile(override_filename))

    if os.path.isfile(override_filename):
        override_file = open(override_filename, 'r')
        override_data = override_file.readline()
        override_file.close()

        print('Override file contents ' + override_data)

        if override_data.startswith('On'):
            #Override is set on, doesn't matter about the rest
            controllable = False
            special_override = True
            print('override set by web')
        else:
            print('override not set by web')
    else:
        print('no override file')

    if not special_override:
        # Check the days of the week and times for enabled

        # Read the config file to get the settings

        config_file = open('/home/pi/scripts/shower/shower-times.csv', 'r')
        # config_file = open('shower-times.csv', 'r')
        config_data = config_file.readlines()

        line_count = 0

        break_loop = False

        on_time = "05:30"  # Need this declared, as it is used in a different iteration of the loop

        for line in config_data:
            if break_loop:
                break

            line_data = line.split(",")
            line_count += 1

            try:
                setting = line_data[0].lower().strip()
                value = line_data[1].lower().strip()

                if setting == "override" and value == "on":
                    controllable = False
                    controller_log.add_log("Override is on")
                    break_loop = True
                    # If override is on, doesn't matter what the rest of the settings are
                    break

                if setting == "max_on_time":
                    max_on_time = int(value)
                    continue

                if setting == "no_repeat_period":
                    no_repeat_period = int(value)
                    continue

                if setting == 'reset_period':
                    reset_period = int(value) * 60
                    continue

                time_setting = line_data[2].strip()

                # Find the day setting which is today

                weekday_name = current_time.strftime("%A").lower()

                if weekday_name == setting:
                    # There can be more than 1 time zone setting in a day, so do a compare after reading each off time
                    # controller_log.add_log(weekday_name + "," + value + "," + time_setting)
                    if value == "on":
                        on_time = time_setting
                    if value == "off":
                        off_time = time_setting
                        # See if the current time is between the last on and off time

                        start_monitor_time = on_time.split(":")
                        end_monitor_time = off_time.split(":")

                        # Check the end time first
                        if current_time.hour > int(end_monitor_time[0]):
                            # Definitely not in the zone
                            # But there could be more time periods for today

                            controllable = False
                            continue

                        if current_time.hour == int(
                                end_monitor_time[0]
                        ) and current_time.minute >= int(end_monitor_time[1]):
                            controllable = False
                            continue

                        # Now the start time

                        if current_time.hour < int(start_monitor_time[0]):
                            controllable = False
                            continue

                        if current_time.hour == int(
                                start_monitor_time[0]
                        ) and current_time.minute < int(start_monitor_time[1]):
                            controllable = False
                            continue

                        # It must be within the time if it gets here

                        controllable = True
                        break_loop = True
                        break

            except Exception:
                pass  # Every parse of the file will throw an exception as the fields are not consistent

        config_file.close()

    return {'Controlled': controllable, 'Monitored': monitoring}