Beispiel #1
0
    def lru(self, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int] = None) -> LRUCache:
        """
        Create a new LRU (least recently used) based cache. This cache discards the
        least recently used items to make space if needed.

        Default is a maxsize of 512 entires.

        :param ttl: seconds cache should be good for.
        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)

        if name not in self.caches:
            self.caches[name] = {
                "cache": LRUCache(maxsize),
                "tags": tags,
                "type": "LRUCache",
                "lock": RLock(),
            }
        return self.caches[name]["cache"]
Beispiel #2
0
    def ttl(self, ttl: Optional[int] = None, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int] = None) -> TTLCache:
        """
        Create a new TTL based cache. Items in this cache will timeout after a certain period
        of time.

        Default is 120 seconds timeout with a maxsize of 512 entires.

        :param ttl: seconds cache should be good for.
        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if ttl is None:
            ttl = 120
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)

        if name not in self.caches:
            self.caches[name] = {
                "cache": TTLCache(maxsize, ttl),
                "tags": tags,
                "type": "TTLCache",
                "lock": RLock(),
            }
        return self.caches[name]["cache"]
Beispiel #3
0
    def lfu(self, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int]=None) -> LFUCache:
        """
        Create a new LFU (Least Frequently Used) based cache. This counts how often
        the cache items are used, and when the maxsize is reached, it will discard
        the least freqently used item. Use this with caution, new items tend to be
        pushed off faster and older but less freqently used items. See LRU cache.

        Default is a maxsize of 512 entires.

        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)
        if name not in self.caches:
            self.caches[name] = {
                "cache": LFUCache(maxsize),
                "tags": tags,
                "type": "LFUCache",
                "lock": RLock(),
            }
Beispiel #4
0
    def set_state_delayed(self, delay=None, **kwargs):
        """
        Accepts all the arguments of set_status, but delays submitting to set_status. This
        is used by devices that set several attributes separately, but quickly.

        :param kwargs:
        :return:
        """
        if delay is None:
            delay = 0.1
        if kwargs is None:
            raise ImportError("Must supply status arguments...")

        self.state_delayed = recursive_dict_merge(self.state_delayed, kwargs)
        if DEVICE_COMMAND_REPORTING_SOURCE not in self.state_delayed:
            self.state_delayed[
                DEVICE_COMMAND_REPORTING_SOURCE] = caller_string(
                    prefix=f"g=self._gateway_id")

        if self.state_delayed_calllater is not None and self.state_delayed_calllater.active(
        ):
            self.state_delayed_calllater.cancel()

        self.state_delayed_calllater = reactor.callLater(
            delay, self.do_set_state_delayed)
Beispiel #5
0
    def start_device_command(self):
        """
        Send the device command to the device's command processor, which calls the "_device_command_".

        :return:
        """
        print(f"start_device_command 0 - self.broadcast_at : {self.broadcast_at}")
        source = caller_string()
        if self.started is True:
            print("start_device_command 1")
            return
        if (self.broadcast_at is not None and self.broadcast_at > 0) or self.status_id >= 200:
            print("start_device_command 2")
            self.started = True
            return
        print("start_device_command a")
        self.started = True
        # if self.source == "database" and self.status == "sent":
        #     logger.debug(
        #         "Discarding a device command message loaded from database it's already been sent.")
        #     self.set_sent()
        #     return

        print(f"start_device_command d - self.broadcast_at : {self.broadcast_at}")
        if self.broadcast_at is not None:
            return

        print(f"start_device_command e - self.not_before_at : {self.not_before_at}")
        if self.not_before_at is not None:
            print("start_device_command g")
            cur_at = time()
            if self.not_after_at < cur_at:
                self.set_delay_expired(
                    message=f'Unable to send message due to request being expired by "{cur_at - self.not_after_at}" seconds.'
                )
                if self._meta["load_source"] != "database":  # Nothing should be loaded from the database that not a delayed command.
                    raise YomboWarning("Cannot setup delayed device command, it's already expired.")
            else:
                print("start_device_command l")
                when = self.not_before_at - cur_at
                if when < 0:
                    yield self._start_device_command()
                else:
                    self.call_later = reactor.callLater(when, self.device._start_device_command)
                    self.set_state("delayed")
                return True
        else:
            print(f"start_device_command w")
            if self._meta["load_source"] == "database":  # Nothing should be loaded from the database that not a delayed command.
                logger.debug("Discarding a device command message loaded from database because it's not meant to be called later.")
                self.set_failed(message="Was loaded from database, but not meant to be called later.");
            else:
                print(f"start_device_command y")
                yield self._start_device_command()
                return True
Beispiel #6
0
    def set_state(self, **kwargs):
        """
        Usually called by the device's command/logic module to set/update the
        device status. This can also be called externally as needed.

        :raises YomboWarning: Raised when:

            - If no valid status sent in. error_code: 120
            - If statusExtra was set, but not a dictionary. error_code: 121
        :param kwargs: Named arguments:

            - human_state *(int or string)* - The new status.
            - human_message *(string)* - A human friendly text message to display.
            - command *(string)* - Command label from the last command.
            - machine_state *(decimal)* - The new status.
            - machine_state_extra *(dict)* - Extra status as a dictionary.
            - silent *(any)* - If defined, will not broadcast a status update message; atypical.

        """
        self.state_delayed = recursive_dict_merge(self.state_delayed, kwargs)
        if DEVICE_COMMAND_REPORTING_SOURCE not in self.state_delayed:
            self.state_delayed[
                DEVICE_COMMAND_REPORTING_SOURCE] = caller_string(
                    prefix=f"g=self._gateway_id")

        delayed_args = copy(self.state_delayed)
        self.state_delayed.clear()

        kwargs_delayed = self.set_state_process(**delayed_args)
        try:
            kwargs_delayed, device_state = yield self._set_state(
                **kwargs_delayed)
        except YomboWarning as e:
            logger.info("{e}", e=e)
            return
        # print(f")_set_state done, got kwargs_delayed: {kwargs_delayed}")
        # print(f")_set_state done, got device_state: {device_state}")
        # print(f"device state as asdict: {device_state.asdict()}")
        # print(f"device state as dict: {device_state.__dict__}")
        # print(f"device state device: {device_state.device}")
        # print(f"device state device_id: {device_state.device_id}")

        if "silent" not in kwargs_delayed and device_state is not None:
            self.send_state(device_state)

        if self.state_delayed_calllater is not None and self.state_delayed_calllater.active(
        ):
            self.state_delayed_calllater.cancel()
        return device_state
Beispiel #7
0
    def add(self, in_statements, callback, slot_schema=None, intent_type=None,
            description=None, meta=None, intent_id=None):
        """
        Add a new intent.

        :param in_statements: The string to parse.
        :param callback: The callback to call if an intent matches.
        :param meta: Any content set here will be included with an intent match.
        :param intent_id: Optional ID to be to used.
        :return: The intent instance.
        """
        if isinstance(in_statements, str):
            statements = [in_statements]
        statements = []
        for statement in in_statements:
            if isinstance(statement, str):
                statements.append({
                    "statement": statement,
                    "statement_slots": {}
                    }
                )
            else:
                statements.append(statement)

        source = caller_string()
        if intent_id is None:
            hash_string = ""
            for statement in statements:
                hash_string += f":{statement['statement']}"
            if isinstance(description, str):
                hash_string += description
            intent_id = self._Hash.sha224_compact(hash_string)[:15]

        self.intents[intent_id] = Intent(self,
                                         statements=statements,
                                         intent_id=intent_id,
                                         callback=callback,
                                         meta=meta,
                                         description=description,
                                         source=source,
                                         intent_type=intent_type,
                                         slot_schema=slot_schema,
                                         )

        return self.intents[intent_id]
Beispiel #8
0
    def command(self, device: Union[Device, str], **kwargs):
        """
        Tells the device to do a command. This in turn calls the hook _device_command_ so modules can process the
        command if they are supposed to.

        If a pin is required, "pin" must be included as one of the arguments. All kwargs are sent with the
        hook call.

            - command doesn't exist
            - delay or max_delay is not a float or int.

        :raises YomboPinCodeError: Raised when:

            - pin is required but not received one.

        :param device: Device Instance, Device ID, machine_label, or Label.
        :type device: str
        :param command: Command InstanceCommand ID, machine_label, or Label to send.
        :type command: str
        :param pin: A pin to check.
        :type pin: str
        :param request_id: Request ID for tracking. If none given, one will be created.
        :type request_id: str
        :param delay: How many seconds to delay sending the command. Not to be combined with "not_before"
        :type delay: int or float
        :param not_before: An epoch time when the command should be sent. Not to be combined with "delay".
        :type not_before: int or float
        :param max_delay: How many second after the "delay" or "not_before" can the command be send. This can occur
            if the system was stopped when the command was supposed to be send.
        :type max_delay: int or float
        :param inputs: A list of dictionaries containing the "input_type_id" and any supplied "value".
        :type input: list of dictionaries
        :param kwargs: Any additional named arguments will be sent to the module for processing.
        :type kwargs: named arguments
        :return: The request id.
        :rtype: str
        """
        if "request_context" not in kwargs or kwargs["request_context"] is None:
            kwargs["request_context"] = caller_string()
        return self.get(device).command(**kwargs)
Beispiel #9
0
    def new(self,
            machine_label: str,
            label: Optional[str] = None,
            description: Optional[str] = None,
            request_by: Optional[str] = None,
            request_by_type: Optional[str] = None,
            request_context: Optional[str] = None,
            authentication: Optional[Type[AuthMixin]] = None,
            mqtt_topics: Optional[Union[List[str], str]] = None,
            role_id: Optional[str] = None,
            load_source: Optional[str] = None) -> Role:
        """
        Add a new role to the system.

        To track how the role 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.

        For the MQTT topic, the format is: permission:/topic   The following example grants read, write, and
        subscribe permission to the topic /yombo_set/devices/#
        read,write,subscribe:/yombo_set/devices/#

        :param machine_label: Role machine_label
        :param label: Role human label.
        :param description: Role description.
        :param request_by: Who created the role. "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: read,write:/topic/path
        :param mqtt_topics: A list of strings, a single string, that denotes which topics the role can perform
        :param load_source: How the role was loaded.
        :param role_id: How the role was loaded.
        :return:
        """
        try:
            found = self.get_advanced(
                {
                    "machine_label": machine_label,
                    "role_id": role_id
                },
                multiple=False)
            # print(f"found matching role: {role_id} = {found.role_id}")
            # print(self.roles)
            raise YomboWarning(
                f"Found a matching role: {found.role_id} - {found.machine_label} - {found.label}"
            )
        except KeyError:
            pass

        load_source = load_source or "local"

        try:
            self.get(machine_label)
            raise YomboWarning("Role already exists.")
        except KeyError:
            pass
        if load_source not in LOCAL_SOURCES:
            raise YomboWarning(
                f"Add new roles requires a load_source received '{load_source}',"
                f" must be one of: {', '.join(LOCAL_SOURCES)}")

        try:
            request_by, request_by_type = self._Permissions.request_by_info(
                authentication, request_by, request_by_type)
        except YomboWarning:
            raise YomboWarning(
                "New role must have a valid authentication or request_by and request_by_type."
            )
        if request_context is None:
            request_context = caller_string(
            )  # get the module/class/function name of caller

        mqtt = []
        if isinstance(mqtt_topics, str):
            mqtt_topics = [mqtt_topics]

        if isinstance(mqtt_topics, list):
            for temp_topic in mqtt_topics:
                permissions, topic = temp_topic.split(":")
                for permission in permissions.split(","):
                    if permission not in ("read", "write", "subscribe"):
                        logger.warn(
                            "Discarding mqtt_topic for role, invalid permission: '{permission}' - {temp_topic}",
                            permission=permission,
                            temp_topic=temp_topic)
                    mqtt.append(temp_topic)

        results = yield self.load_an_item_to_memory(
            {
                "id": role_id,
                "machine_label": machine_label,
                "label": label,
                "description": description,
                "request_by": request_by,
                "request_by_type": request_by_type,
                "request_context": request_context,
                "mqtt_topics": mqtt
            },
            load_source=load_source)
        return results
Beispiel #10
0
    def set_yield(self,
                  key,
                  value,
                  value_human=None,
                  value_type=None,
                  gateway_id=None,
                  request_by: Optional[str] = None,
                  request_by_type: Optional[str] = None,
                  request_context: Optional[str] = None,
                  authentication: Optional[
                      Type["yombo.mixins.auth_mixin.AuthMixin"]] = None,
                  created_at: Optional[int] = None,
                  updated_at: Optional[int] = None,
                  dont_save: Optional[bool] = None):
        """
        Get the value of a given data item.

        **Hooks called**:

        * _atoms_set_ / _states_set_: Sends kwargs "key", and "value". *key* is the name of the data item being set
           and *value* is the new value to set.

        :raises YomboWarning: Raised when request is malformed.
        :param key: Name of data item to set.
        :type key: string
        :param value: The value to set
        :type value: mixed
        :param value_human: What to display to mere mortals.
        :type value_human: mixed
        :param value_type: Data type to help with display formatting. Should be: str, dict, list, int, float, epoch
        :type value_type: string
        :param gateway_id: Gateway ID this item belongs to, defaults to local gateway.
        :type gateway_id: string
        :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 created_at: Change the default created_at, typically used internally.
        :type created_at: int
        :param updated_at: Change the default updated_at, typically used internally.
        :type updated_at: int
        :return: Data item instance
        :rtype: instance
        """
        search_chars = ["#", "+"]
        if any(s in key for s in search_chars):
            raise YomboWarning(
                "data item keys cannot have # or + in them, reserved for searching."
            )

        if gateway_id is None:
            gateway_id = self._gateway_id
        # self.logger.debug("systemdatamixin:set: {gateway_id}: {key} = {value}", gateway_id=gateway_id, key=key, value=value)

        if self._Loader.operating_mode != "run":
            gateway_id = self._gateway_id

        data = getattr(self, self._storage_attribute_name)

        if gateway_id not in data:
            data[gateway_id] = {}

        if updated_at is None:
            updated_at = int(time())
        if created_at is None:
            created_at = int(time())

        if isinstance(request_context, str):
            request_context = request_context
        else:
            source_type, request_context = get_yombo_instance_type(
                request_context)
        if request_context is None:
            request_context = caller_string()

        try:
            request_by, request_by_type = self._Permissions.request_by_info(
                authentication=authentication,
                request_by=request_by,
                request_by_type=request_by_type,
                default=self._Users.system_user)
        except YomboWarning:
            print(
                "System data accepted a value without any authentication information."
            )

        if value_human is None:
            value_human = self.convert_to_human(value, value_type)

        if self._Loader.run_phase[1] >= 6000 and dont_save is not True:
            try:
                yield global_invoke_all(
                    f"_{self._storage_attribute_name}_preset_",
                    called_by=self,
                    arguments={
                        "key": key,
                        "value": value,
                        "value_type": value_type,
                        "value_human": value_human,
                        "gateway_id": gateway_id,
                        "request_context": request_context,
                    },
                    stop_on_error=True,
                )

            except YomboHookStopProcessing as e:
                self.logger.warn(
                    "Not saving data item '{key}'. Resource '{resource}' raised' YomboHookStopProcessing exception.",
                    key=key,
                    resource=e.by_who)
                return None

        if value_type == "str":
            value_type = "string"
        if value_type == "boolean":
            value_type = "bool"

        if key in data[gateway_id]:
            save_data = {}
            if value_type is not None and data[gateway_id][
                    key].value_type != value_type:
                save_data["value_type"] = value_type
            # If data item is already set to value, we don't do anything.
            if data[gateway_id][key].value == value:
                return
            save_data["updated_at"] = updated_at
            self._Statistics.increment(
                f"lib.{self._storage_attribute_name}.update",
                bucket_size=60,
                anon=True)
            save_data["request_context"] = request_context
            save_data["value"] = value
            save_data["value_human"] = value_human
            data[gateway_id][key].update(save_data)
        else:
            new_instance = yield self.load_an_item_to_memory(
                {
                    "id": key,
                    "gateway_id": gateway_id,
                    "value": value,
                    "value_human": value_human,
                    "value_type": value_type,
                    "request_by": request_by,
                    "request_by_type": request_by_type,
                    "request_context": request_context,
                    "last_access_at": None,
                    "created_at": created_at,
                    "updated_at": updated_at,
                },
                save_into_storage=False)
            if dont_save is not True:
                data[gateway_id][key] = new_instance
                self._Statistics.increment(
                    f"lib.{self._storage_attribute_name}.new",
                    bucket_size=60,
                    anon=True)

        if dont_save is not True:
            self._Events.new(self._storage_attribute_name, "set",
                             (key, value, value_human, value_type, gateway_id,
                              request_context))

        # Call any hooks
        if self._Loader.run_phase[1] >= 6000 and dont_save is not True:
            yield global_invoke_all(f"_{self._storage_attribute_name}_set_",
                                    called_by=self,
                                    arguments={
                                        "item": data[gateway_id][key],
                                        "key": key,
                                        "gateway_id": gateway_id,
                                        "request_context": request_context,
                                    })
Beispiel #11
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
Beispiel #12
0
    def new(self, device: Type[Device] = None, command: Optional[Command] = None,
            gateway_id: Optional[str] = None, inputs: Optional[List[dict]] = None,
            status: Optional[str] = None, idempotence: Optional[str] = None, request_by: Optional[str] = None,
            request_by_type: Optional[str] = None, request_context: Optional[str] = None,
            authentication=None, device_command_id: Optional[str] = None, persistent_request_id: Optional[str] = None,
            created_at: Optional[Union[int, float]] = None, broadcast_at: Optional[Union[int, float]] = None,
            accepted_at: Optional[Union[int, float]] = None, sent_at: Optional[Union[int, float]] = None,
            received_at: Optional[Union[int, float]] = None, pending_at: Optional[Union[int, float]] = None,
            finished_at: Optional[Union[int, float]] = None, not_before_at: Optional[Union[int, float]] = None,
            not_after_at: Optional[Union[int, float]] = None, history: Optional[List[dict]] = None,
            uploadable: Optional[bool] = None, load_source: Optional[str] = None) -> DeviceCommand:
        """
        Add a new role to the system.

        To track how the role 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 device: Device instance, device id, or device machine label.
        :param command: Command instance, command id, or command machine label.
        :param gateway_id:
        :param inputs:
        :param status:
        :param idempotence:
        :param device_command_id:
        :param authentication: An auth item such as a websession or authkey.
        :param request_by: Who created the device command. "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 created_at:
        :param broadcast_at:
        :param accepted_at:
        :param sent_at:
        :param received_at:
        :param pending_at:
        :param finished_at:
        :param not_before_at:
        :param not_after_at:
        :param history:
        :param persistent_request_id:
        :param uploadable:
        :param load_source: How the role was loaded.
        :return:
        """
        print(f"devicecommand/init: new: {device}")
        if isinstance(device, Device) is False:
            device = self._Devices.get(device)
        if isinstance(command, Command) is False:
            command = self._Commands.get(command)

        if gateway_id is None:
            gateway_id = self._gateway_id

        if device_command_id is None:
            device_command_id = random_string(length=20)

        if status is None:
            status = DEVICE_COMMAND_STATUS_NEW

        if uploadable is None:
            uploadable = True

        if persistent_request_id is not None:  # cancel any previous device requests for this persistent id.
            for search_request_id, search_device_command in self._Parent._DeviceCommands.device_commands.items():
                if search_device_command.persistent_request_id == persistent_request_id:
                    search_device_command.cancel(message="This device command was superseded by a new persistent request.")

        print("device command, about load into memory..")
        try:
            results = yield self.load_an_item_to_memory(
                {
                    "device": device,
                    "command": command,
                    "persistent_request_id": persistent_request_id,
                    "device_command_id": device_command_id,
                    "gateway_id": gateway_id,
                    "inputs": inputs,
                    "status": status,
                    "idempotence": idempotence,
                    "created_at": created_at,
                    "broadcast_at": broadcast_at,
                    "accepted_at": accepted_at,
                    "sent_at": sent_at,
                    "received_at": received_at,
                    "pending_at": pending_at,
                    "finished_at": finished_at,
                    "not_before_at": not_before_at,
                    "not_after_at": not_after_at,
                    "history": history,
                    "request_context": request_context,
                    "uploadable": uploadable,
                    "uploaded": False,
                    "request_by": authentication.accessor_id,
                    "request_by_type": authentication.accessor_type,
                },
                authentication=authentication,
                load_source=load_source,
                generated_source=caller_string())
            print(f"device command, about load into memory..: {results}")
            yield results.start_device_command()
        except Exception as e:
            print(f"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!device command, caught : {e}")
            return None
        print("device command, about load into memory..done, now call start_device_command")
        return results
Beispiel #13
0
    def new(self,
            device: Union[str, Device],
            command: Union[str, Command],
            machine_state: Any,
            machine_state_extra: Optional[dict] = None,
            device_command: Union[str, DeviceCommand] = None,
            created_at: Optional[Union[int, float]] = None,
            energy_usage: Optional[str] = None,
            energy_type: Optional[str] = None,
            human_state: Optional[str] = None,
            human_message: Optional[str] = None,
            gateway_id: Optional[str] = None,
            reporting_source: Optional[str] = None,
            request_by: Optional[str] = None,
            request_by_type: Optional[str] = None,
            request_context: Optional[str] = None,
            authentication: Optional[
                Type["yombo.mixins.auth_mixin.AuthMixin"]] = None,
            uploaded: Optional[bool] = None,
            uploadable: Optional[bool] = None,
            _fake_data: Optional[bool] = None,
            load_source: Optional[str] = None,
            device_state_id: Optional[str] = None) -> DeviceState:
        """
        Create a new device state.

        To track how the device state 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 device:
        :param command:
        :param device_command:
        :param created_at:
        :param energy_usage:
        :param energy_type:
        :param human_state:
        :param human_message:
        :param machine_state:
        :param machine_state_extra:
        :param gateway_id:
        :param reporting_source: Which module or library is reporting this state.
        :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 uploaded:
        :param uploadable:
        :param _fake_data:
        :param load_source: How the authkey was loaded.
        :param device_state_id: Device state id to use, not normally set.
        :return:
        """
        if device_state_id is not None:
            try:
                found = self.get_advanced({"device_state_id": device_state_id},
                                          multiple=False)
                raise YomboWarning(
                    f"Found a matching device_state_id: {found.machine_label} - {found.label}"
                )
            except KeyError:
                pass

        if device_command is not None:
            if isinstance(device_command, DeviceCommand) is False:
                device_command = self._DeviceCommands.get(device_command)

        try:
            request_by, request_by_type = self._Permissions.request_by_info(
                authentication, request_by, request_by_type, device_command)
        except YomboWarning:
            logger.warn(
                "Device states accepted a state without any authentication information."
            )
        if request_context is None:
            request_context = caller_string(
            )  # get the module/class/function name of caller

        if isinstance(machine_state, float):
            machine_state = Decimal(machine_state)
        # print({
        #         "id": device_state_id,
        #         "device": device,
        #         "command": command,
        #         "device_command": device_command,
        #         "energy_usage": energy_usage,
        #         "energy_type": energy_type,
        #         "human_state": human_state,
        #         "human_message": human_message,
        #         "machine_state": machine_state,
        #         "machine_state_extra": machine_state_extra,
        #         "gateway_id": gateway_id,
        #         "reporting_source": reporting_source,
        #         "uploaded": uploaded,
        #         "uploadable": uploadable,
        #         "request_by": request_by,
        #         "request_by_type": request_by_type,
        #         "request_context": request_context,
        #         "created_at": created_at,
        #         "_fake_data": _fake_data,
        #     })

        results = yield self.load_an_item_to_memory(
            {
                "id": device_state_id,
                "device": device,
                "command": command,
                "machine_state": machine_state,
                "machine_state_extra": machine_state_extra,
                "human_state": human_state,
                "human_message": human_message,
                "device_command": device_command,
                "energy_usage": energy_usage,
                "energy_type": energy_type,
                "gateway_id": gateway_id,
                "reporting_source": reporting_source,
                "uploaded": uploaded,
                "uploadable": uploadable,
                "request_by": request_by,
                "request_by_type": request_by_type,
                "request_context": request_context,
                "created_at": created_at,
                "_fake_data": _fake_data,
            },
            load_source=load_source)
        return results
Beispiel #14
0
    def new(self, attachment: Type[AuthMixin], machine_label: str, label: str, description: str, actions, resources,
            subjects: Optional[list] = None, effect: Union[None, vakt.ALLOW_ACCESS, vakt.DENY_ACCESS] = None,
            request_by: Optional[str] = None, request_by_type: Union[None, str] = None,
            request_context: Optional[str] = None, authentication: Optional[Type[AuthMixin]] = None,
            permission_id=None, load_source=None):
        """
        Create a new permission and attach to a role, user, or authkey.

        To track how the permission 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 attachment: A role, authkey, or user.
        :param machine_label: Permission machine_label
        :param label: Permission human label.
        :param description: Permission description.
        :param actions:
        :param resources:
        :param subjects:
        :param effect:
        :param request_by: Who created the permission. "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 permission_id: permission the role was loaded.
        :param load_source: How the permission was loaded.
        :return:
        """
        _request_by, _request_by_type = self.request_by_info(authentication, request_by, request_by_type)
        if permission_id is None:
            permission_id = random_string(length=38)
        # else:
        #     permission_id = self._Hash.sha224_compact(f"{_request_by}:{_request_by_type}:{machine_label}:{permission_id}")

        try:
            found = self.get_advanced({"machine_label": machine_label, "permission_id": permission_id}, multiple=False)
            raise YomboWarning(f"Found a matching permission: {found.machine_label} - {found.label}")
        except KeyError:
            pass

        if effect in (None, True, 1, 'allow'):
            effect = vakt.ALLOW_ACCESS
        else:
            effect = vakt.DENY_ACCESS

        if subjects is None:
            subjects = [Eq(f"{attachment.auth_type}:{attachment.auth_id}")]
        else:
            subjects = self.convert_items_to_vakt(subjects)

        policy = vakt.Policy(
            permission_id,
            actions=self.convert_items_to_vakt(actions),
            resources=self.convert_items_to_vakt(resources),
            subjects=subjects,
            effect=effect,
            # context=self.convert_items_to_vakt(context),
            description=description,
        ).to_json()

        if label is None:
            label = machine_label
        if description is None:
            description = label

        results = yield self.load_an_item_to_memory(
            {
                "id": permission_id,
                "attach_id": attachment.auth_id,
                "attach_type": attachment.auth_type,
                "policy": policy,
                "machine_label": machine_label,
                "label": label,
                "description": description,
                "request_by": request_by,
                "request_by_type": request_by_type,
                "request_context": request_context,
            },
            authentication=authentication,
            load_source=load_source,
            generated_source=caller_string())
        return results