Esempio n. 1
0
    def validate_scene(self, data: Optional[dict] = None):
        """ Validates the scene (node.data). Removes attributes that are not allowed. """
        validate_only = False
        if data is None:
            data = self.data
            validate_only = True

        for field in SCENE_DATA_COMPONENTS:
            if field not in data or isinstance(data[field], dict) is False:
                data[field] = {}

        if "allow_intents" not in data["configs"]:
            data["configs"]["allow_intents"] = True
        if isinstance(data["configs"]["allow_intents"], bool) is False:
            data["configs"]["allow_intents"] = is_true_false(
                data["configs"]["allow_intents"])
        if "description" not in data["configs"]:
            data["configs"]["description"] = self.label
        if "run_on_start" not in data["configs"]:
            data["configs"]["run_on_start"] = False
        if isinstance(data["configs"]["run_on_start"], bool) is False:
            data["configs"]["run_on_start"] = is_true_false(
                data["configs"]["run_on_start"])
        if "run_on_start_forced" not in data["configs"]:
            data["configs"]["run_on_start_forced"] = False
        if isinstance(data["configs"]["run_on_start_forced"], bool) is False:
            data["configs"]["run_on_start_forced"] = is_true_false(
                data["configs"]["run_on_start_forced"])

        for action_id, action in data["actions"].items():
            self.validate_action(action_id,
                                 action,
                                 validate_only=validate_only)

        return self.sort_actions(data)
 def is_direct_controllable(self):
     """
     Return true if this device can be directly controlled. This is usally True, except for instances
     like relays that control garage doors, etc.
     :return:
     """
     if self.has_device_feature(FEATURE_CONTROLLABLE) and \
                 self.has_device_feature(FEATURE_ALLOW_DIRECT_CONTROL) and \
                 is_true_false(self.allow_direct_control) and \
                 is_true_false(self.controllable):
         return True
     return False
Esempio n. 3
0
    def load_attribute_values_pre_process(self, incoming: dict) -> None:
        """ Setup basic class attributes based on incoming data. """

        self.__dict__["callbacks"] = {
            "broadcast": [],
            "accepted": [],
            "sent": [],
            "received": [],
            "pending": [],
            "failed": [],
            "canceled": [],
            "done": [],
        }

        # Default values
        self.__dict__["started"] = False
        self.__dict__["status"] = DEVICE_COMMAND_STATUS_NEW
        self.__dict__["created_at"] = time()
        self.__dict__["call_later"] = None
        self.__dict__["started"] = incoming.get("started", False)
        self.__dict__["idempotence"] = incoming.get("idempotence", None)
        self.__dict__["history"] = []
        self.__dict__["started"] = False
        self.__dict__["uploadable"] = True  # Todo: Make this settable.
        if "uploaded" in incoming:
            self.__dict__["uploaded"] = is_true_false(incoming["uploaded"], only_bool=True)
        else:
            self.__dict__["uploaded"] = False
        self.gateway_id = incoming.get("gateway_id", self._gateway_id)
        if self.gateway_id is None:
            self.gateway_id = self._gateway_id

        self.update_attributes_pre_process(incoming)
        def page_automation_add_get(webinterface, request, session):
            session.has_access("automation", "*", "add", raise_error=True)
            data = {
                "label":
                webinterface.request_get_default(request, "label", ""),
                "machine_label":
                webinterface.request_get_default(request, "machine_label", ""),
                "description":
                webinterface.request_get_default(request, "description", ""),
                "run_on_start":
                webinterface.request_get_default(request, "run_on_start",
                                                 True),
                "status":
                int(webinterface.request_get_default(request, "status", 1)),
            }
            try:
                data["run_on_start"] = is_true_false(data["run_on_start"])
            except:
                webinterface.add_alert(
                    "Cannot add automation rule. run_on_start must be either true or false.",
                    "warning")
                return page_automation_form(
                    webinterface,
                    request,
                    session,
                    "add",
                    data,
                    "Add Automation Rule",
                )

            root_breadcrumb(webinterface, request)
            webinterface.add_breadcrumb(request, "/automation/add", "Add")
            return page_automation_form(webinterface, request, session, "add",
                                        data, "Add Automation Rule")
Esempio n. 5
0
    def add(self, label, machine_label, description, status):
        """
        Add new scene.

        :param label:
        :param machine_label:
        :return:
        """
        self.check_duplicate_scene(label, machine_label)
        data = {
            "actions": {},
            "config": {
                "description": description,
                "enabled": is_true_false(status),
            },
        }
        new_scene = yield self._Nodes.create(label=label,
                                             machine_label=machine_label,
                                             node_type="scene",
                                             data=data,
                                             data_content_type="json",
                                             gateway_id=self.gateway_id,
                                             destination="gw",
                                             status=1)
        self.patch_scene(new_scene)
        self.scenes[new_scene.node_id] = new_scene
        reactor.callLater(
            0.001,
            global_invoke_all,
            "_scene_added_",
            called_by=self,
            scene_id=new_scene.node_id,
            scene=new_scene,
        )
        return new_scene
 def is_controllable(self):
     """
     Returns True if the device can be controlled. This will be False for input type devices, like motion sensors.
     :return:
     """
     if self.has_device_feature(FEATURE_ALLOW_DIRECT_CONTROL) and \
                 is_true_false(self.controllable):
         return True
     return False
Esempio n. 7
0
 def set_fake_data(self, value):
     """ If data is fake, we won't sync to anywhere. """
     if isinstance(is_true_false(value), bool):
         self.__dict__["_fake_data"] = value
         if value is True:
             self.stop_data_sync()
         else:
             self.start_data_sync()
         return True
