def __process_next_sms(self): signal_strength_percentage = "--" time_before_fetch = time.time() try: sms_fetcher = SmsFetcher( self.config["gammuConfigFile"], self.config["gammuConfigSection"], self.config["logDir"] + "/sms-delete.log", ) signal_strength_percentage = sms_fetcher.get_signal_strength_percentage() sms_messages = sms_fetcher.delete_get_next_sms() except (gammu.ERR_TIMEOUT, gammu.ERR_DEVICENOTEXIST, gammu.ERR_NOTCONNECTED): timeout_after_time = time.time() - time_before_fetch debug( "Got exception after {} seconds while trying to fetch/delete next sms (signalStrength: {}%).".format( timeout_after_time, signal_strength_percentage ) ) raise # re-raise exception so we get the stacktrace to stderr absolute_script_path = os.path.abspath(__file__) if not sms_messages: # would write a log message every minute even if no sms found # debug("No sms found by {} - bye.".format(absolute_script_path)) return TemperatureController.NO_SMS_FOUND debug("Start sms processing (found {} messages) by {}".format(len(sms_messages), absolute_script_path)) sms = sms_messages[0] # set system datetime to sent/received DateTime from received sms if delta is big enough if self.config["systemDatetimeMaxDiffNoUpdateSeconds"] > 0: system_datetime = datetime.now() sms_datetime_naive = sms[0]["DateTime"] delta_datetime = sms_datetime_naive - system_datetime delta_seconds = delta_datetime.total_seconds() if abs(delta_seconds) > self.config["systemDatetimeMaxDiffNoUpdateSeconds"]: # example unix date: Thu Nov 28 23:29:53 CET 2014 sms_datetime_unix = sms_datetime_naive.strftime("%a %b %d %H:%M:%S %Y") set_date_cmd = 'date -s "{0}" > /dev/null'.format(sms_datetime_unix) debug("Updating system datetime (delta: {} seconds) using cmd: {}".format(delta_seconds, set_date_cmd)) os.system(set_date_cmd) # else: # debug("system date diff ({} seconds) is not greater than configured delta ({} seconds), skipping updating.".format(delta_seconds, self.config['systemDatetimeMaxDiffNoUpdateSeconds'])) now_string = datetime.now().strftime(DATETIME_FORMAT) sender_number = sms[0]["Number"] sender_message_raw = sms[0]["Text"] sender_message = repr(sender_message_raw) debug(" got sms message from {}: {}".format(sender_number, sender_message)) if self.config["myNumber"] in sender_number: debug(" got sms from my own number - not responding in order to prevent infinite loop. Bye!") return TemperatureController.SMS_IGNORED elif self.config["blacklistSenders"]: # empty strings are false in python for blacklist_sender in self.config["blacklistSenders"].split(","): if len(blacklist_sender) > 0 and blacklist_sender in sender_number: debug(" this sender is in blacklist (matched '{}') - ignoring. Bye!".format(blacklist_sender)) return TemperatureController.SMS_IGNORED if len(sms_messages) > 1: debug( " found sms consisting of {} parts - only the first part will be considered.".format(len(sms_messages)) ) response_message = None if sender_message_raw and sender_message_raw.lower().startswith("temp"): temp_raw = temperaturereader.read_celsius() if temp_raw: temp = round(temp_raw, 1) debug(" responding with temperature: {} Celsius.".format(temp)) response_message = u"Hi! Current temperature here is {0} Celsius ({1}).".format(temp, now_string) else: debug(" temperature could not be read.") response_message = u"Hi! Temperature sensor is offline, check log files." elif sender_message_raw and sender_message_raw.lower().startswith("power autocontrol "): message_payload = sender_message_raw[len("power autocontrol ") :] parts = message_payload.split(None, 3) success = False if len(parts) >= 2: try: switch_on_temperature = float(parts[0].replace(",", ".")) switch_off_temperature = float(parts[1].replace(",", ".")) except Exception as e: # silently ignore, reply with help message debug(" exception occurred while trying to convert supplied args to float number, ignoring") else: pac = PowerAutocontroller(config_parser) switch_on_temp_before = pac.get_switch_on_temperature() switch_off_temp_before = pac.get_switch_off_temperature() current_temp_raw = temperaturereader.read_celsius() pac.set_switch_onoff_temperatures(CONFIG_FILEPATH, switch_on_temperature, switch_off_temperature) switch_on_temp_after = pac.get_switch_on_temperature() switch_off_temp_after = pac.get_switch_off_temperature() if current_temp_raw: current_temp = round(current_temp_raw, 1) debug( " responding with updated temperature interval [{2}:{3}] (was: [{0}:{1}]), current temperature: {4} Celsius.".format( switch_on_temp_before, switch_off_temp_before, switch_on_temp_after, switch_off_temp_after, current_temp, ) ) response_message = u"Hi! Successfully set temperature interval to [{0}:{1}]. Current temperature is {2}.".format( switch_on_temp_after, switch_off_temp_after, current_temp ) else: debug( " responding with updated temperature interval [{2}:{3}] (was: [{0}:{1}]), current temperature could not be read.".format( switch_on_temp_before, switch_off_temp_before, switch_on_temp_after, switch_off_temp_after, ) ) response_message = u"Hi! Successfully set temperature interval to [{0}:{1}]. Current temperature could not be read.".format( switch_on_temp_after, switch_off_temp_after ) success = True if not success: debug(" couldnt understand 'power autocontrol' message, responding with help message.") response_message = u'Hi! Didnt understand your message, use "power autocontrol 4 12" to enable power between 4 and 12 degrees.' elif sender_message_raw and sender_message_raw.lower().startswith("power"): gpio_channels = [int(channel) for channel in self.config["relayGpioChannels"].split(",")] powerswitcher = PowerSwitcher(gpio_channels=gpio_channels) power_status_before = powerswitcher.get_status_string() requested_state = "" if sender_message_raw.lower().startswith("power on"): requested_state = "ON" elif sender_message_raw.lower().startswith("power off"): requested_state = "OFF" manual_switching_allowed = not self.config["powerAutocontrolEnabled"] if manual_switching_allowed and requested_state and requested_state == "ON": powerswitcher.set_status_on() debug(" power has been set ON") elif manual_switching_allowed and requested_state and requested_state == "OFF": powerswitcher.set_status_off() debug(" power has been set OFF") else: debug( " power switching not requested/allowed (manual switching allowed? {})".format( manual_switching_allowed ) ) power_status = powerswitcher.get_status_string() debug(" responding with power status: {1} (was: {0}).".format(power_status_before, power_status)) if requested_state and not manual_switching_allowed: response_message = u"Hi! Power autocontrol is enabled, cannot switch power manually (currently {0}).".format( power_status ) elif requested_state: response_message = u"Hi! Power has been switched {0}, was {1} ({2}).".format( power_status, power_status_before, now_string ) else: response_message = u"Hi! Power is currently {0} ({1}).".format(power_status, now_string) elif sender_message_raw and sender_message_raw.lower().startswith("systeminfo"): debug(" responding with system info:") up_since = systeminfo.get_last_reboot_date_time() kernelVersion = systeminfo.get_kernel_version() rpiSerialNumber = systeminfo.get_rpi_serial_number() localInetAddress = systeminfo.get_inet_address() gitRevision = systeminfo.get_git_revision() balance_info = self.__get_cached_balance_info(short=True) debug(" system datetime : {}".format(now_string)) debug(" up since : {}".format(up_since)) debug(" kernel version : {}".format(kernelVersion)) debug(" RPi serial : {}".format(rpiSerialNumber)) debug(" inet address : {}".format(localInetAddress)) debug(" git revision : {}".format(gitRevision)) debug(" Signal strength : {}%".format(signal_strength_percentage)) debug(" Balance : {}".format(balance_info)) response_message = u"Hi!\n sysTime: {0}\n uptime: {1}\n kernel: {2}\n serial: {3}\n inet: {4}\n gitRev: {5}\n signal: {6}%\n $$: {7}.".format( now_string, up_since, kernelVersion, rpiSerialNumber, localInetAddress, gitRevision, signal_strength_percentage, balance_info, ) elif sender_message_raw and sender_message_raw.lower().startswith("checkbalance"): ussd = self.config["ussdCheckBalance"] self.__update_balance_if_necessary(force=True) balance_info = self.__get_cached_balance_info() debug(" responding with USSD reply for {}:".format(ussd)) debug(" " + balance_info.encode("ascii", "replace")) response_message = u"Hi! Current balance info:\n{0}".format(balance_info) else: debug(" not recognized, answering with help message.") response_message = u"Hi! 'temp' to get current temperature, 'power' (+' on'/' off') to get (change) power status. Other commands: 'systeminfo', 'checkbalance'." time_before_send = time.time() try: sms_sender = SmsSender(self.config["gammuConfigFile"], self.config["gammuConfigSection"]) sms_sender.send_sms(response_message, sender_number) return TemperatureController.SMS_PROCESSED except (gammu.ERR_UNKNOWN): timeout_after_time = time.time() - time_before_send debug("Got exception after {} seconds while trying to send sms.".format(timeout_after_time)) raise # re-raise exception so we get the stacktrace to stderr
def __process_next_sms(self): signal_strength_percentage = '--' time_before_fetch = time.time() try: sms_fetcher = SmsFetcher(self.config['gammuConfigFile'], self.config['gammuConfigSection'], self.config['logDir'] + '/sms-delete.log') signal_strength_percentage = sms_fetcher.get_signal_strength_percentage() sms_messages = sms_fetcher.delete_get_next_sms() except (gammu.ERR_TIMEOUT, gammu.ERR_DEVICENOTEXIST, gammu.ERR_NOTCONNECTED): timeout_after_time = time.time() - time_before_fetch debug("Got exception after {} seconds while trying to fetch/delete next sms (signalStrength: {}%).".format(timeout_after_time, signal_strength_percentage)) raise # re-raise exception so we get the stacktrace to stderr absolute_script_path = os.path.abspath(__file__) if not sms_messages: # would write a log message every minute even if no sms found #debug("No sms found by {} - bye.".format(absolute_script_path)) return TemperatureController.NO_SMS_FOUND debug("Start sms processing (found {} messages) by {}".format(len(sms_messages), absolute_script_path)) sms = sms_messages[0] # set system datetime to sent/received DateTime from received sms if delta is big enough if self.config['systemDatetimeMaxDiffNoUpdateSeconds'] > 0: system_datetime = datetime.now() sms_datetime_naive = sms[0]['DateTime'] delta_datetime = sms_datetime_naive - system_datetime delta_seconds = delta_datetime.total_seconds() if abs(delta_seconds) > self.config['systemDatetimeMaxDiffNoUpdateSeconds']: # example unix date: Thu Nov 28 23:29:53 CET 2014 sms_datetime_unix = sms_datetime_naive.strftime("%a %b %d %H:%M:%S %Y") set_date_cmd = "date -s \"{0}\" > /dev/null".format(sms_datetime_unix) debug("Updating system datetime (delta: {} seconds) using cmd: {}".format(delta_seconds, set_date_cmd)) os.system(set_date_cmd) #else: #debug("system date diff ({} seconds) is not greater than configured delta ({} seconds), skipping updating.".format(delta_seconds, self.config['systemDatetimeMaxDiffNoUpdateSeconds'])) now_string = datetime.now().strftime(DATETIME_FORMAT) sender_number = sms[0]['Number'] sender_message_raw = sms[0]['Text'] sender_message = repr(sender_message_raw) debug(" got sms message from {}: {}".format(sender_number, sender_message)) if self.config['myNumber'] in sender_number: debug(" got sms from my own number - not responding in order to prevent infinite loop. Bye!") return TemperatureController.SMS_IGNORED elif self.config['blacklistSenders']: # empty strings are false in python for blacklist_sender in self.config['blacklistSenders'].split(","): if len(blacklist_sender) > 0 and blacklist_sender in sender_number: debug(" this sender is in blacklist (matched '{}') - ignoring. Bye!".format(blacklist_sender)) return TemperatureController.SMS_IGNORED if len(sms_messages) > 1: debug(" found sms consisting of {} parts - only the first part will be considered.".format(len(sms_messages))) response_message = None if sender_message_raw and sender_message_raw.lower().startswith('temp'): temp_raw = temperaturereader.read_celsius() if temp_raw: temp = round(temp_raw, 1) debug(" responding with temperature: {} Celsius.".format(temp)) response_message = u'Hi! Current temperature here is {0} Celsius ({1}).'.format(temp, now_string) else: debug(" temperature could not be read.") response_message = u'Hi! Temperature sensor is offline, check log files.' elif sender_message_raw and sender_message_raw.lower().startswith('power autocontrol '): message_payload = sender_message_raw[len('power autocontrol '):] parts = message_payload.split(None, 3) success = False if len(parts) >= 2: try: switch_on_temperature = float(parts[0].replace(',', '.')) switch_off_temperature = float(parts[1].replace(',', '.')) except Exception as e: # silently ignore, reply with help message debug(" exception occurred while trying to convert supplied args to float number, ignoring") else: pac = PowerAutocontroller(config_parser) switch_on_temp_before = pac.get_switch_on_temperature() switch_off_temp_before = pac.get_switch_off_temperature() current_temp_raw = temperaturereader.read_celsius() pac.set_switch_onoff_temperatures(CONFIG_FILEPATH, switch_on_temperature, switch_off_temperature) switch_on_temp_after = pac.get_switch_on_temperature() switch_off_temp_after = pac.get_switch_off_temperature() if current_temp_raw: current_temp = round(current_temp_raw, 1) debug(" responding with updated temperature interval [{2}:{3}] (was: [{0}:{1}]), current temperature: {4} Celsius.".format(switch_on_temp_before, switch_off_temp_before, switch_on_temp_after, switch_off_temp_after, current_temp)) response_message = u'Hi! Successfully set temperature interval to [{0}:{1}]. Current temperature is {2}.'.format(switch_on_temp_after, switch_off_temp_after, current_temp) else: debug(" responding with updated temperature interval [{2}:{3}] (was: [{0}:{1}]), current temperature could not be read.".format(switch_on_temp_before, switch_off_temp_before, switch_on_temp_after, switch_off_temp_after)) response_message = u'Hi! Successfully set temperature interval to [{0}:{1}]. Current temperature could not be read.'.format(switch_on_temp_after, switch_off_temp_after) success = True if not success: debug(" couldnt understand 'power autocontrol' message, responding with help message.") response_message = u'Hi! Didnt understand your message, use "power autocontrol 4 12" to enable power between 4 and 12 degrees.' elif sender_message_raw and sender_message_raw.lower().startswith('power'): gpio_channels = [int(channel) for channel in self.config['relayGpioChannels'].split(',')] powerswitcher = PowerSwitcher(gpio_channels=gpio_channels) power_status_before = powerswitcher.get_status_string() requested_state = '' if sender_message_raw.lower().startswith('power on'): requested_state = 'ON' elif sender_message_raw.lower().startswith('power off'): requested_state = 'OFF' manual_switching_allowed = not self.config['powerAutocontrolEnabled'] if manual_switching_allowed and requested_state and requested_state == 'ON': powerswitcher.set_status_on() debug(" power has been set ON") elif manual_switching_allowed and requested_state and requested_state == 'OFF': powerswitcher.set_status_off() debug(" power has been set OFF") else: debug(" power switching not requested/allowed (manual switching allowed? {})".format(manual_switching_allowed)) power_status = powerswitcher.get_status_string() debug(" responding with power status: {1} (was: {0}).".format(power_status_before, power_status)) if requested_state and not manual_switching_allowed: response_message = u'Hi! Power autocontrol is enabled, cannot switch power manually (currently {0}).'.format(power_status) elif requested_state: response_message = u'Hi! Power has been switched {0}, was {1} ({2}).'.format(power_status, power_status_before, now_string) else: response_message = u'Hi! Power is currently {0} ({1}).'.format(power_status, now_string) elif sender_message_raw and sender_message_raw.lower().startswith('systeminfo'): debug(" responding with system info:") up_since = systeminfo.get_last_reboot_date_time() kernelVersion = systeminfo.get_kernel_version() rpiSerialNumber = systeminfo.get_rpi_serial_number() localInetAddress = systeminfo.get_inet_address() gitRevision = systeminfo.get_git_revision() balance_info = self.__get_cached_balance_info(short=True) debug(" system datetime : {}".format(now_string)) debug(" up since : {}".format(up_since)) debug(" kernel version : {}".format(kernelVersion)) debug(" RPi serial : {}".format(rpiSerialNumber)) debug(" inet address : {}".format(localInetAddress)) debug(" git revision : {}".format(gitRevision)) debug(" Signal strength : {}%".format(signal_strength_percentage)) debug(" Balance : {}".format(balance_info)) response_message = u'Hi!\n sysTime: {0}\n uptime: {1}\n kernel: {2}\n serial: {3}\n inet: {4}\n gitRev: {5}\n signal: {6}%\n $$: {7}.'.format(now_string, up_since, kernelVersion, rpiSerialNumber, localInetAddress, gitRevision, signal_strength_percentage, balance_info) elif sender_message_raw and sender_message_raw.lower().startswith('checkbalance'): ussd = self.config['ussdCheckBalance'] self.__update_balance_if_necessary(force=True) balance_info = self.__get_cached_balance_info() debug(" responding with USSD reply for {}:".format(ussd)) debug(" " + balance_info.encode('ascii', 'replace')) response_message = u'Hi! Current balance info:\n{0}'.format(balance_info) else: debug(" not recognized, answering with help message.") response_message = u"Hi! 'temp' to get current temperature, 'power' (+' on'/' off') to get (change) power status. Other commands: 'systeminfo', 'checkbalance'." time_before_send = time.time() try: sms_sender = SmsSender(self.config['gammuConfigFile'], self.config['gammuConfigSection']) sms_sender.send_sms(response_message, sender_number) return TemperatureController.SMS_PROCESSED except (gammu.ERR_UNKNOWN): timeout_after_time = time.time() - time_before_send debug("Got exception after {} seconds while trying to send sms.".format(timeout_after_time)) raise # re-raise exception so we get the stacktrace to stderr
errors_file = work_dir + '/GAMMU_ERRORS' if os.path.isfile(errors_file): with open(errors_file, 'r') as f: gammu_errors_count = f.readlines()[0] current_reboot_threshold = '-' errors_threshold_file = work_dir + '/ERRORS_THRESHOLD_BEFORE_REBOOT' if os.path.isfile(errors_threshold_file): with open(errors_threshold_file, 'r') as f: current_reboot_threshold = f.readlines()[0] send_success = False send_attempts = 0 retry_interval_seconds = 60 while not send_success: try: send_attempts += 1 sms_sender = SmsSender(gammu_config_file, gammu_config_section) reboot_message = u'Hi Admin! Reboot done @ {0}. Power is {1}. Inet: {2}. {3} sms send attempts needed. 3G dongle error status: {4}/{5}.'.format(log_ts, power_status, localIpAddress, send_attempts, gammu_errors_count, current_reboot_threshold) sms_sender.send_sms(reboot_message, admin_phone_number) send_success = True except (gammu.ERR_TIMEOUT) as e: if (('Code' in e and e['Code'] == 14) and ('Where' in e and e['Where'] == 'Init')): print("{0} attempt #{1} to send boot-completed sms to admin failed, retrying after sleeping {2}mins ...".format(log_ts, send_attempts, retry_interval_seconds/60)) time.sleep(retry_interval_seconds) else: raise
errors_threshold_file = work_dir + '/ERRORS_THRESHOLD_BEFORE_REBOOT' if os.path.isfile(errors_threshold_file): with open(errors_threshold_file, 'r') as f: current_reboot_threshold = f.readlines()[0] send_success = False send_attempts = 0 retry_interval_seconds = 60 while not send_success: try: send_attempts += 1 sms_sender = SmsSender(gammu_config_file, gammu_config_section) reboot_message = u'Hi Admin! Reboot done @ {0}. Power is {1}. Inet: {2}. {3} sms send attempts needed. 3G dongle error status: {4}/{5}.'.format( log_ts, power_status, localIpAddress, send_attempts, gammu_errors_count, current_reboot_threshold) sms_sender.send_sms(reboot_message, admin_phone_number) send_success = True except (gammu.ERR_TIMEOUT) as e: if (('Code' in e and e['Code'] == 14) and ('Where' in e and e['Where'] == 'Init')): print( "{0} attempt #{1} to send boot-completed sms to admin failed, retrying after sleeping {2}mins ..." .format(log_ts, send_attempts, retry_interval_seconds / 60)) time.sleep(retry_interval_seconds) else: raise