Пример #1
0
def test_delete_not_crashing(path=None):
    # although in above test we just use/interact with Keyring without specifying
    # any custom one, there we do not change it so I guess it is ok. Here we want
    # a real keyring backend which we will alter
    from keyrings.alt.file import PlaintextKeyring
    kb = PlaintextKeyring()
    kb.filename = path

    keyring = Keyring(keyring_backend=kb)
    cred = UserPassword("test1", keyring=keyring)

    cred.set(user="******", password="******")
    ok_file_has_content(path, ".*test1.*",
                        re_=True)  # keyring backend saves where we expect

    # manually delete one component of the credential
    cred._keyring.delete(cred.name, next(iter(cred._FIELDS)))

    # now delete entire credential -- we must not crash
    cred.delete()
    try:
        ok_file_has_content(path, ".*test1.*",
                            re_=True)  # keyring backend saves where we expect
        raise AssertionError("keyring still has our key")
    except AssertionError:
        pass
Пример #2
0
def setup_virtual_cc(virtual_name):
    """ to have multiple instances running at the same time """
    virtual_dir = os.path.join(os.path.expanduser('~'), 'virtual-crosscloud',
                               virtual_name)
    config.log_dir = os.path.join(virtual_dir, 'log')
    config.sync_root = os.path.join(virtual_dir, 'sync_root')
    config.config_dir = os.path.join(virtual_dir, 'config')
    config.cache_dir = os.path.join(virtual_dir, 'cache')
    config.private_key_path = os.path.join(virtual_dir, 'private.pem')
    config.public_key_path = os.path.join(virtual_dir, 'public.pub')
    config.lock_file = os.path.join(virtual_dir, 'lock_file')
    config.config_file = os.path.join(config.config_dir, 'config.ini')

    plaintext = PlaintextKeyring()
    plaintext.file_path = os.path.join(config.config_dir, 'keyring.ini')
    keyring.set_keyring(plaintext)
Пример #3
0
 def setup_method(self):
     self.PROGNAME = totp_generator.__progname__
     self.VERSION = totp_generator.__version__
     # force keyring to use tmpfile
     PlaintextKeyring.file_path = self.tmp_keyring_file = tempfile.mktemp()
     self.keyring_generator = KeyringTotpGenerator(force_keyring=PlaintextKeyring())
     self.test_data = import_test_data()
Пример #4
0
    def setUp(self):
        keyring.set_keyring(PlaintextKeyring())

        self.env = EnvironmentVarGuard()
        self.temp_file = tempfile.NamedTemporaryFile()

        test_path = os.path.dirname(os.path.realpath(__file__))
        self.env['XDG_DATA_HOME'] = test_path
        self.env['XDG_CONFIG_HOME'] = test_path
Пример #5
0
 def save_session(self, **save_session_kwargs):
     if self.SESSION_ARG_KEYNAME not in save_session_kwargs:
         raise ValueError(
             '"%s" must be specified in save_session() argument.' %
             self.SESSION_ARG_KEYNAME)
     data = base64.b64encode(
         zlib.compress(pickle.dumps(self,
                                    self.PICKLE_PROTOCOL))).decode('utf-8')
     keyring.set_keyring(PlaintextKeyring())
     keyring.set_password(self.KEYRING_SERVICE_NAME,
                          save_session_kwargs[self.SESSION_ARG_KEYNAME],
                          data)
Пример #6
0
 def set_keyring(self):
     if platform.system() == WINDOWS:
         keyring.set_keyring(WinVaultKeyring())
     elif os.environ.get(OVERRIDE_KEYRING_ENV_VAR
                         ) == "true":  # Used in builds when running tests
         keyring.set_keyring(PlaintextKeyring())
     elif platform.system() == MAC:
         keyring.set_keyring(keyring.backends.OS_X.Keyring())
     elif platform.system() == LINUX:
         keyring.set_keyring(FiggyKeyring())
     else:
         Utils.stc_error_exit(
             "Only OSX and MAC and Linux with installed SecretStorage are supported for "
             "OKTA + Keyring integration.")