Esempio n. 8
0
    def basic_values_run_filter_callback(self, rule, portion, new_value,
                                         **kwargs):
        """
        A callback to check if a provided condition is valid before being added as a possible condition.

        :param rule: The rule. We don't use this here.
        :param kwargs: None
        :return:
        """
        filter_value = portion['filter']['value']

        # logger.debug("basic_values_run_filter_callback rule name: {name}", name=rule['name'])
        # logger.debug("Checking new = filter: {new_value} = {filter_value}",
        #             new_value=new_value,
        #             filter_value=filter_value)
        # logger.debug("Checking as bools: {new_value} = {filter_value}",
        #             new_value=is_true_false(new_value),
        #             filter_value=is_true_false(filter_value))

        if 'operator' in portion['filter']:
            op_func = ops[portion['filter']['operator']]
        else:
            op_func = ops['==']

        if op_func(new_value, filter_value):
            return True
        else:
            if new_value == filter_value:
                return True
            else:
                try:
                    logger.debug(
                        "basic_values_run_filter_callback - checking if values match as a bool"
                    )
                    logger.debug(
                        "basic_values_run_filter_callback as bool: {result}",
                        result=op_func(is_true_false(new_value),
                                       is_true_false(filter_value)))
                    return op_func(is_true_false(new_value),
                                   is_true_false(filter_value))
                except:
                    return False
        return False
        def page_automation_edit_rule_post(webinterface, request, session,
                                           rule_id):
            session.has_access("automation", rule_id, "edit", raise_error=True)
            data = {
                "label":
                webinterface.request_get_default(request, "label", ""),
                "machine_label":
                webinterface.request_get_default(request, "machine_label", ""),
                "description":
                webinterface.request_get_default(request, "description", ""),
                "run_on_start":
                webinterface.request_get_default(request, "run_on_start",
                                                 True),
                "status":
                int(webinterface.request_get_default(request, "status", 1)),
                "rule_id":
                rule_id,
            }
            try:
                data["run_on_start"] = is_true_false(data["run_on_start"])
            except:
                webinterface.add_alert(
                    "Cannot add automation rule. run_on_start must be either true or false.",
                    "warning")
                return page_automation_form(
                    webinterface,
                    request,
                    session,
                    "add",
                    data,
                    "Add Automation rule",
                )

            try:
                rule = webinterface._Automation.edit(rule_id, data["label"],
                                                     data["machine_label"],
                                                     data["description"],
                                                     data["status"],
                                                     data["run_on_start"])
            except YomboWarning as e:
                webinterface.add_alert(
                    f"Cannot edit automation rule. {e.message}", "warning")
                root_breadcrumb(webinterface, request)
                webinterface.add_breadcrumb(
                    request, f"/automation/{rule.rule_id}/details", rule.label)
                webinterface.add_breadcrumb(
                    request, f"/automation/{rule.rule_id}/edit", "Edit")

                return page_automation_form(
                    webinterface, request, session, "edit", data,
                    "Edit Automation rule: {rule.label}")

            webinterface.add_alert(f"Automation rule '{rule.label}' edited.")
            return webinterface.redirect(
                request, f"/automation/{rule.rule_id}/details")
Esempio n. 10
0
    def convert_to_human(self, value, value_type):
        if value_type == 'bool':
            results = is_true_false(value)
            if results is not None:
                return results
            else:
                return value

        elif value_type == 'epoch':
            return epoch_to_string(value)
        else:
            return value
Esempio n. 11
0
    def convert_to_human(self, value, value_type):
        if value_type == "bool":
            results = is_true_false(value)
            if results is not None:
                return results
            else:
                return value

        elif value_type == "epoch":
            return epoch_to_string(value)
        else:
            return value
Esempio n. 12
0
        def page_automation_add_post(webinterface, request, session):
            session.has_access("automation", "*", "add", raise_error=True)
            data = {
                "label":
                webinterface.request_get_default(request, "label", ""),
                "machine_label":
                webinterface.request_get_default(request, "machine_label", ""),
                "description":
                webinterface.request_get_default(request, "description", ""),
                "run_on_start":
                webinterface.request_get_default(request, "run_on_start",
                                                 "true"),
                "status":
                int(webinterface.request_get_default(request, "status", 1)),
            }

            try:
                data["run_on_start"] = is_true_false(data["run_on_start"])
            except:
                webinterface.add_alert(
                    "Cannot add automation rule. run_on_start must be either true or false.",
                    "warning")
                return page_automation_form(
                    webinterface,
                    request,
                    session,
                    "add",
                    data,
                    "Add Automation rule",
                )
            try:
                rule = yield webinterface._Automation.add(
                    data["label"], data["machine_label"], data["description"],
                    data["status"], data["run_on_start"])
            except YomboWarning as e:
                webinterface.add_alert(
                    f"Cannot add automation rule. {e.message}", "warning")
                return page_automation_form(
                    webinterface,
                    request,
                    session,
                    "add",
                    data,
                    "Add Automation rule",
                )

            webinterface.add_alert(
                f"New automation rule '{rule.label}' added.")
            return webinterface.redirect(
                request, f"/automation/{rule.rule_id}/details")
Esempio n. 13
0
    def field_remap(self, data, config_data):
        new_data = {}
        table_meta = self._LocalDB.db_model[config_data["table"]]
        key = None
        value = None
        try:
            for key, value in data.items(
            ):  # we must re-map AMQP names to local names.  Removes ones without a DB column too.
                if key in config_data["map"]:
                    # Convert ints and floats.
                    if value is None:
                        pass
                    elif table_meta[config_data["map"]
                                    [key]]["type"] == "INTEGER":
                        value = int(value)
                    elif table_meta[config_data["map"][key]]["type"] == "REAL":
                        value = float(value)
                    elif table_meta[config_data["map"]
                                    [key]]["type"] == "BOOLEAN":
                        value = is_true_false(value)

                    if key == "energy_map":
                        if isinstance(value, dict):
                            try:
                                value = json.dumps(value)
                            except Exception as e:
                                value = '{"0.0":0,"1.0":0}'
                    new_data[config_data["map"][key]] = value

                else:
                    new_data[key] = value
            return new_data
        except Exception as e:
            print(f"error in field remap.  Last key: {key}")
            print(f"input value: {value}")
            print(f"field remap - config_data = {config_data}")
            print(f"field remap - data = {data}")
            print(f"tablemeta: {table_meta}")
            logger.error("--------==(Error: {e})==--------", e=e)
            logger.error(
                "--------------------------------------------------------")
            logger.error("{error}", error=sys.exc_info())
            logger.error(
                "---------------==(Traceback)==--------------------------")
            logger.error("{trace}", trace=traceback.print_exc(file=sys.stdout))
            logger.error(
                "--------------------------------------------------------")
Esempio n. 14
0
    def convert_to_human(self, value, value_type):
        """
        Convert various value types to a more human friendly display.

        :param value:
        :param value_type:
        :return:
        """
        if value_type == "bool":
            results = is_true_false(value)
            if results is not None:
                return results
            else:
                return value
        elif value_type == "epoch":
            return converters.epoch_to_string(value)
        else:
            return value
