Beispiel #1
0
    def read(self):
        """Parse and validate the config file. The read data is accessible as a dictionary in this instance

        :return: None
        """
        try:
            data = load(open(self.file), Loader)
        except (UnicodeDecodeError, YAMLError) as e:
            raise InvalidConfig(self.file, '{}'.format(e))
        try:
            validate(data, SCHEMA)
        except ValidationError as e:
            raise InvalidConfig(self.file, e)
        self.update(data)
Beispiel #2
0
    def get_url(self):
        """IFTTT Webhook url

        :return: url
        :rtype: str
        """
        if not self.data[self.execute_name]:
            raise InvalidConfig(extra_body='Value for IFTTT is required on {} device. Get your key here: '
                                           'https://ifttt.com/services/maker_webhooks/settings'.format(self.name))
        if not self.data.get('event'):
            raise InvalidConfig(extra_body='Event option is required for IFTTT on {} device. '
                                           'You define the event name when creating a Webhook '
                                           'applet'.format(self.name))
        url = self.url_pattern.format(event=self.data['event'], key=self.data[self.execute_name])
        return url
Beispiel #3
0
def execute_over_ssh(cmd, ssh, cwd=None, shell='bash'):
    """Excecute command on remote machine using SSH

    :param cmd: Command to execute
    :param ssh: Server to connect. Port is optional
    :param cwd: current working directory
    :return: None
    """
    port = None
    parts = ssh.split(':', 1)
    if len(parts) > 1 and not parts[1].isdigit():
        raise InvalidConfig(extra_body='Invalid port number on ssh config: {}'.
                            format(parts[1]))
    elif len(parts) > 1:
        port = parts[1]
    quoted_cmd = ' '.join(
        [x.replace("'", """'"'"'""") for x in cmd.split(' ')])
    remote_cmd = ' '.join(
        [
            ' '.join(get_shell(shell)),  # /usr/bin/env bash
            ' '.join([
                EXECUTE_SHELL_PARAM, "'",
                ' '.join((['cd', cwd, ';'] if cwd else []) + [quoted_cmd]), "'"
            ])
        ], )
    return ['ssh', parts[0]
            ] + (['-p', port] if port else []) + ['-C'] + [remote_cmd]
Beispiel #4
0
    def execute(self, root_allowed=False):
        """Execute using self.data

        :param bool root_allowed: Allow execute as root commands
        :return:
        """
        if self.user == ROOT_USER and not root_allowed and not self.data.get(
                'ssh'):
            raise SecurityException(
                'For security, execute commands as root is not allowed. '
                'Use --root-allowed to allow executing commands as root. '
                ' It is however recommended to add a user to the configuration '
                'of the device (device: {})'.format(self.name))
        if self.data.get('user') and self.data.get('ssh'):
            raise InvalidConfig(
                'User option is unsupported in ssh mode. The ssh user must be defined in '
                'the ssh option. For example: user@machine')
        if self.data.get('ssh'):
            cmd = execute_over_ssh(self.data['cmd'], self.data['ssh'],
                                   self.data.get('cwd'))
            output = execute_cmd(cmd)
        else:
            cmd = run_as_cmd(self.data['cmd'], self.user)
            output = execute_cmd(cmd, self.data.get('cwd'))
        if output:
            return output[0]
Beispiel #5
0
def get_confirmation(device_id, device_data, confirmations):
    name = device_data.get('confirmation')
    if name and name not in confirmations:
        raise InvalidConfig(
            extra_body='{} is not a registered confirmation config on {} device'
            .format(name, device_id))
    if name:
        return get_confirmation_instance(confirmations[name])
    defaults = list(
        filter(lambda x: x.get('is_default'), confirmations.values()))
    if len(defaults) > 1:
        raise InvalidConfig(
            extra_body='Multiple default confirmations. There can be only one.'
        )
    if defaults:
        return get_confirmation_instance(defaults[0])