Пример #7
0
    def load_session(**load_session_kwargs):
        """
        :param dict[str, str] load_session_kwargs:
        :return onedrived.od_api_session.OneDriveAPISession:
        """
        keyarg = OneDriveAPISession.SESSION_ARG_KEYNAME
        if keyarg not in load_session_kwargs:
            raise ValueError(
                '"%s" must be specified in load_session() argument.' % keyarg)
        keyring.set_keyring(PlaintextKeyring())
        saved_data = keyring.get_password(
            OneDriveAPISession.KEYRING_SERVICE_NAME,
            load_session_kwargs[keyarg])

        if saved_data is None:
            raise ValueError("Don't find anything")

        data = zlib.decompress(base64.b64decode(saved_data.encode('utf-8')))
        return pickle.loads(data)
Пример #8
0
async def async_setup(hass, config):
    """Set up this component using YAML."""

    if config.get(DOMAIN) is None:
        # We get her if the integration is set up using config flow
        return True

    # Print startup message

    # Check that all required files are present
    file_check = await check_files(hass)
    if not file_check:
        return False

    # Create DATA dict
    hass.data[DOMAIN_DATA] = {}

    # Get "global" configuration.
    creds = config[DOMAIN].get(CONF_CREDENTIALS_LOCATION)
    default_list = config[DOMAIN].get(CONF_DEFAULT_LIST)
    force_login = config[DOMAIN].get(CONF_FORCE_LOGIN)
    hass.data[DOMAIN_DATA]["default_list"] = default_list

    # Configure the client.
    try:
        kr = PlaintextKeyring()
        keyring.set_keyring(kr)
        _LOGGER.info('keyring : {}'.format(kr))
        gtasks_obj = Gtasks(open_browser=False,
                            force_login=force_login,
                            credentials_location=creds,
                            two_steps=True)
        hass.data[DOMAIN_DATA]["auth_url"] = gtasks_obj.auth_url()
        hass.data[DOMAIN_DATA]["gtasks_obj"] = gtasks_obj
    except Exception as e:
        _LOGGER.exception(e)
        return False

    return True
Пример #9
0
import test.framework.systemtools as s
import test.framework.toolchain as tc
import test.framework.toolchainvariables as tcv
import test.framework.toy_build as t
import test.framework.type_checking as et
import test.framework.tweak as tw
import test.framework.variables as v
import test.framework.yeb as y

# set plain text key ring to be used,
# so a GitHub token stored in it can be obtained without having to provide a password
try:
    # with recent versions of keyring, PlaintextKeyring comes from keyrings.alt
    import keyring
    from keyrings.alt.file import PlaintextKeyring
    keyring.set_keyring(PlaintextKeyring())
except ImportError:
    try:
        # with old versions of keyring, PlaintextKeyring comes from keyring.backends
        import keyring
        from keyring.backends.file import PlaintextKeyring
        keyring.set_keyring(PlaintextKeyring())
    except ImportError:
        pass

# disable all logging to significantly speed up tests
fancylogger.disableDefaultHandlers()
fancylogger.setLogLevelError()

# make sure temporary files can be created/used
try:
Пример #10
0
 def __init__(self):
     keyring.set_keyring(PlaintextKeyring())
     self.yag = yagmail.SMTP(
         {'*****@*****.**': 'RSTSolutions-IoT Hub'})
     self.email_list = ['*****@*****.**']