Esempio n. 15
0
    def edit(self,
             label=None,
             machine_label=None,
             description=None,
             status=None,
             allow_intents=None):
        """
        Edit various scene attributes. This anti-pythonic due to syncing back to to yombo api and the database.

        :param scene_id:
        :param label:
        :param machine_label:
        :param description:
        :param status:
        :return:
        """
        if label is not None and machine_label is not None:
            self._Scenes.check_duplicate_scene(label, machine_label,
                                               self.node_id)

        updates = {}
        if label is not None:
            updates["label"] = label
        if machine_label is not None:
            updates["machine_label"] = label
        if description is not None:
            self.data["configs"]["description"] = description
        if status is not None:
            updates["status"] = 1 if is_true_false(status,
                                                   only_bool=True) else 0
        if allow_intents is not None:
            self.data["configs"]["allow_intents"] = allow_intents

        self.update(updates)
        reactor.callLater(0.001,
                          global_invoke_all,
                          "_scene_edited_",
                          called_by=self,
                          arguments={
                              "scene_id": self.node_id,
                              "scene": self,
                          })
Esempio n. 16
0
    def edit(self,
             scene_id,
             label=None,
             machine_label=None,
             description=None,
             status=None,
             allow_intents=None):
        """
        Edit a scene label and machine_label.

        :param scene_id:
        :param label:
        :param machine_label:
        :param description:
        :param status:
        :return:
        """
        if label is not None and machine_label is not None:
            self.check_duplicate_scene(label, machine_label, scene_id)

        scene = self.get(scene_id)
        if label is not None:
            scene.label = label
        if machine_label is not None:
            scene.machine_label = machine_label
        if description is not None:
            scene.data["config"]["description"] = description
        if status is not None:
            scene.status = is_true_false(status)
            scene.data["config"]["enabled"] = scene.status
        if allow_intents is not None:
            scene.data["config"]["allow_intents"] = allow_intents

        reactor.callLater(
            0.001,
            global_invoke_all,
            "_scene_edited_",
            called_by=self,
            scene_id=scene_id,
            scene=scene,
        )
        return scene
Esempio n. 17
0
def coerce_value(value, value_type):
    """
    Convert a value to it's intended type. Typically used when loading data from databases.

    :param value:2502jXYQR1fcSyPc
    :param value_type: one of - string, bool, int, float, epoch. Unknowns will be converted to strings.
    :return:
    """
    if value_type is None:
        return value
    if value_type.lower() in ("str", "string"):
        return str(value)
    elif value_type.lower() in ("int", "integer", "epoch", "number"):
        return int(value)
    elif value_type.lower() == "float":
        return float(value)
    elif value_type.lower() in ("bool", "boolean"):
        return is_true_false(value)
    else:
        return value
Esempio n. 18
0
def coerce_value(value, value_type):
    """
    Convert a value to it's intended type. Typically used when loading data from databases.

    :param value:2502jXYQR1fcSyPc
    :param value_type: one of - string, bool, int, float, epoch. Unknowns will be converted to strings.
    :return:
    """
    if value_type is None:
        return value
    if value_type.lower() in ('str', 'string'):
        return str(value)
    elif value_type.lower() in ('int', 'integer', 'epoch', 'number'):
        return int(value)
    elif value_type.lower() == 'float':
        return float(value)
    elif value_type.lower() == 'bool':
        return is_true_false(value)
    else:
        return value