Beispiel #6
0
 def __init__(self, data):
     for key in self.required_fields:
         if key not in data:
             raise InvalidConfig(
                 extra_body='{} is a required parameter for {} confirmation'
                 .format(key, self.name))
     self.data = data
Beispiel #7
0
    def __init__(self, src, data=None, config=None):
        """

        :param str src: Mac address
        :param data: device data
        """
        data = data or {}
        config = config or {}

        if isinstance(src, Device):
            src = src.src
        self.src = src.lower()
        self.data = data
        execs = [
            cls(self.name, data) for name, cls in EXECUTE_CLS.items()
            if name in self.data
        ]
        if len(execs) > 1:
            raise InvalidConfig(
                extra_body=
                'There can only be one method of execution on a device. The device is {}. '
                .format(self.name))
        elif len(execs):
            self.execute_instance = execs[0]
            self.execute_instance.validate()
        self.confirmation = get_confirmation(src, data,
                                             config.get('confirmations', {}))
Beispiel #8
0
 def __init__(self, data):
     one_fields = set(data) & self.one_field_of
     if len(one_fields) > 1:
         raise InvalidConfig(
             extra_body='Only one in {} is required for {} notifications'.
             format(', '.join(one_fields), self.name))
     elif one_fields:
         self.to_field = one_fields.pop()
     super(PushbulletConfirmation, self).__init__(data)
Beispiel #9
0
    def get_url(self):
        """Open Hab url

        :return: url
        :rtype: str
        """
        url = super(ExecuteOpenHab, self).get_url()
        if not self.data.get('item'):
            raise InvalidConfig(extra_body='Item option is required for Open Hab on {} device.'.format(self.name))
        url += '/rest/items/{}'.format(self.data['item'])
        return url
Beispiel #10
0
    def get_url(self):
        """Home assistant url

        :return: url
        :rtype: str
        """
        url = super(ExecuteHomeAssistant, self).get_url()
        if not self.data.get('event'):
            raise InvalidConfig(extra_body='Event option is required for HomeAsistant on {} device.'.format(self.name))
        url += '/api/events/{}'.format(self.data['event'])
        return url
Beispiel #11
0
    def validate(self):
        """Check self.data. Raise InvalidConfig on error

        :return: None
        """
        if (self.data.get('content-type') or self.data.get('body')) and \
                self.data.get('method', '').lower() not in CONTENT_TYPE_METHODS:
            raise InvalidConfig(
                extra_body='The body/content-type option only can be used with the {} methods. The device is {}. '
                           'Check the configuration file.'.format(', '.join(CONTENT_TYPE_METHODS), self.name)
            )
        self.data['content-type'] = CONTENT_TYPE_ALIASES.get(self.data.get('content-type'),
                                                             self.data.get('content-type'))
        form_type = CONTENT_TYPE_ALIASES['form']
        if self.data.get('body') and (self.data.get('content-type') or form_type) == form_type:
            try:
                self.data['body'] = json.loads(self.data['body'])
            except JSONDecodeError:
                raise InvalidConfig(
                    extra_body='Invalid JSON body on {} device.'.format(self.name)
                )
Beispiel #12
0
    def get_url(self):
        """Home assistant url

        :return: url
        :rtype: str
        """
        url = self.data['homeassistant']
        parsed = urlparse(url)
        if not parsed.scheme:
            url = 'http://{}'.format(url)
        if not url.split(':')[-1].isalnum():
            url += ':8123'
        if not self.data.get('event'):
            raise InvalidConfig(
                extra_body=
                'Event option is required for HomeAsistant on {} device.'.
                format(self.name))
        url += '/api/events/{}'.format(self.data['event'])
        return url
Beispiel #13
0
def get_confirmation_instance(confirmation_data):
    confirmation_data = confirmation_data.copy()
    if confirmation_data.get('service') not in CONFIRMATIONS:
        raise InvalidConfig(extra_body='{} is a invalid confirmation service')
    return CONFIRMATIONS[confirmation_data.pop('service')](confirmation_data)