Пример #11
0
def check_power_and_internet(run, notification):
    if run == "boot":
        just_booted = True
    elif run == "scheduled":
        just_booted = False
    if notification == "notification" or notification == "ifttt":
        send_notification = True
        ifttt_notification = False
    elif notification == "mail":
        send_notification = False
        ifttt_notification = False
    if notification == "ifttt":
        ifttt_notification = True

    config_path = os.path.join(os.path.expanduser("~"), ".config/outagedetector")
    log_path = os.path.join("/var/log/")
    address_available = False
    address = ""
    timestamp_format = "%d-%m-%Y %H-%M-%S"
    hour_minute_format = "%H:%M"

    internet_connected = check_internet_connection()

    if not send_notification:
        try:
            with open(os.path.join(config_path, "config.json")) as json_file:
                mail_json = json.load(json_file)
                sender = mail_json["sender"]
                receivers = mail_json["receivers"]
                smtp_server = mail_json["smtp_server"]
                keyring.set_keyring(PlaintextKeyring())
                password = keyring.get_password("Mail-OutageDetector", sender)
                if password is None:
                    print("Mail password not found, try running initial configuration again!")
                    exit(1)
                address = mail_json["house_address"]
        except FileNotFoundError:
            print("Mail will not be sent, there is no config file in the folder.")
        except KeyError:
            print("Config.json file doesn't have all fields (sender, receivers, smtp_server, house address")
    else:
        if not ifttt_notification:
            keyring.set_keyring(PlaintextKeyring())
            push_key = keyring.get_password("PushBullet-OutageDetector", "pushbullet")
            try:
                with open(os.path.join(config_path, "config.json")) as json_file:
                    notification_json = json.load(json_file)
                    address = notification_json["house_address"]
            except FileNotFoundError:
                print("Configuration file does not exist, try running the initial configuration again!")
            except KeyError:
                print("Config.json file doesn't have all fields, try running the initial configuration again!")
        else:
            try:
                with open(os.path.join(config_path, "config.json")) as json_file:
                    notification_json = json.load(json_file)
                    ifttt_name = notification_json["ifttt_event"]
                    address = notification_json["house_address"]
            except FileNotFoundError:
                print("Configuration file does not exist, try running the initial configuration again!")
            except KeyError:
                print("Config.json file doesn't have all fields, try running the initial configuration again!")
            keyring.set_keyring(PlaintextKeyring())
            api_key = keyring.get_password("IFTTT-OutageDetector", ifttt_name)

    if address:
        address_available = True

    current_timestamp = datetime.now()
    current_timestring = datetime.strftime(current_timestamp, timestamp_format)
    current_hour_min = datetime.strftime(current_timestamp, hour_minute_format)

    try:
        with open(os.path.join(log_path, "outagedetector_last_timestamp.txt")) as file:
            read_string = file.read()
    except FileNotFoundError:
        read_string = ""

    file_data = read_string.split(",")

    try:
        last_power_timestring = file_data[0]
        last_internet_timestring = file_data[1]
        last_argument = file_data[2]
        last_periodicity = int(file_data[3])
    except IndexError:
        last_power_timestring = current_timestring
        last_internet_timestring = current_timestring
        last_argument = "N/A"
        last_periodicity = 0
    except ValueError:
        last_power_timestring = current_timestring
        last_internet_timestring = current_timestring
        last_argument = "N/A"
        last_periodicity = 0

    last_power_timestamp = datetime.strptime(last_power_timestring, timestamp_format)

    periodicity = extract_run_periodicity(run,
                                          last_argument,
                                          current_timestamp,
                                          last_power_timestamp,
                                          last_periodicity)

    with open(os.path.join(log_path, "outagedetector_last_timestamp.txt"), 'w+') as file:
        if internet_connected:
            file.write("{},{},{},{}".format(current_timestring, current_timestring, run, periodicity))
        else:
            file.write("{},{},{},{}".format(current_timestring, last_internet_timestring, run, periodicity))

    if internet_connected:
        if just_booted:
            power_outage_time = int((current_timestamp - last_power_timestamp).total_seconds() / 60)
            if periodicity > 0:
                min_outage_time = max(range(0, power_outage_time + 1, periodicity))
            else:
                min_outage_time = 0
            notification = "Power was out for {} to {} minutes at {}.".format(min_outage_time, power_outage_time,
                                                                              current_hour_min)
            if address_available:
                notification += " Address: {}.".format(address)
            print("Power was out for {} to {} minutes at {}".format(min_outage_time, power_outage_time,
                                                                    current_timestring))
            if send_notification:
                if ifttt_notification:
                    push.push_to_ifttt(ifttt_name, api_key, notification)
                else:
                    push.push_to_iOS("Power outage", notification, push_key)
            else:
                mail.send_mail(sender, receivers, "Power outage", notification, smtp_server, password)

        if not last_power_timestring == last_internet_timestring:
            last_internet_timestamp = datetime.strptime(last_internet_timestring, timestamp_format)
            internet_downtime = int((current_timestamp - last_internet_timestamp).total_seconds() / 60)
            if periodicity > 0:
                min_outage_time = max(range(0, internet_downtime + 1, periodicity))
            else:
                min_outage_time = 0
            print("Internet was down for {} to {} minutes at {}".format(min_outage_time, internet_downtime,
                                                                        current_timestring))
            notification = "Internet has been down for {} to {} minutes at {}.".format(min_outage_time,
                                                                                       internet_downtime,
                                                                                       current_hour_min)
            if address_available:
                notification += " Address: {}.".format(address)
            if send_notification:
                if ifttt_notification:
                    push.push_to_ifttt(ifttt_name, api_key, notification)
                else:
                    push.push_to_iOS("Internet down", notification, push_key)
            else:
                mail.send_mail(sender, receivers, "Internet down", notification, smtp_server, password)

    print("Script has run at {}. Internet connected: {}. Just booted: {}.".format(current_timestring,
                                                                                  internet_connected,
                                                                                  just_booted))