Esempio n. 19
0
        def page_setup_wizard_6_post(webinterface, request, session):
            """
            Last step is to handle the GPG key. One of: create a new one, import one, or select an existing one.

            :param webinterface:
            :param request:
            :param session:
            :return:
            """
            result_output = ""
            if session.get('setup_wizard_last_step', 1) not in (5, 6):
                webinterface.add_alert(
                    "Invalid wizard state. Please don't use the browser forward or back buttons."
                )
                return webinterface.redirect(request, '/setup_wizard/1')

            session.set('setup_wizard_6_post', 1)
            try:
                submitted_gpg_action = request.args.get('gpg_action')[
                    0]  # underscore here due to jquery
            except:
                webinterface.add_alert(
                    "Please select an appropriate GPG/PGP Key action.")
                return webinterface.redirect(request, '/setup_wizard/5')

            if session['setup_wizard_gateway_id'] == 'new':
                data = {
                    'machine_label':
                    session['setup_wizard_gateway_machine_label'],
                    'label': session['setup_wizard_gateway_label'],
                    'description': session['setup_wizard_gateway_description'],
                }
                try:
                    results = yield webinterface._YomboAPI.request(
                        'POST', '/v1/gateway', data,
                        session['yomboapi_session'])
                except YomboWarning as e:
                    webinterface.add_alert(e.html_message, 'warning')
                    return webinterface.redirect(request, '/setup_wizard/3')
                session['setup_wizard_gateway_id'] = results['data']['id']

            else:
                data = {
                    'label': session['setup_wizard_gateway_label'],
                    'description': session['setup_wizard_gateway_description'],
                }
                results = yield webinterface._YomboAPI.request(
                    'PATCH',
                    '/v1/gateway/%s' % session['setup_wizard_gateway_id'])
                if results['code'] > 299:
                    webinterface.add_alert(results['content']['html_message'],
                                           'warning')
                    return webinterface.redirect(request, '/setup_wizard/5')

                results = yield webinterface._YomboAPI.request(
                    'GET', '/v1/gateway/%s/new_hash' %
                    session['setup_wizard_gateway_id'])
                if results['code'] > 299:
                    webinterface.add_alert(results['content']['html_message'],
                                           'warning')
                    return webinterface.redirect(request, '/setup_wizard/5')

            # webinterface._Configs.set('core', 'updated', results['data']['updated_at'])
            # webinterface._Configs.set('core', 'created', results['data']['created_at'])
            # print("new gwid: %s" % results['data']['id'])
            # print("got gwid before set: %s" % webinterface._Configs.get('core', 'gwid'))
            webinterface._Configs.set('core', 'gwid', results['data']['id'])
            # print("got gwid after set: %s" % webinterface._Configs.get('core', 'gwid'))
            webinterface._Configs.set('core', 'gwuuid',
                                      results['data']['uuid'])
            webinterface._Configs.set(
                'core', 'machine_label',
                session['setup_wizard_gateway_machine_label'])
            webinterface._Configs.set('core', 'label',
                                      session['setup_wizard_gateway_label'])
            webinterface._Configs.set(
                'core', 'description',
                session['setup_wizard_gateway_description'])
            webinterface._Configs.set('core', 'gwhash',
                                      results['data']['hash'])
            webinterface._Configs.set(
                'core', 'is_master',
                is_true_false(session['setup_wizard_gateway_is_master']))
            webinterface._Configs.set(
                'core', 'master_gateway',
                session['setup_wizard_gateway_master_gateway'])
            webinterface._Configs.set('security', 'amqpsendstatus',
                                      session['setup_wizard_security_status'])
            webinterface._Configs.set(
                'security', 'amqpsendgpsstatus',
                session['setup_wizard_security_gps_status'])
            webinterface._Configs.set(
                'security', 'amqpsendprivatestats',
                session['setup_wizard_security_send_private_stats'])
            webinterface._Configs.set(
                'security', 'amqpsendanonstats',
                session['setup_wizard_security_send_anon_stats'])
            webinterface._Configs.set('location', 'latitude',
                                      session['setup_wizard_gateway_latitude'])
            webinterface._Configs.set(
                'location', 'longitude',
                session['setup_wizard_gateway_longitude'])
            webinterface._Configs.set(
                'location', 'elevation',
                session['setup_wizard_gateway_elevation'])
            webinterface._Configs.set('core', 'first_run', False)

            # Remove wizard settings...
            for session_key in list(session.keys()):
                if session_key.startswith('setup_wizard_'):
                    del session[session_key]
            session['setup_wizard_done'] = True
            session['setup_wizard_last_step'] = 7

            print("gf 1")
            if submitted_gpg_action == 'new':  # make GPG keys!
                print("gf 2")
                logger.info("New gpg key will be generated on next restart.")
                # reactor.callLater(0.0001, webinterface._GPG.generate_key)
                # yield webinterface._GPG.generate_key()
            elif submitted_gpg_action == 'import':  # make GPG keys!
                try:
                    submitted_gpg_private = request.args.get(
                        'gpg-private-key')[0]
                except:
                    webinterface.add_alert(
                        "When importing, must have a valid private GPG/PGP key."
                    )
                    return webinterface.redirect(request, '/setup_wizard/5')
                try:
                    submitted_gpg_public = request.args.get(
                        'gpg-public-key')[0]
                except:
                    webinterface.add_alert(
                        "When importing, must have a valid public GPG/PGP key."
                    )
                    return webinterface.redirect(request, '/setup_wizard/5')
            else:
                gpg_existing = yield webinterface._LocalDB.get_gpg_key()
                if submitted_gpg_action in gpg_existing:
                    key_ascii = webinterface._GPG.get_key(submitted_gpg_action)
                    webinterface._Configs.set('gpg', 'keyid',
                                              submitted_gpg_action)
                    webinterface._Configs.set('gpg', 'keyascii', key_ascii)
                else:
                    webinterface.add_alert("Existing GPG/PGP key not fount.")
                    return webinterface.redirect(request, '/setup_wizard/5')
            print("gj 1")
            session['gpg_selected'] = submitted_gpg_action

            session['setup_wizard_last_step'] = 6

            print("gj 4")
            results = yield form_setup_wizard_6(webinterface, request, session)
            print("gj 5")
            return results
Esempio n. 20
0
    def new(self, machine_label: str, label: str, description: str, preserve_key: Optional[bool] = None,
            status: Optional[int] = None, roles: Optional[List[str]] = None, request_by: Optional[str] = None,
            request_by_type: Optional[str] = None, request_context: Optional[str] = None,
            authentication: Optional[Type[AuthMixin]] = None, last_access_at: Optional[Union[int, float]] = None,
            auth_key_id: Optional[str] = None, auth_key_id_full: Optional[str] = None,
            load_source: Optional[str] = None, **kwargs) -> AuthKey:
        """
        Create a new auth_key.

        To track how the authkey was created, either request_by and request_by_type or an authentication item
        can be anything with the authmixin, such as a user, websession, or authkey.


        :param machine_label: Authkey machine_label
        :param label: Authkey human label.
        :param description: Authkey description.
        :param preserve_key: If true, the original auth_id will be available from auth_key
        :param status: 0 - disabled, 1 - enabled, 2 - deleted
        :param roles: Assign authkey to a list of roles.
        :param request_by: Who created the Authkey. "alexamodule"
        :param request_by_type: What type of item created it: "module"
        :param request_context: Some additional information about where the request comes from.
        :param authentication: An auth item such as a websession or authkey.
        :param last_access_at: When auth was last used.
        :param auth_key_id: Authkey id to use, not normally set.
        :param auth_key_id_full: Full auth key id to use, not normally set.
        :param load_source: How the authkey was loaded.
        :return:
        """
        preserve_key = is_true_false(preserve_key) or True

        if request_context is None:
            request_context = caller_string()  # get the module/class/function name of caller

        try:
            results = self.get(machine_label)
            raise YomboWarning(
                {
                    "id": results.auth_key_id,
                    "title": "Duplicate entry",
                    "something": "hahahaha",
                    "detail": "An authkey with that machine_label already exists."
                })
        except KeyError as e:
            pass

        logger.debug("authkey new: about to load a new item....: {machine_label}", machine_label=machine_label)
        results = yield self.load_an_item_to_memory(
            {
                "id": auth_key_id,
                "auth_key_id_full": auth_key_id_full,
                "machine_label": machine_label,
                "label": label,
                "description": description,
                "preserve_key": preserve_key,
                "status": status,
                "roles": roles,
                "request_by": request_by,
                "request_by_type": request_by_type,
                "request_context": request_context,
                "last_access_at": last_access_at,
            },
            authentication=authentication,
            load_source=load_source)
        return results
Esempio n. 21
0
    def new(self, title: str, message: str, gateway_id: Optional[str] = None,
            priority: Optional[str] = None, persist: Optional[bool] = None,
            timeout: Optional[Union[int, float]] = None, expire_at: Optional[Union[int, float]] = None,
            always_show: Optional[bool] = None, always_show_allow_clear: Optional[bool] = None,
            notice_type: Optional[str] = None, notice_id: Optional[str] = None, local: Optional[bool] = None,
            targets: Optional[List[str]] = None,
            request_by: Optional[str] = None, request_by_type: Optional[str] = None,
            request_context: Optional[str] = None,
            meta: Optional[dict] = None,
            acknowledged: Optional[bool] = None, acknowledged_at: Optional[int] = None,
            created_at: Optional[Union[int, float]] = None,
            # gateway_id: Optional[str] = None,
            create_event: Optional[bool] = None):
        """
        Add a new notice.

        :param title: Title, or label, for the not
        :returns: Pointer to new notice. Only used during unittest
        """
        if gateway_id is None:
            gateway_id = self._gateway_id
        if priority not in ("low", "normal", "high", "urgent"):
            priority = "normal"
        if persist is None:
            persist = False
        if always_show is None:
            always_show = False
        else:
            always_show = is_true_false(always_show)
        if isinstance(always_show, bool) is False:
            raise YomboWarning(f"always_show must be True or False, got: {always_show}")
        if always_show_allow_clear is None:
            always_show_allow_clear = False
        else:
            always_show_allow_clear = is_true_false(always_show_allow_clear)
        if isinstance(always_show_allow_clear, bool) is False:
            raise YomboWarning("always_show must be True or False.")
        if notice_type is None:
            notice_type = "notice"
        if notice_type not in ("notice"):
            raise YomboWarning("Invalid notification type.")
        if notice_id is None:
            notice_id = random_string(length=50)
        notice_id = self._Hash.sha224_compact(notice_id)
        if local is None:
            local = True
        else:
            local = is_true_false(local)

        if persist is True and always_show_allow_clear is False:
            raise YomboWarning(f"New notification cannot be 'persist'=True and 'always_show_allow_clear'=False..{title}")

        if isinstance(expire_at, int) or isinstance(expire_at, float):
            expire_at = time() + expire_at
        elif isinstance(timeout, int) or isinstance(timeout, float):
            expire_at = time() + timeout
        elif expire_at is not None:
            raise YomboWarning("expire_at must be int or float.")
        elif timeout is not None:
            raise YomboWarning("timeout must be int or float.")
        if persist is True and expire_at is None:
                expire_at = time() + 60*60*24*30  # keep persistent notifications for 30 days.

        if targets is not None:  # tags on where to send notifications
            if isinstance(targets, list) is False:
                if isinstance(targets, str):
                    targets = [targets]
                else:
                    raise YomboWarning("targets argument must be a list of strings.")
            for target in targets:
                if isinstance(target, str) is False:
                    raise YomboWarning("targets argument must be a list of strings.")

        if created_at is None:
            created_at = time()

        if acknowledged is None:
            acknowledged = False
        else:
            acknowledged = is_true_false(acknowledged)

        if isinstance(acknowledged_at, float):
            acknowledged_at = int(acknowledged_at)
        if acknowledged is True and acknowledged_at is None:
            acknowledged_at = int(time)

        notice = {
            "id": notice_id,
            "title": title,
            "message": message,
            "gateway_id": gateway_id,
            "priority": priority,
            "persist": persist,
            "always_show": always_show,
            "always_show_allow_clear": always_show_allow_clear,
            "type": notice_type,
            "local": local,
            "targets": targets,
            "request_by": request_by,
            "request_by_type": request_by_type,
            "request_context": request_context,
            "meta": meta,
            "acknowledged": acknowledged,
            "acknowledged_at": acknowledged_at,
            "expire_at": expire_at,
            "created_at": created_at,
        }
        if notice_id in self.notifications:
            del notice["id"]
            self.notifications[notice_id].update(notice)
            return self.notifications[notice_id]

        logger.debug("notice: {notice}", notice=notice)

        notification = yield self.load_an_item_to_memory(notice, load_source="local")

        reactor.callLater(.0001,
                          global_invoke_all,
                          "_notification_new_",
                          called_by=self,
                          arguments={
                              "notification": notification,
                              "target": targets,
                              "event": notification.to_dict(),
                              }
                          )
        return notification
Esempio n. 22
0
    def add(self, notice, from_db=None, create_event=None):
        """
        Add a new notice.

        :param notice: A dictionary containing notification details.
        :type record: dict
        :returns: Pointer to new notice. Only used during unittest
        """
        if "title" not in notice:
            raise YomboWarning("New notification requires a title.")
        if "message" not in notice:
            raise YomboWarning("New notification requires a message.")

        if "id" not in notice:
            notice["id"] = random_string(length=16)
        else:
            if notice["id"] in self.notifications:
                self.notifications[notice["id"]].update(notice)
                return notice["id"]

        if "type" not in notice:
            notice["type"] = "notice"
        if "gateway_id" not in notice:
            notice["gateway_id"] = self.gateway_id
        if "priority" not in notice:
            notice["priority"] = "normal"
        if "source" not in notice:
            notice["source"] = ""
        if "always_show" not in notice:
            notice["always_show"] = False
        else:
            notice["always_show"] = is_true_false(notice["always_show"])
        if "always_show_allow_clear" not in notice:
            notice["always_show_allow_clear"] = True
        else:
            notice["always_show_allow_clear"] = is_true_false(
                notice["always_show_allow_clear"])
        if "persist" not in notice:
            notice["persist"] = False
        if "meta" not in notice:
            notice["meta"] = {}
        if "user" not in notice:
            notice["user"] = None
        if "targets" not in notice:  # tags on where to send notifications
            notice["targets"] = []
        if isinstance(notice["targets"], str):
            notice["targets"] = [notice["targets"]]
        if "local" not in notice:
            notice["local"] = False

        if notice["persist"] is True and "always_show_allow_clear" is True:
            YomboWarning(
                "New notification cannot have both 'persist' and 'always_show_allow_clear' set to true."
            )

        if "expire_at" not in notice:
            if "timeout" in notice:
                notice["expire_at"] = time() + notice["timeout"]
            else:
                notice["expire_at"] = time(
                ) + 60 * 60 * 24 * 30  # keep persistent notifications for 30 days.
        else:
            if notice["expire_at"] == None:
                if notice["persist"] == True:
                    YomboWarning("Cannot persist a non-expiring notification")
            elif notice["expire_at"] > time():
                YomboWarning(
                    "New notification is set to expire before current time.")
        if "created_at" not in notice:
            notice["created_at"] = time()

        if "acknowledged" not in notice:
            notice["acknowledged"] = False
        else:
            if notice["acknowledged"] not in (True, False):
                YomboWarning(
                    "New notification 'acknowledged' must be either True or False."
                )

        if "acknowledged_at" not in notice:
            notice["acknowledged_at"] = None

        logger.debug("notice: {notice}", notice=notice)

        self.notifications.prepend(notice["id"], Notification(self, notice))

        for target in notice["targets"]:
            reactor.callLater(.0001,
                              global_invoke_all,
                              "_notification_target_",
                              called_by=self,
                              notification=self.notifications[notice["id"]],
                              target=target,
                              event=self.notifications[notice["id"]].asdict())
        return notice["id"]