Пример #12
0
def _main(argv=None):
    if sys.version_info < (3, 5):
        print(
            "Error: Your version of Python is too old, 3.5+ is required: %d.%d.%d"
            % sys.version_info[:3])
        return -1

    try:
        check_runtime_requirements()
    except RuntimeError as e:
        print("Error: %s" % (e, ))
        return -1

    # Protect access token and potentially encryption keys
    block_tracing()

    if argv is None:
        argv = sys.argv

    parser = argparse.ArgumentParser()
    userspacefs.add_cli_arguments(parser)
    parser.add_argument("-c", "--config-file", help="config file path")
    parser.add_argument(
        "-e",
        "--encrypted-folder",
        dest='encrypted_folders',
        type=parse_encrypted_folder_arg,
        default=[],
        action='append',
        help=
        "relative paths of encrypted folders, can be used multiple times. requires safefs"
    )
    parser.add_argument(
        "--print-default-config-file",
        action='store_true',
        help="print default config file path to standard out and quit")
    parser.add_argument("--cache-dir", help="file cache directory")
    parser.add_argument("mount_point", nargs='?')
    args = parser.parse_args(argv[1:])

    try:
        version = pkg_resources.require("dbxfs")[0].version
    except Exception:
        log.warning("Failed to get version", exc_info=True)
        version = ''

    if version:
        try:
            with urllib.request.urlopen(
                    "https://pypi.org/pypi/dbxfs/json") as f:
                rversion = json.load(io.TextIOWrapper(f))['info']['version']
                if rversion != version:
                    print(
                        "\033[0;31m\033[1mWarning: dbxfs is out of date (%s vs %s), upgrade with 'pip3 install --upgrade dbxfs'\033[0;0m"
                        % (rversion, version))
        except Exception:
            log.warning("Failed to get most recent version", exc_info=True)

    config_dir = appdirs.user_config_dir(APP_NAME)

    if args.config_file is not None:
        config_file = args.config_file
    else:
        config_file = os.path.join(config_dir, "config.json")

    if args.print_default_config_file:
        print(config_file)
        return 0

    try:
        os.makedirs(config_dir, exist_ok=True)
    except OSError as e:
        print("Unable to create configuration directory: %s" % (e, ))
        return -1

    config = {}
    try:
        f = open(config_file)
    except IOError as e:
        if e.errno != errno.ENOENT: raise
    else:
        try:
            with f:
                config = json.load(f)
        except ValueError as e:
            print("Config file %r is not valid json: %s" % (config_file, e))
            return -1

    cache_folder = args.cache_dir
    if cache_folder is None:
        cache_folder = config.get("cache_dir")

    mount_point = args.mount_point
    if mount_point is None:
        mount_point = config.get("mount_point")

    if not args.smb_no_mount and mount_point is None:
        parser.print_usage()
        print("%s: error: please provide the mount_point argument" %
              (os.path.basename(argv[0]), ))
        return 1

    encrypted_folders = config.get("encrypted_folders",
                                   []) + args.encrypted_folders
    if safefs_wrap_create_fs is None and encrypted_folders:
        print(
            "safefs not installed, can't transparently decrypt encrypted folders"
        )
        return 1

    access_token = None
    save_access_token = False
    save_config = False

    access_token_command = config.get("access_token_command", None)
    if access_token_command is not None:
        print("Running %r for access token" %
              (' '.join(access_token_command), ))
        try:
            access_token = subprocess.check_output(
                access_token_command).decode("utf-8")
        except UnicodeDecodeError:
            print("Access token command output is not utf-8 encoded")
            return -1
        except TypeError:
            print("Bad access token command: %r, " % (access_token_command, ))
            return -1
        # NB: access tokens never contain white-space and the access token
        #     command often accidentally appends a newline character.
        access_token = access_token.strip()

    if access_token is None:
        keyring_user = config.get("keyring_user", None)

        if keyring_user is not None:
            try:
                access_token = keyring.get_password(APP_NAME, keyring_user)
            except KeyringError as e:
                print("Failed to get access token from keyring: %s" % (e, ))

    if access_token is None:
        access_token_privy = config.get("access_token_privy", None)
        if access_token_privy is not None:
            passwd = None
            while True:
                passwd = getpass.getpass(
                    "Enter access token passphrase (not your Dropbox password) (Ctrl-C to quit): "
                )
                try:
                    access_token = privy.peek(access_token_privy,
                                              passwd).decode('utf-8')
                except ValueError:
                    if not yes_no_input(
                            "Incorrect password, create new access token?"):
                        continue
                break
            del passwd

    try_directly = False
    while True:
        if access_token is None:
            save_access_token = True

        if (access_token is None and try_directly and yes_no_input(
                "Want to try entering the access token directly?")):
            print("Go to https://dropbox.com/developers/apps to "
                  "create an app and generate a personal access token.")

            while True:
                access_token = getpass.getpass(
                    "Enter Access token (Ctrl-C to quit): ")
                if not access_token:
                    print("Access tokens cannot be empty")
                    continue
                break

        if access_token is None:
            auth_flow = dropbox.DropboxOAuth2FlowNoRedirect(
                APP_KEY, APP_SECRET)
            authorize_url = auth_flow.start()
            print("We need an access token. Perform the following steps:")
            print("1. Go to " + authorize_url)
            print("2. Click \"Allow\" (you may have to log in first)")
            print("3. Copy the authorization code.")

            while True:
                auth_code = input(
                    "Enter authorization code (Ctrl-C to quit): ")
                if not auth_code:
                    print("Authorization code cannot be empty")
                    continue
                break

            try:
                oauth_result = auth_flow.finish(auth_code)
            except Exception as e:
                print("Authorization code was invalid!")
                try_directly = True
                continue

            access_token = oauth_result.access_token

        # test out access token
        try:
            dropbox.Dropbox(access_token).users_get_current_account()
        except (dropbox.exceptions.BadInputError, dropbox.exceptions.AuthError,
                ValueError) as e:
            print("Error using access token: %s" % (e, ))
            access_token = None
            try_directly = True
        except OSError:
            if not yes_no_input("Error connecting to Dropbox, Try again?"):
                return 1
        else:
            break

    if save_access_token and yes_no_input(
            "We're all connected. Do you want to save your credentials for future runs?",
            default_yes=True):
        keyring_user = ''.join(
            [random.choice("asdfghjklzxcvbnmqwertyuiop") for _ in range(24)])
        if not yes_no_input(
                "Do you want to encrypt your credentials with a password?",
                default_yes=True):
            from keyrings.alt.file import PlaintextKeyring
            keyring.set_keyring(PlaintextKeyring())
        try:
            keyring.set_password(APP_NAME, keyring_user, access_token)
        except (KeyringError, RuntimeError) as e:
            print(
                "We need a passphrase to encrypt your access token before we can save it."
            )
            print(
                "Warning: Your access token passphrase must contain enough randomness to be resistent to hacking. You can read this for more info: https://blogs.dropbox.com/tech/2012/04/zxcvbn-realistic-password-strength-estimation/"
            )
            while True:
                pass_ = getpass.getpass("Enter new access token passphrase: ")
                pass2_ = getpass.getpass(
                    "Enter new access token passphrase (again): ")
                if pass_ != pass2_:
                    print("Passphrases didn't match, please re-enter")
                else:
                    del pass2_
                    break
            config.pop('keyring_user', None)
            config['access_token_privy'] = privy.hide(
                access_token.encode('utf-8'), pass_, server=False)
            del pass_
            save_config = True
        else:
            config.pop('access_token_privy', None)
            config['keyring_user'] = keyring_user
            save_config = True

    if not config.get("asked_send_error_reports", False):
        if yes_no_input(
                "Would you like to help us improve %s by providing anonymous error reports?"
                % (APP_NAME, ),
                default_yes=True):
            config['send_error_reports'] = True
        config['asked_send_error_reports'] = True
        save_config = True

    if save_access_token and yes_no_input(
            "Do you want \"%s\" to be the default mount point?" %
        (mount_point, ),
            default_yes=True):
        config['mount_point'] = mount_point
        save_config = True

    if save_config:
        with open(config_file, "w") as f:
            json.dump(config, f)

    log.info("Starting %s...", APP_NAME)

    if config.get('send_error_reports', False):
        try:
            sentry_sdk.init(
                "https://[email protected]/1293235",
                release='%s@%s' % (APP_NAME, version),
                with_locals=False)
        except Exception:
            log.warning("Failed to initialize sentry", exc_info=True)

    if cache_folder is None:
        cache_folder = os.path.join(appdirs.user_cache_dir(APP_NAME),
                                    "file_cache")
        try:
            os.makedirs(cache_folder, exist_ok=True)
        except OSError:
            log.warning(
                "Failed to create cache folder, running without file cache")
            cache_folder = None
        log.debug("Using default cache path %s", cache_folder)
    else:
        if not os.path.isdir(cache_folder):
            print(
                "User-provided \"cache_dir\" setting doesn't refer to a directory: \"%s\""
                % (cache_folder, ))
            return 1
        log.debug("Using custom cache path %s", cache_folder)

    def create_fs():
        fs = CachingFileSystem(DropboxFileSystem(access_token),
                               cache_folder=cache_folder)

        # From a purity standpoint the following layer ideally would
        # go between the caching fs and dropbox fs, but because the
        # contract between those two is highly specialized, just put
        # it on top
        fs = TranslateIgnoredFilesFileSystem(fs)

        if sys.platform == 'darwin':
            fs = DisableQuickLookFileSystem(fs)

        return fs

    if safefs_wrap_create_fs is not None:
        create_fs = safefs_wrap_create_fs(create_fs, encrypted_folders)

    if not os.path.exists(mount_point):
        if yes_no_input(
                "Mount point \"%s\" doesn't exist, do you want to create it?" %
            (mount_point, ),
                default_yes=True):
            try:
                os.makedirs(mount_point, exist_ok=True)
            except OSError as e:
                print("Unable to create mount point: %s" % (e, ))
                return -1

    return userspacefs.simple_main(
        mount_point,
        "dbxfs",
        create_fs,
        args,
        on_new_process=None if BLOCK_TRACING_INHERITS else block_tracing)