Esempio n. 23
0
    def __init__(self, data, parent, start=None):
        """
        Get the instance setup.

        :param data: Basic details about the device command to get started.
        :param parent: A pointer to the device types instance.
        """
        # print("new device_comamnd: %s" % data)
        self._status = 0
        self._Parent = parent
        self.source_gateway_id = data.get('source_gateway_id',
                                          self._Parent.gateway_id)
        self.local_gateway_id = self._Parent.gateway_id
        self.request_id = data['request_id']
        if 'device' in data:
            self.device = data['device']
        elif 'device_id' in data:
            self.device = parent.get(data['device_id'])
        else:
            raise ValueError("Must have either device reference, or device_id")
        if 'command' in data:
            self.command = data['command']
        elif 'command_id' in data:
            self.command = parent._Commands.get(data['command_id'])
        else:
            raise ValueError(
                "Must have either command reference, or command_id")
        self.inputs = data.get('inputs', None)
        if 'history' in data:
            self.history = data['history']
        else:
            self.history = []
        self.requested_by = data['requested_by']
        self.status = data.get('status', 'new')

        self.command_status_received = is_true_false(
            data.get(
                'command_status_received',
                False))  # if a status has been reported against this request
        self.persistent_request_id = data.get('persistent_request_id', None)
        self.broadcast_at = data.get(
            'broadcast_at', None)  # time when command was sent through hooks.
        self.accepted_at = data.get(
            'accepted_at',
            None)  # when a module accepts the device command for processing
        self.sent_at = data.get(
            'sent_at', None
        )  # when a module or receiver sent the command to final end-point
        self.received_at = data.get(
            'received_at',
            None)  # when the command was received by the final end-point
        self.pending_at = data.get(
            'pending_at', None
        )  # if command takes a while to process time, this is the timestamp of last update
        self.finished_at = data.get(
            'finished_at', None
        )  # when the command is finished and end-point has changed state
        self.not_before_at = data.get('not_before_at', None)
        self.not_after_at = data.get('not_after_at', None)
        self.pin = data.get('pin', None)
        self.call_later = None
        self.created_at = data.get('created_at', time())
        self._dirty = is_true_false(data.get('dirty', True))
        self._source = data.get('_source', None)
        self.started = data.get('started', False)

        if self._source == 'database':
            self._dirty = False
            self._in_db = True
        elif self._source == 'gateway_coms':
            self._dirty = False
            self._in_db = False
            reactor.callLater(1, self.check_if_device_command_in_database)

        else:
            self.history.append((self.created_at, self.status, 'Created.',
                                 self.local_gateway_id))
            self._in_db = False

        if self.device.gateway_id == self.local_gateway_id:
            # print("I should start....")
            self.started = False
            start = True

        if start is None or start is True:
            reactor.callLater(0.001, self.start)
Esempio n. 24
0
    def _init_(self, **kwargs):
        """
        Gets various configuration items and determines if the system is running for the first time,
        needs configuration setup, or should run as normal.

        It also validates that the system has a valid API login and API session. This is used to interact
        with the Yombo API cloud.
        :param kwargs:
        :return:
        """
        self.configs_needed = []
        self.configs_needed_human = []
        self.gwid = self._Configs.get("core.gwid", "local", False)
        self.gwhash = self._Configs.get("core.gwhash", None, False)
        self.has_valid_gw_auth = False

        if self._Loader.operating_mode == "first_run" or self._Configs.get(
                "core.first_run", False, False):
            self._Loader.operating_mode = "first_run"
            self.configs_needed = ['gwid', 'gwhash']
            return

        if self.gwid is None or self.gwid == "":
            self.configs_needed_human.append(
                "Gateway ID is missing. Please complete the setup wizard again."
            )
            self._Loader.operating_mode = "first_run"
            self.configs_needed = ['gwid']
            return

        if self.gwhash is None or self.gwhash == "":
            print("setting to config mode ... 11")
            self._Loader.operating_mode = "config"
            self.configs_needed = ['gwhash']
            self.configs_needed_human.append("Gateway password is missing.")
            return

        if len(self.configs_needed_human) == 0:
            has_valid_credentials = self._YomboAPI.gateway_credentials_is_valid
            if has_valid_credentials is False:
                print("setting to config mode ... 22")
                self._Loader.operating_mode = "config"
                self.configs_needed_human.append(
                    "Gateway ID is invalid or has invalid authentication info."
                )
                return

            else:  # If we have a valid gateway, download it's details.
                try:
                    response = yield self._YomboAPI.request(
                        "GET", f"/v1/gateways/{self.gwid}")

                except YomboWarning as e:
                    logger.warn("Unable to get gateway details:{e}", e=e)
                    self._Loader.operating_mode = "config"
                    self.configs_needed = ['gwhash']
                    self.configs_needed_human.append(
                        "Gateway password is missing.")
                    return

                gateway = response.content["data"]["attributes"]
                self._Configs.set("core.is_master",
                                  is_true_false(gateway["is_master"]))
                self._Configs.set("core.master_gateway_id",
                                  gateway["master_gateway_id"])
                self._Configs.set("core.created_at", gateway["created_at"])
                self._Configs.set("core.updated_at", gateway["updated_at"])
                self._Configs.set("core.machine_label", gateway["label"])
                self._Configs.set("core.label", gateway["label"])
                self._Configs.set("core.description", gateway["description"])
                self._Configs.set("core.owner_id", gateway["user_id"])
                self._Configs.set("dns.fqdn", gateway["dns_name"])

                if gateway["dns_name"] is not None:
                    try:
                        response = yield self._YomboAPI.request(
                            "GET", f"/v1/gateways/{self.gwid}/dns")

                    except YomboWarning as e:
                        logger.warn("Unable to get gateway dns details:{e}",
                                    e=e)
                        self._Loader.operating_mode = "config"
                        self.configs_needed = ['gwhash']
                        self.configs_needed_human.append(
                            "Gateway password is missing.")
                        return
                    gateway_dns = response.content["data"]["attributes"]
                    self._Configs.set("dns.domain_id",
                                      gateway_dns["dns_domain_id"])
                    self._Configs.set("dns.name", gateway_dns["name"])
                    self._Configs.set("dns.allow_change_at",
                                      gateway_dns["allow_change_at"])
                    self._Configs.set("dns.domain", gateway_dns["domain"])
                    self._Configs.set(
                        "dns.fqdn",
                        f"{gateway_dns['name']}.{gateway_dns['domain']}")
                else:
                    self._Configs.set("dns.domain_id", None)
                    self._Configs.set("dns.name", None)
                    self._Configs.set("dns.allow_change_at", None)
                    self._Configs.set("dns.domain", None)
                    self._Configs.set("dns.fqdn", None)

        is_master = self._Configs.get("core.is_master", True)
        if is_master is False:
            master_gateway_id = self._Configs.get("core.master_gateway_id",
                                                  None, False)
            if master_gateway_id is None or master_gateway_id == "":
                self.configs_needed_human.append(
                    "Gateway is marked as slave, but no master gateway set.")

        if len(self.configs_needed_human) > 0:
            needed_text = "</li><li>".join(self.configs_needed_human)
            yield self._Notifications.new(
                title="Need configurations",
                message=
                f"System has been placed into configuration mode. The following "
                f"configurations are needed:<p><ul><li>{needed_text}</li></ul>",
                request_context=self._FullName,
                persist=False,
                priority="high",
                always_show=True,
                always_show_allow_clear=True)

            self._Loader.operating_mode = "config"
            return

        self._Loader.operating_mode = "run"