Пример #13
0
def initialize():
    config_path = os.path.join(os.path.expanduser("~"), ".config/outagedetector")
    log_path = os.path.join("/var/log/")
    if not os.path.exists(config_path):
        os.makedirs(config_path)
    if os.path.exists(os.path.join(config_path, "config.json")):
        result = curate_input("Configuration file already exists. Would you like to reconfigure the script? (y/n) ",
                              ("y", "n"))
        if result != "y":
            print("Alright, script should be ready to run. If you run into issues, run the initialization process "
                  "again")
            exit(1)

    json_data = {}
    print("We are going to walk you through setting up this script!")
    notification_type = curate_input("Would you like to be alerted of an outage through a notification"
                                     " on your phone, through mail, or through ifttt? ",
                                     ("notification", "mail", "ifttt"))
    json_data["notification_type"] = notification_type
    if notification_type == "mail":
        mail_working = False
        failed_attempts = 0
        while not mail_working:
            sender_mail_address = None
            while sender_mail_address is None:
                sender_mail_address = mail.check_mails(input("Please input the mail address you want to send the "
                                                             "notification mail from: "))
            json_data["sender"] = sender_mail_address
            keyring.set_keyring(PlaintextKeyring())
            keyring.set_password("Mail-OutageDetector", json_data["sender"],
                                 getpass.getpass("Type in your password: "******"Please input the mail addresses "
                                                                 "(separated by a comma) to which you want to send "
                                                                 "the notification: "))
            json_data["receivers"] = receiver_mail_addresses

            if "gmail" in json_data["sender"]:
                json_data["smtp_server"] = "smtp.gmail.com"
                json_data["port"] = 465
            elif "yahoo" in json_data["sender"]:
                json_data["smtp_server"] = "smtp.mail.yahoo.com"
                json_data["port"] = 465
            else:
                json_data["smtp_server"] = input("Please enter the SMTP server of your mail provider "
                                                 "(you can look it up online): ")
                port_number = ""
                while not port_number.isdigit():
                    port_number = input("Type in the port number of the SMTP server: ")
                json_data["port"] = port_number
            password = keyring.get_password("Mail-OutageDetector", json_data["sender"])
            try:
                mail.send_mail(json_data["sender"], json_data["receivers"], "Testing mail notification",
                               "Mail sent successfully!", json_data["smtp_server"], password, json_data["port"])
                mail_working = True
                print("Mail has been successfully sent, check your mailbox!")
            except mail.SMTPAuthenticationError as e:
                failed_attempts += 1
                if failed_attempts >= 3:
                    print("Too many failed attempts, exiting script, try again later!")
                    exit(1)
                if "BadCredentials" in str(e):
                    print(e)
                    print("Wrong user/password or less secure apps are turned off")
                elif "InvalidSecondFactor" in str(e):
                    print(e)
                    print("Two factor authentification is not supported! Turn it off and try again!")
            except socket.gaierror:
                print("No internet connection, try again later!")
                exit(1)

    elif notification_type == "notification":
        pushbullet_working = False
        failed_attempts = 0
        while not pushbullet_working:
            try:
                keyring.set_keyring(PlaintextKeyring())
                keyring.set_password("PushBullet-OutageDetector", "pushbullet",
                                     getpass.getpass("Input your PushBullet API key: "))
                pushbullet_key = keyring.get_password("PushBullet-OutageDetector", "pushbullet")
                print("Trying to send a notification through PushBullet!")
                push.push_to_iOS("Testing PushBullet Key", "Test is successful!", pushbullet_key)
                pushbullet_working = True
                print("Notification has been successfully sent, check your phone!")
            except push.errors.InvalidKeyError:
                failed_attempts += 1
                if failed_attempts >= 3:
                    print("Too many failed attempts, exiting script, try again later!")
                    exit(1)
                print("Key is not valid, try again!")

            except requests.exceptions.ConnectionError:
                print("No internet, try reconnecting and running the script again!")
                exit(1)

    elif notification_type == "ifttt":
        ifttt_working = False
        failed_attempts = 0
        while not ifttt_working:
            try:
                ifttt_name = input("Input your IFTTT event name: ")
                keyring.set_keyring(PlaintextKeyring())
                keyring.set_password("IFTTT-OutageDetector", ifttt_name, getpass.getpass("Input your IFTTT API key: "))
                api_key = keyring.get_password("IFTTT-OutageDetector", ifttt_name)
                print("Trying to send a notification through IFTTT!")
                push.push_to_ifttt(ifttt_name, api_key, "Testing IFTTT")
                ifttt_work = curate_input("Did you get the notification? (y/n) ", ("y", "n"))
                if ifttt_work == "y":
                    ifttt_working = True
                else:
                    failed_attempts += 1
                    if failed_attempts >= 3:
                        print("Too many failed attempts, exiting script, try again later!")
                        exit(1)
                    print("Check to make sure you followed the steps correctly and try again.")

            except requests.exceptions.ConnectionError:
                print("No internet, try reconnecting and running the script again!")
                exit(1)
        json_data["ifttt_event"] = ifttt_name
    
    json_data["house_address"] = input("Enter a description of the run location (used to tell you in the "
                                       "{} where the outage happened): ".format(notification_type))
    with open(os.path.join(config_path, 'config.json'), 'w+') as json_file:
        json.dump(json_data, json_file)

    crontab_edit = curate_input("Would you like to setup the script to run automatically "
                                "(it will run at boot time and at 5 minute intervals)? (y/n)", ("y", "n"))
    if crontab_edit == "y":
        exec_path = os.path.join(os.path.dirname(sys.executable), "outage_detector")
        cron_scheduling.schedule_job(exec_path, "--run scheduled --notify {}".format(notification_type), log_path, 5)
        cron_scheduling.schedule_job(exec_path, "--run boot --notify {}".format(notification_type), log_path,
                                     at_boot=True)
Пример #14
0
def main():
    """
    Main processing loop
    """

    # pylint: disable=global-statement
    # use of global statement here is required to allow main() to set the value based on passed arguments to the program

    global DEBUG, INFLUX_PASSWORD

    # Get the passwords from the plain text keyring
    keyring.set_keyring(PlaintextKeyring())
    mqtt_password = keyring.get_password(MQTT_HOST, MQTT_USER)
    INFLUX_PASSWORD = keyring.get_password(INFLUX_HOST, INFLUX_USER)

    try:
        pid_file = os.environ['PIDFILE']
    except:
        pid_file = "null"

    args = parse_args()

    # Setup logging

    if args.D:
        DEBUG = True
        set_logging('debug')
        logging.debug("Running in debug mode, not writing data")
    else:
        DEBUG = False
        set_logging('info')
        if os.path.exists(pid_file):
            logging.error("PID already exists. Is getsolar already running?")
            logging.error(
                "Either, stop the running process or remove %s or run with the debug flag set (-D)",
                pid_file)
            sys.exit(2)
        else:
            write_pid_file(pid_file)

    # Connect to MQTT

    m_d = mqtt.Client(MQTT_CLIENT_NAME)
    m_d.connected_flag = False
    m_d.error_code = 0
    m_d.on_connect = on_connect  # bind call back function
    m_d.on_disconnect = on_disconnect
    m_d.on_log = on_log
    m_d.username_pw_set(MQTT_USER, mqtt_password)
    m_d.connect(MQTT_HOST, int(MQTT_PORT))
    m_d.loop_start()

    retry = MAX_RETRIES
    while not m_d.connected_flag:
        if retry == 0:
            # wait in loop for MAX_RETRIES
            sys.exit("Connect failed with error", m_d.error_code)
        else:
            if m_d.error_code == 5:
                sys.exit("Authorisation Failure" + mqtt_password)
            time.sleep(1)
            retry -= 1

    # Connect to two InfluxDB databases
    #   DB 1 = Home Assistant database for one minute logging of power and energy data
    #   DB 2 = Powerlogging for 10s logging of power only

    d_d = InfluxDBClient(INFLUX_HOST, INFLUX_PORT, INFLUX_USER,
                         INFLUX_PASSWORD, INFLUX_DB_ALL)
    d_p = InfluxDBClient(INFLUX_HOST, INFLUX_PORT, INFLUX_USER,
                         INFLUX_PASSWORD, INFLUX_DB_POWER)

    inv_data = InverterData()

    # Initialise cycle counter and number of retries

    counter = MAX_COUNTER
    retry = MAX_RETRIES

    # Connect to solaredge modbus inverter

    logging.debug("Connect to device. Host " + args.i + " Port " +
                  str(args.p) + " Timeout " + str(args.t) + " Unit " +
                  str(args.u))
    s_d = solaredge_modbus.Inverter(host=args.i,
                                    port=args.p,
                                    timeout=args.t,
                                    unit=args.u)
    # s_d.connect()

    # Try up to MAX_RETRIES times to read data from the inverter

    while retry != 0:
        if not s_d.connected():
            retry -= 1
            time.sleep(WAIT_TIME)
            logging.debug("Retry. Connect to device. Host " + args.i +
                          " Port " + str(args.p) + " Timeout " + str(args.t) +
                          " Unit " + str(args.u))
            s_d = solaredge_modbus.Inverter(host=args.i,
                                            port=args.p,
                                            timeout=args.t,
                                            unit=args.u)
        else:
            retry = MAX_RETRIES
            # Read registers
            logging.debug("Reading data - cycle %s", counter)
            inv_data.update(s_d)
            inv_data.write_power(d_p)
            if counter == 0:
                inv_data.write_ha(m_d, d_d)
                counter = 5
            else:
                counter -= 1
            time.sleep(SLEEP_TIME)
    logging.error("Too many retries")
    rm_pid_file(pid_file)
    sys.exit(2)