Esempio n. 25
0
    def update_attributes_postprocess(self, incoming):
        try:
            if "device" in incoming:
                self.device_id = incoming["device"].device_id
            elif "device_id" in incoming:
                self.device = self._Devices.get(incoming["device_id"])
            else:
                raise YomboWarning(
                    "Device command must have either a device instance or device_id."
                )
        except:
            raise YomboWarning(
                "Device command is unable to find a matching device for the provided device command."
            )

        try:
            if "command" in incoming:
                self.command_id = incoming["command"].command_id
            elif "command_id" in incoming:
                self.command = self._Commands.get(incoming["command_id"])
            else:
                raise YomboWarning(
                    "Device command must have either a command instance or command_id."
                )
        except:
            raise YomboWarning(
                "Device command is unable to find a matching command for the provided device command."
            )

        if self.history is None:
            self.history = []

        # TODO: Check to make sure there's some from of auth_id - system or user.
        # if "auth_id" in incoming:
        #     self.auth_id = incoming["auth_id"]

        if self.status is None:
            self.status = "new"
        if self.created_at is None:
            self.created_at = time()

        if isinstance(self.command_status_received, bool) is False:
            self.command_status_received = is_true_false(
                self.command_status_received)

        self.call_later = None

        self.started = incoming.get("started", False)
        self.idempotence = incoming.get("idempotence", None)

        if len(self.history) == 0:
            self.history.append(
                self.history_dict(self.created_at, self.status, "Created.",
                                  self.gateway_id))

        if self.device.gateway_id == self.gateway_id:
            self.started = False
            start = True

        # Allows various callbacks to be called when status changes.
        if "callbacks" in incoming and incoming["callbacks"] is not None:
            for cb_status, cb_callback in incoming["callbacks"].items():
                self.add_callback(cb_status, cb_callback)

        if start is None or start is True:
            reactor.callLater(0.0001, self.start)
Esempio n. 26
0
    def add(self, notice, from_db=None, create_event=None):
        """
        Add a new notice.

        :param notice: A dictionary containing notification details.
        :type record: dict
        :returns: Pointer to new notice. Only used during unittest
        """
        if 'title' not in notice:
            raise YomboWarning("New notification requires a title.")
        if 'message' not in notice:
            raise YomboWarning("New notification requires a message.")

        if 'id' not in notice:
            notice['id'] = random_string(length=16)
        else:
            if notice['id'] in self.notifications:
                self.notifications[notice['id']].update(notice)
                return notice['id']

        if 'type' not in notice:
            notice['type'] = 'notice'
        if 'gateway_id' not in notice:
            notice['gateway_id'] = self.gateway_id
        if 'priority' not in notice:
            notice['priority'] = 'normal'
        if 'source' not in notice:
            notice['source'] = ''
        if 'always_show' not in notice:
            notice['always_show'] = False
        else:
            notice['always_show'] = is_true_false(notice['always_show'])
        if 'always_show_allow_clear' not in notice:
            notice['always_show_allow_clear'] = True
        else:
            notice['always_show_allow_clear'] = is_true_false(
                notice['always_show_allow_clear'])
        if 'persist' not in notice:
            notice['persist'] = False
        if 'meta' not in notice:
            notice['meta'] = {}
        if 'user' not in notice:
            notice['user'] = None
        if 'local' not in notice:
            notice['local'] = False

        if notice['persist'] is True and 'always_show_allow_clear' is True:
            YomboWarning(
                "New notification cannot have both 'persist' and 'always_show_allow_clear' set to true."
            )

        if 'expire_at' not in notice:
            if 'timeout' in notice:
                notice['expire_at'] = time() + notice['timeout']
            else:
                notice['expire_at'] = time(
                ) + 60 * 60 * 24 * 30  # keep persistent notifications for 30 days.
        else:
            if notice['expire_at'] == None:
                if notice['persist'] == True:
                    YomboWarning("Cannot persist a non-expiring notification")
            elif notice['expire_at'] > time():
                YomboWarning(
                    "New notification is set to expire before current time.")
        if 'created_at' not in notice:
            notice['created_at'] = time()

        if 'acknowledged' not in notice:
            notice['acknowledged'] = False
        else:
            if notice['acknowledged'] not in (True, False):
                YomboWarning(
                    "New notification 'acknowledged' must be either True or False."
                )

        if 'acknowledged_at' not in notice:
            notice['acknowledged_at'] = None

        logger.debug("notice: {notice}", notice=notice)
        if from_db is None and notice['persist'] is True:
            self._LocalDB.add_notification(notice)

        self.notifications.prepend(notice['id'], Notification(self, notice))

        # Call any hooks
        try:
            global_invoke_all(
                '_notification_add_', **{
                    'called_by': self,
                    'notification': self.notifications[notice['id']],
                })
        except YomboHookStopProcessing:
            pass

        return notice['id']
        def page_setup_wizard_dns_post(webinterface, request, session):
            """
            Last step is to handle the DNS hostname.

            :param webinterface:
            :param request:
            :param session:
            :return:
            """
            if session.get("setup_wizard_last_step",
                           1) not in ("advanced_settings", "dns", "finished"):
                session.add_alert(
                    "Invalid wizard state. Please don't use the browser forward or back buttons."
                )
                return webinterface.redirect(request,
                                             "/setup_wizard/select_gateway")

            valid_submit = True
            try:
                submitted_gateway_master_gateway_id = request.args.get(
                    "master-gateway-id")[0]
                if submitted_gateway_master_gateway_id == "local":
                    submitted_gateway_is_master = 1
                    submitted_gateway_master_gateway_id = None
                else:
                    submitted_gateway_is_master = 0
            except:
                valid_submit = False
                session.add_alert("Invalid Master Gateway.")

            try:
                submitted_security_status = request.args.get(
                    "security-status")[0]
            except:
                valid_submit = False
                session.add_alert("Invalid Gateway Device Send Status.")

            if valid_submit is False:
                return webinterface.redirect(
                    request, "/setup_wizard/advanced_settings")

            try:
                submitted_security_send_private_stats = request.args.get(
                    "security-send-private-stats")[0]
            except:
                valid_submit = False
                session.add_alert("Invalid send private stats.")

            if valid_submit is False:
                return webinterface.redirect(
                    request, "/setup_wizard/advanced_settings")

            try:
                submitted_security_send_anon_stats = request.args.get(
                    "security-send-anon-stats")[0]
            except:
                valid_submit = False
                session.add_alert("Invalid send anonymous statistics.")

            if valid_submit is False:
                return webinterface.redirect(
                    request, "/setup_wizard/advanced_settings")

            session[
                "setup_wizard_gateway_is_master"] = submitted_gateway_is_master
            session[
                "setup_wizard_gateway_master_gateway_id"] = submitted_gateway_master_gateway_id
            session["setup_wizard_security_status"] = submitted_security_status
            session[
                "setup_wizard_security_send_private_stats"] = submitted_security_send_private_stats
            session[
                "setup_wizard_security_send_anon_stats"] = submitted_security_send_anon_stats

            auth_header = yield session.authorization_header(request)
            if session["setup_wizard_gateway_id"] == "new":
                data = {
                    "machine_label":
                    session["setup_wizard_gateway_machine_label"],
                    "label": session["setup_wizard_gateway_label"],
                    "description": session["setup_wizard_gateway_description"],
                    "is_master": session["setup_wizard_gateway_is_master"],
                    "status": 1
                }
                if session[
                        "setup_wizard_gateway_master_gateway_id"] is not None:
                    data["master_gateway"] = session[
                        "setup_wizard_gateway_master_gateway_id"],

                try:
                    response = yield webinterface._YomboAPI.request(
                        "POST",
                        "/v1/gateways",
                        data,
                        authorization_header=auth_header)
                except YomboWarning as e:
                    for error in e.errors:
                        session.add_alert(
                            f"Unable to add gateway, Yombo API responded with: ({error['code']}) {error['title']} - "
                            f"{error['detail']}", "warning")
                    return webinterface.redirect(
                        request, "/setup_wizard/basic_settings")
                # print(f"response.content: {response.content}")
                session["setup_wizard_gateway_id"] = response.content["data"][
                    "attributes"]

            else:
                data = {
                    "label": session["setup_wizard_gateway_label"],
                    "description": session["setup_wizard_gateway_description"],
                }
                try:
                    response = yield webinterface._YomboAPI.request(
                        "PATCH",
                        f"/v1/gateways/{session['setup_wizard_gateway_id']}",
                        data,
                        authorization_header=auth_header)
                except YomboWarning as e:
                    for error in e.errors:
                        session.add_alert(
                            f"Unable to setup gateway, Yombo API responded with: ({error['code']}) {error['title']} -"
                            f" {error['detail']}", "warning")
                        return webinterface.redirect(request,
                                                     "/setup_wizard/dns")

                try:
                    response = yield webinterface._YomboAPI.request(
                        "GET",
                        f"/v1/gateways/{session['setup_wizard_gateway_id']}/reset_authentication",
                        authorization_header=auth_header)
                except YomboWarning as e:
                    for error in e.errors:
                        session.add_alert(
                            f"Unable to setup gateway, Yombo API responded with: ({error['code']}) {error['title']} -"
                            f" {error['detail']}", "warning")
                        return webinterface.redirect(request,
                                                     "/setup_wizard/dns")

            new_auth = response.content["data"]["attributes"]
            print(f"new_auth: {new_auth}")
            webinterface._Configs.set("core", "gwid", new_auth["id"])
            webinterface._Configs.set("core", "gwuuid", new_auth["uuid"])
            webinterface._Configs.set(
                "core", "machine_label",
                session["setup_wizard_gateway_machine_label"])
            webinterface._Configs.set("core", "label",
                                      session["setup_wizard_gateway_label"])
            webinterface._Configs.set(
                "core", "description",
                session["setup_wizard_gateway_description"])
            webinterface._Configs.set("core", "gwhash", new_auth["hash"])
            webinterface._Configs.set(
                "core", "is_master",
                is_true_false(session["setup_wizard_gateway_is_master"]))
            webinterface._Configs.set(
                "core", "master_gateway_id",
                session["setup_wizard_gateway_master_gateway_id"])
            webinterface._Configs.set("security", "amqpsenddevicestatus",
                                      session["setup_wizard_security_status"])
            webinterface._Configs.set(
                "security", "amqpsendprivatestats",
                session["setup_wizard_security_send_private_stats"])
            webinterface._Configs.set(
                "security", "amqpsendanonstats",
                session["setup_wizard_security_send_anon_stats"])
            webinterface._Configs.set("location", "latitude",
                                      session["setup_wizard_gateway_latitude"])
            webinterface._Configs.set(
                "location", "longitude",
                session["setup_wizard_gateway_longitude"])
            webinterface._Configs.set(
                "location", "elevation",
                session["setup_wizard_gateway_elevation"])
            webinterface._Configs.set("core", "first_run", False)

            # Remove wizard settings...
            for session_key in list(session.keys()):
                if session_key.startswith("setup_wizard_"):
                    del session[session_key]
            session["setup_wizard_done"] = True
            session["setup_wizard_last_step"] = "finished"

            logger.info("New gpg key will be generated on next restart.")

            session["setup_wizard_last_step"] = "dns"

            results = yield form_setup_wizard_dns(webinterface, request,
                                                  session)
            return results