Example #1
0
    def import_node(self, node, source=None, test_node=False):
        """
        Imports a new node. This should only be called by this library on during startup or from "add_node"
        function.

        **Hooks called**:

        * _node_before_import_ : If added, sends node dictionary as 'node'
        * _node_before_update_ : If updated, sends node dictionary as 'node'
        * _node_imported_ : If added, send the node instance as 'node'
        * _node_updated_ : If updated, send the node instance as 'node'

        :param node: A dictionary of items required to either setup a new node or update an existing one.
        :type input: dict
        :param test_node: Used for unit testing.
        :type test_node: bool
        :returns: Pointer to new input. Only used during unittest
        """
        logger.debug("node: {node}", node=node)

        global_invoke_all('_nodes_before_import_', called_by=self, **{'node': node})
        node_id = node["id"]
        if node_id not in self.nodes:
            global_invoke_all('_node_before_load_', called_by=self, **{'node': node})
            self.nodes[node_id] = Node(self, node)
            global_invoke_all('_node_loaded_', called_by=self, **{'node': self.nodes[node_id]})
        elif node_id not in self.nodes:
            global_invoke_all('_node_before_update_', called_by=self, **{'node': node})
            self.nodes[node_id].update_attributes(node, source)
            global_invoke_all('_node_updated_', called_by=self, **{'node': self.nodes[node_id]})
Example #2
0
    def ack(self, notice_id, acknowledged_at=None, new_ack=None):
        """
        Acknowledge a notice id.

        :param notice_id:
        :return:
        """
        if notice_id not in self.notifications:
            raise KeyError('Notification not found: %s' % notice_id)

        if new_ack is None:
            new_ack = True

        if acknowledged_at is None:
            acknowledged_at = time()
        self.notifications[notice_id].set_ack(acknowledged_at, new_ack)
        try:
            global_invoke_all('_notification_acked_',
                              called_by=self,
                              notification=self.notifications[notice_id],
                              event={
                                  'notification_id': notice_id,
                              })
        except YomboHookStopProcessing:
            pass
Example #3
0
    def _import_input_type(self, input_type, klass, test_input_type=False):
        """
        Add a new input types to memory or update an existing input types.

        **Hooks called**:

        * _input_type_before_load_ : If added, sends input type dictionary as 'input_type'
        * _input_type_before_update_ : If updated, sends input type dictionary as 'input_type'
        * _input_type_loaded_ : If added, send the input type instance as 'input_type'
        * _input_type_updated_ : If updated, send the input type instance as 'input_type'

        :param input_type: A dictionary of items required to either setup a new input type or update an existing one.
        :type input: dict
        :param test_input_type: Used for unit testing.
        :type test_input_type: bool
        :returns: Pointer to new input. Only used during unittest
        """
        input_type_id = input_type["id"]
        if input_type_id not in self.input_types:
            # print("importing path: %s" % validator_data)
            self.input_types[input_type_id] = klass(self, input_type)

            # global_invoke_all('_input_type_loaded_',
            #               **{'input_type': self.input_types[input_type_id]})
        elif input_type_id not in self.input_types:
            global_invoke_all('_input_type_before_update_',
                              called_by=self,
                              **{'input_type': input_type})
            self.input_types[input_type_id].update_attributes(input_type)
            global_invoke_all(
                '_input_type_updated_',
                called_by=self,
                **{'input_type': self.input_types[input_type_id]})
Example #4
0
    def import_command(self, command, test_command=False):
        """
        Add a new command to memory or update an existing command.

        **Hooks called**:

        * _command_before_load_ : If added, sends command dictionary as 'command'
        * _command_before_update_ : If updated, sends command dictionary as 'command'
        * _command_loaded_ : If added, send the command instance as 'command'
        * _command_updated_ : If updated, send the command instance as 'command'

        :param device: A dictionary of items required to either setup a new command or update an existing one.
        :type device: dict
        :param test_command: Used for unit testing.
        :type test_command: bool
        :returns: Pointer to new device. Only used during unittest
        """
        logger.debug("command: {command}", command=command)

        try:
            global_invoke_all('_command_before_import_',
                              called_by=self,
                              **{'command': command})
        except Exception as e:
            pass
        command_id = command["id"]
        if command_id not in self.commands:
            try:
                global_invoke_all('_command_before_load_',
                                  called_by=self,
                                  **{'command': command})
            except Exception as e:
                pass
            self.commands[command_id] = Command(command)
            try:
                global_invoke_all('_command_loaded_',
                                  called_by=self,
                                  **{'command': self.commands[command_id]})
            except Exception as e:
                pass
        elif command_id not in self.commands:
            try:
                global_invoke_all('_command_before_update_',
                                  called_by=self,
                                  **{'command': command})
            except Exception as e:
                pass

            self.commands[command_id].update_attributes(command)
            try:
                global_invoke_all('_command_updated_',
                                  called_by=self,
                                  **{'command': self.commands[command_id]})
            except Exception as e:
                pass

        if command['voice_cmd'] is not None:
            self.__yombocommandsByVoice[
                command['voice_cmd']] = self.commands[command_id]
Example #5
0
    def edit_node(self, node_id, api_data, source=None, **kwargs):
        """
        Edit a node at the Yombo server level, not at the local gateway level.

        :param data:
        :param kwargs:
        :return:
        """
        results = None
        node = self.nodes[node_id]
        for key, value in api_data.items():
            setattr(node, key, value)

        if source != 'amqp':
            input_data = api_data['data'].copy()
            print("input_data- %s " % type(input_data))
            if 'data_content_type' not in api_data:
                api_data['data_content_type'] = node.data_content_type
            if api_data['data_content_type'] == 'json':
                try:
                    api_data['data'] = json.dumps(api_data['data'])
                except:
                    pass
            elif api_data['data_content_type'] == 'msgpack_base85':
                try:
                    api_data['data'] = base64.b85encode(msgpack.dumps(api_data['data']))
                except:
                    pass
            node_results = yield self._YomboAPI.request('PATCH', '/v1/node/%s' % (node_id), api_data)

            api_data['data'] = input_data

            if node_results['code'] > 299:
                results = {
                    'status': 'failed',
                    'msg': "Couldn't edit node",
                    'data': None,
                    'node_id': node_id,
                    'apimsg': node_results['content']['message'],
                    'apimsghtml': node_results['content']['html_message'],
                }
                return results

        node = self.nodes[node_id]
        if source != 'node':
            node.update_attributes(api_data, source='parent')
            node.save_to_db()

        global_invoke_all('_node_edited_', called_by=self, **{'node': node})
        results = {
            'status': 'success',
            'msg': "Node edited.",
            'node_id': node_id,
            'data': node.dump(),
            'apimsg': "Node edited.",
            'apimsghtml': "Node edited.",
            }
        return results
Example #6
0
    def set(self, key, value, value_type=None, function=None, arguments=None):
        """
        Set the value of a given state (key).

        **Hooks called**:

        * _states_set_ : Sends kwargs: *key* - The name of the state being set. *value* - The new value to set.

        :param key: Name of state to set.
        :param value: Value to set state to. Can be string, list, or dictionary.
        :param value_type: If set, allows a human filter to be applied for display.
        :param function: If this a living state, provide a function to be called to get value. Value will be used
          to set the initial value.
        :param arguments: kwarg (arguments) to send to function.
        :return: Value of state
        """
        if key in self.__States:
            # If state is already set to value, we don't do anything.
            if self.__States[key]['value'] == value:
                return
            self._Statistics.increment("lib.states.set.update", bucket_time=60, anon=True)
            self.__States[key]['created'] = int(round(time()))
        else:
            self.__States[key] = {
                'created': int(time()),
            }
            self._Statistics.increment("lib.states.set.new", bucket_time=60, anon=True)

        # Call any hooks
        try:
            state_changes = global_invoke_all('_states_preset_', **{'called_by': self,'key': key, 'value': value})
        except YomboHookStopProcessing as e:
            logger.warning("Not saving state '{state}'. Resource '{resource}' raised' YomboHookStopProcessing exception.",
                           state=key, resource=e.by_who)
            return

        self.__States[key]['value'] = value
        self.__States[key]['function'] = function
        self.__States[key]['arguments'] = arguments
        self.__States[key]['value_type'] = value_type
        self.__States[key]['value_human'] = self.convert_to_human(value, value_type)

        # Call any hooks
        try:
            state_changes = global_invoke_all('_states_set_', **{'called_by': self,'key': key, 'value': value})
        except YomboHookStopProcessing:
            pass

        live = False
        if function is not None:
            live = True

        self._LocalDB.save_state(key, value, value_type, live)

        self.check_trigger(key, value)  # Check if any automation items need to fire!
Example #7
0
    def import_location(self, location, test_location=False):
        """
        Add a new locations to memory or update an existing locations.

        **Hooks called**:

        * _location_before_load_ : If added, sends location dictionary as 'location'
        * _location_before_update_ : If updated, sends location dictionary as 'location'
        * _location_loaded_ : If added, send the location instance as 'location'
        * _location_updated_ : If updated, send the location instance as 'location'

        :param location: A dictionary of items required to either setup a new location or update an existing one.
        :type input: dict
        :param test_location: Used for unit testing.
        :type test_location: bool
        :returns: Pointer to new input. Only used during unittest
        """
        # logger.debug("location: {location}", location=location)

        location_id = location["id"]
        global_invoke_all(
            '_locations_before_import_',
            called_by=self,
            location_id=location_id,
            location=location,
        )
        if location_id not in self.locations:
            global_invoke_all(
                '_location_before_load_',
                called_by=self,
                location_id=location_id,
                location=location,
            )
            self.locations[location_id] = Location(self, location)
            global_invoke_all(
                '_location_loaded_',
                called_by=self,
                location_id=location_id,
                location=self.locations[location_id],
            )
        elif location_id not in self.locations:

            global_invoke_all(
                '_location_before_update_',
                called_by=self,
                location_id=location_id,
                location=self.locations[location_id],
            )
            self.locations[location_id].update_attributes(location)
            global_invoke_all(
                '_location_updated_',
                called_by=self,
                location_id=location_id,
                location=self.locations[location_id],
            )
Example #8
0
    def _modules_loaded_(self, **kwargs):
        """
        Implements the _modules_loaded_ and is called after _load_ is called for all the modules.

        Expects a list of events to subscribe to.

        **Hooks called**:

        * _voicecmds_add_ : Expects a list of message subscription events to subscrib to.

        **Usage**:

        .. code-block:: python

           def ModuleName_voice_cmds_load(self, **kwargs):
               return ['status']
        """
        voicecommands_to_add = yield global_invoke_all('_voicecmds_add_',
                                                       called_by=self)
        #        logger.info("voicecommands_to_add: {voice_cmds}", voice_cmds=voicecommands_to_add)

        for componentName, voice_cmds in voicecommands_to_add.items():
            if voice_cmds is None:
                continue
            for list in voice_cmds:
                logger.debug(
                    "For module '{fullName}', adding voice_cmd: {voice_cmd}, order: {order}",
                    voice_cmd=list['voice_cmd'],
                    fullName=componentName,
                    order=list['order'])
                self.add_by_string(list['voice_cmd'], list['call_back'],
                                   list['device'], list['order'])
Example #9
0
    def _module_prestart_(self, **kwargs):
        """
        Implements the _module_prestart_ and is called after _load_ is called for all the modules.

        Expects a list of events to subscribe to.

        **Hooks called**:

        * _voicecmds_add_ : Expects a list of message subscription events to subscrib to.

        **Usage**:

        .. code-block:: python

           def ModuleName_voice_cmds_load(self, **kwargs):
               return ['status']
        """
        voicecommands_to_add = global_invoke_all('_voicecmds_add_')
#        logger.info("voicecommands_to_add: {voice_cmds}", voice_cmds=voicecommands_to_add)

        for componentName, voice_cmds in voicecommands_to_add.iteritems():
            if voice_cmds is None:
                continue
            for list in voice_cmds:
                logger.debug("For module '{fullName}', adding voice_cmd: {voice_cmd}, order: {order}", voice_cmd=list['voice_cmd'], fullName=componentName, order=list['order'])
                self.add_by_string(list['voice_cmd'], list['call_back'], list['device'], list['order'])
Example #10
0
    def set_from_gateway_communications(self, key, values):
        """
        Used by the gateway coms (mqtt) system to set state values.
        :param key:
        :param values:
        :return:
        """
        gateway_id = values['gateway_id']
        if gateway_id == self.gateway_id:
            return
        if gateway_id not in self.__States:
            self.__States[gateway_id] = {}
        self.__States[values['gateway_id']][key] = {
            'gateway_id': values['gateway_id'],
            'value': values['value'],
            'value_human': values['value_human'],
            'value_type': values['value_type'],
            'live': False,
            'created_at': values['created_at'],
            'updated_at': values['updated_at'],
        }

        # Call any hooks
        try:
            yield global_invoke_all('_states_set_',
                                    **{'called_by': self,
                                       'key': key,
                                       'value': values['value'],
                                       'value_full': self.__States[gateway_id][key],
                                       'gateway_id': gateway_id,
                                       }
                                    )
        except YomboHookStopProcessing:
            pass
Example #11
0
    def set_from_gateway_communications(self, key, values):
        """
        Used by the gateway coms (mqtt) system to set state values.
        :param key:
        :param values:
        :return:
        """
        gateway_id = values['gateway_id']
        if gateway_id == self.gateway_id:
            return
        if gateway_id not in self.__States:
            self.__States[gateway_id] = {}
        self.__States[values['gateway_id']][key] = {
            'gateway_id': values['gateway_id'],
            'value': values['value'],
            'value_human': values['value_human'],
            'value_type': values['value_type'],
            'live': False,
            'created_at': values['created_at'],
            'updated_at': values['updated_at'],
        }

        # Call any hooks
        yield global_invoke_all(
            '_states_set_',
            called_by=self,
            key=key,
            value=values['value'],
            value_full=self.__States[gateway_id][key],
            gateway_id=gateway_id,
        )
Example #12
0
    def _modules_prestarted_(self, **kwargs):
        """
        This function is called before the _start_ function of all modules is called. This implements the hook:
        _statistics_lifetimes_.  The hook should return a dictionary with the following possible keys - not
        all keys are required, only ones should override the default.

        How these values work: If your module is saving stats every 30 seconds, this can consume a lot of data. After
        a while this data might be useless (or important). Periodically, the statistics are merged and save space. The
        following default values: {'full':60, '5m':90, '15m':90, '60m':365, '6hr':730, '24h':1825}  that the system
        will keep full statistics for 60 days. Then, it'll collapse this down to 15 minute averages. It will keep this
        for an additional 90 days. After that, it will keep 15 minute averages for 90 days. At this point, we have 195
        days worth of statistics. The remaining consolidations should be self explanatory.

        *Usage**:

        .. code-block:: python

           def _statistics_lifetimes_(**kwargs):
               return {'modules.mymodulename.#': {'size': 300, 'lifetime': 0}}
        """
        if self.enabled is not True:
            return

        stat_lifetimes = yield global_invoke_all(
            '_statistics_lifetimes_',
            called_by=self,
        )
        for moduleName, item in stat_lifetimes.items():
            if isinstance(item, dict):
                for bucket_name, lifetime in item.items():
                    self.add_bucket_lifetime(bucket_name, lifetime)
Example #13
0
    def _load_(self, **kwargs):
        """
        Sets up the module to start processng X10 commands. After this function is complete, the X10 API module will
        be ready to accept commands.

        **Hooks implemented**:

        * hook_x10api_interfaces : Expects a dictionary back with "priority" and "callback" for any modules that
          can send X10 commands to the power lines. Since only one module can perform this task, the module with the
          highest priority (highest number) will be used. *callback* is the function to call to perform this request.

        :param kwargs:
        :return:
        """
        results = yield global_invoke_all('x10api_interfaces', called_by=self)
        temp = {}
        #        logger.debug("message: automation_sources: {automation_sources}", automation_sources=automation_sources)
        for component_name, data in results.items():
            temp[data['priority']] = {
                'name': component_name,
                'callback': data['callback']
            }

        interfaces = OrderedDict(sorted(temp.items()))
        self.interface_callback = None
        if len(interfaces) == 0:
            logger.warn(
                "X10 API - No X10 Interface module found, disabling X10 support."
            )
        else:
            self.interface_found = True
            key = list(interfaces.keys())[-1]
            self.interface_callback = temp[key][
                'callback']  # we can only have one interface, highest priority wins!!
        # logger.warn("X10 interface: {interface}", interface=self.interface_callback)

        if self.interface_callback is not None:
            self.x10_devices.clear()
            # print "x10api init1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!114"
            # print "x10api devices: %s" % devices
            module_devices = yield self._module_devices()
            for device_id, device in module_devices.items():
                try:
                    device = self._Devices[device_id]
                    # logger.debug("devicevariables: {vars}", vars=device.device_variables_cached)
                    house = device.device_variables_cached['house']['values'][
                        0].upper()
                    unit = int(device.device_variables_cached['unit_code']
                               ['values'][0])
                except:
                    continue
                if house not in self.x10_devices:
                    self.x10_devices[house] = {}
                self.x10_devices[house][unit] = device
                item = "%s%s" % (house, str(unit))
                self.x10_devices[item] = device
Example #14
0
    def import_device_types(self, device_type, test_device_type=False):
        """
        Add a new device types to memory or update an existing device types.

        **Hooks called**:

        * _device_type_before_load_ : If added, sends device type dictionary as 'device_type'
        * _device_type_before_update_ : If updated, sends device type dictionary as 'device_type'
        * _device_type_loaded_ : If added, send the device type instance as 'device_type'
        * _device_type_updated_ : If updated, send the device type instance as 'device_type'

        :param device_type: A dictionary of items required to either setup a new device type or update an existing one.
        :type device: dict
        :param test_device_type: Used for unit testing.
        :type test_device_type: bool
        :returns: Pointer to new device. Only used during unittest
        """
        logger.debug("device_type: {device_type}", device_type=device_type)

        global_invoke_all('_device_types_before_import_',
                          called_by=self,
                          **{'device_type': device_type})
        device_type_id = device_type["id"]
        if device_type_id not in self.device_types:
            global_invoke_all('_device_type_before_load_',
                              called_by=self,
                              **{'device_type': device_type})
            self.device_types[device_type_id] = DeviceType(device_type, self)
            yield self.device_types[device_type_id]._init_()
            global_invoke_all(
                '_device_type_loaded_',
                called_by=self,
                **{'device_type': self.device_types[device_type_id]})
        elif device_type_id not in self.device_types:
            global_invoke_all('_device_type_before_update_',
                              called_by=self,
                              **{'device_type': device_type})
            self.device_types[device_type_id].update_attributes(device_type)
            yield self.device_types[device_type_id]._init_()
            global_invoke_all(
                '_device_type_updated_',
                called_by=self,
                **{'device_type': self.device_types[device_type_id]})
Example #15
0
    def enable_node(self, node_id, source=None, **kwargs):
        """
        Enable a node at the Yombo server level

        :param node_id: The node ID to enable.
        :param kwargs:
        :return:
        """
        results = None
        api_data = {
            'status': 1,
        }

        if source != 'amqp':
            node_results = yield self._YomboAPI.request('PATCH', '/v1/node/%s' % node_id, api_data)

            if node_results['code'] > 299:
                results = {
                    'status': 'failed',
                    'msg': "Couldn't enable node",
                    'node_id': node_id,
                    'data': None,
                    'apimsg': node_results['content']['message'],
                    'apimsghtml': node_results['content']['html_message'],
                }
                return results

        node = self.nodes[node_id]
        if source != 'node':
            node.update_attributes(api_data, source='parent')
            node.save_to_db()
        global_invoke_all('_node_enabled_', called_by=self, **{'node': node})
        results = {
            'status': 'success',
            'msg': "Node enabled.",
            'node_id': node_id,
            'data': node.dump(),
            'apimsg': "Node enabled.",
            'apimsghtml': "Node enabled.",
            }
        return results
Example #16
0
 def _load_(self, **kwargs):
     """
     Loads device types from the database and imports them.
     
     :return: 
     """
     yield self._load_device_types_from_database()
     self.load_platforms(BASE_DEVICE_TYPE_PLATFORMS)
     platforms = yield global_invoke_all('_device_platforms_',
                                         called_by=self)
     for component, item in platforms.items():
         self.load_platforms(item)
Example #17
0
    def delete_node(self, node_id, source=None, **kwargs):
        """
        Delete a node at the Yombo server level, not at the local gateway level.

        :param node_id: The node ID to delete.
        :param kwargs:
        :return:
        """
        results = None
        if source != 'amqp':
            node_results = yield self._YomboAPI.request('DELETE', '/v1/node/%s' % node_id)

            if node_results['code'] > 299:
                results = {
                    'status': 'failed',
                    'msg': "Couldn't delete node",
                    'node_id': node_id,
                    'data': None,
                    'apimsg': node_results['content']['message'],
                    'apimsghtml': node_results['content']['html_message'],
                }
                return results

        api_data = {
            'status': 2,
        }
        node = self.nodes[node_id]
        if source != 'node':
            node.update_attributes(api_data, source='parent')
            node.save_to_db()
        global_invoke_all('_node_deleted_', called_by=self, **{'node': node})
        results = {
            'status': 'success',
            'msg': "Node deleted.",
            'node_id': node_id,
            'data': node.dump(),
            'apimsg': "Node deleted.",
            'apimsghtml': "Node deleted.",
            }
        return results
Example #18
0
    def delete(self, notice_id):
        """
        Deletes a provided notification.

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

        try:
            del self.notifications[notice_id]
            self._LocalDB.delete_notification(notice_id)
        except:
            pass
Example #19
0
    def delete(self, notice_id):
        """
        Deletes a provided notification.

        :param notice_id:
        :return:
        """
        # Call any hooks
        try:
            global_invoke_all('_notification_delete_',
                              called_by=self,
                              notification=self.notifications[notice_id],
                              event={
                                  'notification_id': notice_id,
                              })
        except YomboHookStopProcessing:
            pass

        try:
            del self.notifications[notice_id]
            self._LocalDB.delete_notification(notice_id)
        except:
            pass
Example #20
0
    def _modules_loaded_(self, **kwargs):
        """
        Called after _load_ is called for all the modules. Get's a list of configuration items all library
        or modules define or use.

        Note: This complies with i18n translations for future use.

        **Hooks called**:

        * _configuration_details_ : Gets various details about a configuration item. Do not implement, not set
          in stone. Might migrate to i18n library.

        **Usage**:

        .. code-block:: python

           def _configuration_details_(self, **kwargs):
               return [{'webinterface': {
                           'enabled': {
                               'description': {
                                   'en': 'Enables/disables the web interface.',
                               }
                           },
                           'port': {
                               'description': {
                                   'en': 'Port number for the web interface to listen on.'
                               }
                           }
                       },
               }]

        """
        config_details = yield global_invoke_all('_configuration_details_',
                                                 called_by=self)

        for component, details in config_details.items():
            if details is None:
                continue
            for list in details:
                #                logger.warn("For module {component}, adding details: {list}", component=component, list=list)
                self.configs_details = dict_merge(self.configs_details, list)

        for section, options in self.configs.items():
            for option, keys in options.items():
                try:
                    self.configs[section][option][
                        'details'] = self.configs_details[section][option]
                except:
                    pass
Example #21
0
    def send_event_hook(self, event_msg):
        """
        Generate an "event" message of status type being the time
        event name.

        **Hooks called**:

        * _time_event_ : Sends kwargs: *key* - The name of the state being set. *value* - The new value to set.

        """
        try:
            state_changes = global_invoke_all('_time_event_', **{'value': event_msg})
        except YomboHookStopProcessing:
            logger.warning("Stopping processing 'send_event_hook' due to YomboHookStopProcessing exception.")
            return
Example #22
0
    def _module_prestart_(self, **kwargs):
        """
        This function is called before the _start_ function of all modules is called. This implements the hook:
        _statistics_lifetimes_.  The hook should return a dictionary with the following possible keys - not
        all keys are required, only ones should override the default.

        How these values work: If your module is saving stats every 30 seconds, this can consume a lot of data. After
        a while this data might be useless (or important). Periodically, the statistics are merged and save space. The
        following default values: {'full':60, '5m':90, '15m':90, '60m':365, '6hr':730, '24h':1825}  that the system
        will keep full statistics for 60 days. Then, it'll collapse this down to 15 minute averages. It will keep this
        for an additional 90 days. After that, it will keep 15 minute averages for 90 days. At this point, we have 195
        days worth of statistics. The remaining consolidations should be self explanatory.

        *Usage**:

        .. code-block:: python

           def _statistics_lifetimes_(**kwargs):
               return {'modules.mymodulename': {'full':180, '5m':180}}

        This will create statistics save duration with the following values:
        {'full':180, '5m':180, '15m':90, '60m':365, '6hr':730, '24h':1825}

        Notes: set any value to 0 and it will keep that level of data forever!

        See :py:mod:`Atoms Library <yombo.lib.automationhelpers>` for demo.
        """
        # first, set some generic defaults. The filter matcher when processing will always use the most specific.
        # full, 5m, 15m, 60m
        self.bucket_lifetimes_default = {"full": 60, "5m": 90, "15m": 90, "60m": 365, "6hr": 730, "24h": 1825}
        self.add_bucket_lifetime("#", self.bucket_lifetimes_default)
        self.add_bucket_lifetime("lib.#", {"full": 15, "5m": 30, "15m": 60, "60m": 90, "6hr": 180, "24h": 1000})
        self.add_bucket_lifetime("lib.device.#", {"full": 60, "5m": 90, "15m": 90, "60m": 90, "6hr": 180, "24h": 1000})
        self.add_bucket_lifetime(
            "lib.amqpyombo.amqp.#", {"full": 5, "5m": 10, "15m": 15, "60m": 15, "6hr": 15, "24h": 90}
        )
        self.add_bucket_lifetime("lib.#", {"full": 10, "5m": 15, "15m": 15, "60m": 15, "6hr": 180, "24h": 720})
        self.add_bucket_lifetime("modules.#", {"full": 30, "5m": 15, "15m": 15, "60m": 15, "6hr": 180, "24h": 1000})

        stat_lifetimes = global_invoke_all("_statistics_lifetimes_", called_by=self)
        #        print "################## %s " % stat_lifetimes
        #        logger.debug("message: automation_sources: {automation_sources}", automation_sources=automation_sources)
        for moduleName, item in stat_lifetimes.iteritems():
            if isinstance(item, dict):
                for bucket, values in item.iteritems():
                    # print "hook....bucket: %s" % bucket
                    # print "hook....bucket: %s" % values
                    self.add_bucket_lifetime(bucket, values)
Example #23
0
 def _load_(self, **kwargs):
     # print("################# about to process_amqpyombo_options")
     results = yield global_invoke_all('_amqpyombo_options_',
                                       called_by=self)
     for component_name, data in results.items():
         if 'connected' in data:
             self.amqpyombo_options['connected'].append(data['connected'])
         if 'disconnected' in data:
             self.amqpyombo_options['disconnected'].append(
                 data['disconnected'])
         if 'routing' in data:
             for key, the_callback in data['gateway_routing'].items():
                 if key not in self.amqpyombo_options['gateway_routing']:
                     self.amqpyombo_options['gateway_routing'][key] = []
                 self.amqpyombo_options['gateway_routing'][key].append(
                     the_callback)
Example #24
0
    def _load_(self, **kwargs):
        """
        Starts the loop to check if any certs need to be updated.

        :return:
        """
        self.check_if_certs_need_update_loop = LoopingCall(
            self.check_if_certs_need_update)
        self.check_if_certs_need_update_loop.start(
            self._Configs.get('sqldict', 'save_interval',
                              random_int(60 * 60 * 24, .1), False))

        # Check if any libraries or modules need certs.
        sslcerts = yield global_invoke_all('_sslcerts_', called_by=self)
        # print("about to add sslcerts")
        yield self._add_sslcerts(sslcerts)
Example #25
0
    def delete(self, section, option):
        """
        Delete a section/option value from configs (yombo.ini).

        :param section: The configuration section to use.
        :type section: string
        :param option: The option (key) to delete.
        :type option: string
        """
        if section in self.configs:
            if option in self.configs[section]:
                self.configs_dirty = True
                del self.configs[section][option]
                yield global_invoke_all('_configuration_set_',
                                        called_by=self,
                                        **{
                                            'section': section,
                                            'option': option,
                                            'value': None,
                                            'action': 'delete'
                                        })
Example #26
0
    def _load_(self, **kwargs):
        results = yield global_invoke_all('insteonapi_interfaces',
                                          called_by=self)
        temp = {}
        for component_name, data in results.items():
            temp[data['priority']] = {
                'name': component_name,
                'module': data['module']
            }

        interfaces = OrderedDict(sorted(temp.items()))
        self.interface_module = None
        if len(interfaces) == 0:
            logger.error(
                "Insteon API - No Insteon interface module found, disabling Insteon support."
            )
        else:
            key = list(interfaces.keys())[-1]
            self.interface_module = temp[key][
                'module']  # we can only have one interface, highest priority wins!!
            self.interface_module.insteonapi_init(
                self)  # tell the interface module about us.
Example #27
0
    def _load_(self, **kwargs):
        """
        Starts the loop to check if any certs need to be updated.

        :return:
        """
        self.check_if_certs_need_update_loop = LoopingCall(
            self.check_if_certs_need_update)
        self.check_if_certs_need_update_loop.start(
            self._Configs.get('sqldict', 'save_interval',
                              random_int(60 * 60 * 2, .2), False), False)

        # Check if any libraries or modules need certs.
        fqdn = self._Configs.get('dns', 'fqdn', None, False)
        if fqdn is None:
            logger.warn(
                "Unable to create webinterface SSL cert: DNS not set properly."
            )
            return
        sslcerts = yield global_invoke_all(
            '_sslcerts_',
            called_by=self,
        )
        yield self._add_sslcerts(sslcerts)
Example #28
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']
Example #29
0
    def set(self, section, option, value, **kwargs):
        """
        Set value of configuration option for a given section.  The option length
        **cannot exceed 1000 characters**.  The value cannot exceed 5000 bytes.

        Section and option will be converted to lowercase, rending the set/get function case insenstive.

        **Usage**:

        .. code-block:: python

           gatewayUUID = self._Config.set("section_name", "myoption", "New Value")

        :raises InvalidArgumentError: When an argument is invalid or illegal.
        :raises KeyError: When the requested section and option are not found.
        :param section: The configuration section to use.
        :type section: string
        :param option: The option (key) to use.
        :type option: string
        :param value: What to return if no result is found, default = None.
        :type value: int or string
        """
        # print("cfgs set: %s:%s = %s" % (section, option, value))

        if len(section) > self.MAX_SECTION_LENGTH:
            self._Statistics.increment("lib.configuration.set.invalid_length",
                                       bucket_size=15,
                                       anon=True)
            raise InvalidArgumentError("section cannot be more than %d chars" %
                                       self.MAX_OPTION_LENGTH)
        if len(option) > self.MAX_OPTION_LENGTH:
            self._Statistics.increment("lib.configuration.set.invalid_length",
                                       bucket_size=15,
                                       anon=True)
            raise InvalidArgumentError("option cannot be more than %d chars" %
                                       self.MAX_OPTION_LENGTH)

        # Can't set value!
        if section == 'yombo':
            self._Statistics.increment(
                "lib.configuration.set.no_setting_yombo",
                bucket_size=15,
                anon=True)
            raise InvalidArgumentError("Not allowed to set value")

        if isinstance(value, str):
            if len(value) > self.MAX_VALUE_LENGTH:
                self._Statistics.increment(
                    "lib.configuration.set.value_too_long",
                    bucket_size=15,
                    anon=True)
                raise InvalidArgumentError(
                    "value cannot be more than %d chars" % self.MAX_VALUE)

        section = section.lower()
        option = option.lower()

        if section not in self.configs:
            self.configs[section] = {}
        if option not in self.configs[section]:
            self.configs[section][option] = {
                'created_at': int(time()),
                'reads': 0,
                'writes': 0,
            }
            self._Statistics.increment("lib.configuration.set.new",
                                       bucket_size=15,
                                       anon=True)
        else:
            # already have a value. If it's the same, we won't set it.
            if self.configs[section][option]['value'] == value:
                self._Statistics.increment(
                    "lib.configuration.set.skipped_same_value",
                    bucket_size=15,
                    anon=True)
                return
            self._Statistics.increment("lib.configuration.set.update",
                                       bucket_size=15,
                                       anon=True)

        self.configs[section][option] = dict_merge(
            self.configs[section][option], {
                'updated_at': int(time()),
                'value': value,
                'hash': hashlib.sha224(str(value).encode('utf-8')).hexdigest(),
            })
        self.configs_dirty = True
        if self.loading_yombo_ini is False:
            self.configs[section][option]['writes'] += 1
            yield global_invoke_all('_configuration_set_',
                                    called_by=self,
                                    **{
                                        'section': section,
                                        'option': option,
                                        'value': value,
                                        'action': 'set'
                                    })
Example #30
0
    def add_node(self, api_data, source=None, **kwargs):
        """
        Add a new node. Updates Yombo servers and creates a new entry locally.

        :param api_data:
        :param kwargs:
        :return:
        """
        results = None
        new_node = None
        if 'gateway_id' not in api_data:
            api_data['gateway_id'] = self.gateway_id()

        if 'data_content_type' not in api_data:
            api_data['data_content_type'] = 'json'

        if source != 'amqp':
            input_data = api_data['data'].copy()
            if api_data['data_content_type'] == 'json':
                try:
                    api_data['data'] = json.dumps(api_data['data'])
                except:
                    pass
            elif api_data['data_content_type'] == 'msgpack_base85':
                try:
                    api_data['data'] = base64.b85encode(msgpack.dumps(api_data['data']))
                except:
                    pass

            node_results = yield self._YomboAPI.request('POST', '/v1/node', api_data)
            print("added node results: %s" % node_results)
            if node_results['code'] > 299:
                results = {
                    'status': 'failed',
                    'msg': "Couldn't add node",
                    'data': None,
                    'node_id': None,
                    'apimsg': node_results['content']['message'],
                    'apimsghtml': node_results['content']['html_message'],
                }
                return results
            node_id = node_results['data']['id']
            new_node = node_results['data']
            new_node['data'] = input_data

        else:
            node_id = api_data['id']
            new_node = api_data

        self.import_node(new_node, source)
        self.nodes[node_id].add_to_db()
        global_invoke_all('_node_added_', called_by=self, **{'node': self.nodes[node_id]})
        results = {
            'status': 'success',
            'msg': "Node edited.",
            'node_id': node_id,
            'data': self.nodes[node_id].dump(),
            'apimsg': "Node edited.",
            'apimsghtml': "Node edited.",
        }
        return results
        def page_configs_basic_post(webinterface, request, session):
            session.has_access("system_setting", "*", "edit")

            valid_submit = True
            # more checks to come, just doing basic for now.

            try:
                submitted_core_label = request.args.get("core_label")[0]
                webinterface._Configs.set("core", "label",
                                          submitted_core_label)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Label.")

            try:
                submitted_master_gateway_id = request.args.get(
                    "master_gateway_id")[0]
            except:
                valid_submit = False
                webinterface.add_alert("Master gateway not selected.")

            try:
                master_gateways_results = yield webinterface._YomboAPI.request(
                    "GET",
                    "/v1/gateway?_filters[is_master]=1",
                    session=session["yomboapi_session"])
            except YomboWarning as e:
                webinterface.add_alert(e.html_message, "warning")
                return webinterface.redirect(request, "/")
            new_master = None
            if submitted_master_gateway_id == webinterface.gateway_id:
                new_master = True
            else:
                for gateway in master_gateways_results["data"]:
                    if gateway['id'] == submitted_master_gateway_id:
                        new_master = True
                        break

            if new_master is None:
                valid_submit = False
                webinterface.add_alert(
                    "Invalid master gateway selection, selection doesn't exist."
                )
            else:
                webinterface._Notifications.add({
                    "title":
                    "Restart Required",
                    "message":
                    'Master gateway has been changed. A system <strong>'
                    '<a  class="confirm-restart" href="#" title="Restart Yombo Gateway">restart is required</a>'
                    '</strong> to take affect.',
                    "source":
                    "Web Interface",
                    "persist":
                    False,
                    "priority":
                    "high",
                    "always_show":
                    True,
                    "always_show_allow_clear":
                    False,
                    "id":
                    "reboot_required",
                    "local":
                    True,
                })

            try:
                submitted_core_description = request.args.get(
                    "core_description")[0]
                webinterface._Configs.set("core", "description",
                                          submitted_core_description)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Description.")

            if valid_submit:
                try:
                    if submitted_master_gateway_id == webinterface.gateway_id:
                        is_master = True
                    else:
                        is_master = False
                    data = {
                        "label": submitted_core_label,
                        "description": submitted_core_description,
                        "master_gateway":
                        submitted_master_gateway_id,  # must be master_gateway to API
                        "is_master": is_master,
                    }
                    # print("data: %s" % data)
                    results = yield webinterface._YomboAPI.request(
                        "PATCH",
                        f"/v1/gateway/{webinterface.gateway_id}",
                        data,
                        session=session["yomboapi_session"])
                    # print("api results: %s" % results)

                    previous_master_gateway_id = webinterface._Configs.get(
                        "core", "master_gateway_id", None, False)
                    if previous_master_gateway_id != submitted_master_gateway_id:
                        webinterface._Configs.set("core", "is_master",
                                                  is_master)
                        webinterface._Configs.set("core", "master_gateway_id",
                                                  submitted_master_gateway_id)
                        webinterface._Notifications.add({
                            "title":
                            "Restart Required",
                            "message":
                            "A critical configuration change has occured and requires a restart: The master gateway has been changed.",
                            "source":
                            "Web Interface",
                            "persist":
                            False,
                            "priority":
                            "high",
                            "always_show":
                            True,
                            "always_show_allow_clear":
                            False,
                            "id":
                            "reboot_required",
                            "local":
                            True,
                        })

                except YomboWarning as e:
                    webinterface.add_alert(e.html_message, "warning")
                    return webinterface.redirect(request, "/configs/basic")

            try:
                submitted_location_searchtext = request.args.get(
                    "location_searchtext")[0]
                webinterface._Configs.set("location", "searchbox",
                                          submitted_location_searchtext)
            except:
                valid_submit = False
                webinterface.add_alert(
                    "Invalid Gateway Location Search Entry.")

            try:
                submitted_location_latitude = request.args.get(
                    "location_latitude")[0]
                webinterface._Configs.set("location", "latitude",
                                          submitted_location_latitude)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Lattitude.")

            try:
                submitted_location_longitude = request.args.get(
                    "location_longitude")[0]
                webinterface._Configs.set("location", "longitude",
                                          submitted_location_longitude)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Longitude.")

            try:
                submitted_location_elevation = request.args.get(
                    "location_elevation")[0]
                webinterface._Configs.set("location", "elevation",
                                          submitted_location_elevation)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Elevation.")

            try:
                submitted_area_id = request.args.get("area_id")[0]
                webinterface._Configs.set("location", "area_id",
                                          submitted_area_id)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Location Area.")

            try:
                submitted_location_id = request.args.get("location_id")[0]
                webinterface._Configs.set("location", "location_id",
                                          submitted_location_id)
            except:
                valid_submit = False
                webinterface.add_alert("Invalid Gateway Location.")

            yield global_invoke_all("_refresh_jinja2_globals_",
                                    called_by=webinterface)
            try:
                submitted_webinterface_enabled = request.args.get(
                    "webinterface_enabled")[0]
                webinterface._Configs.set("webinterface", "enabled",
                                          submitted_webinterface_enabled)
            except:
                valid_submit = False
                webinterface.add_alert(
                    "Invalid Webinterface Enabled/Disabled value.")

            try:
                submitted_webinterface_localhost_only = request.args.get(
                    "webinterface_localhost_only")[0]
                webinterface._Configs.set(
                    "webinterface", "localhost_only",
                    submitted_webinterface_localhost_only)
            except:
                valid_submit = False
                webinterface.add_alert(
                    "Invalid Webinterface Localhost Only Selection.")

            try:
                new_port = int(
                    request.args.get("webinterface_nonsecure_port")[0])
                if new_port == 0:
                    submitted_webinterface_nonsecure_port = new_port
                    webinterface._Configs.set(
                        "webinterface", "nonsecure_port",
                        submitted_webinterface_nonsecure_port)
                elif new_port == webinterface._Configs.get(
                        "webinterface", "nonsecure_port"):
                    submitted_webinterface_nonsecure_port = new_port
                    webinterface._Configs.set(
                        "webinterface", "nonsecure_port",
                        submitted_webinterface_nonsecure_port)
                else:
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    try:
                        s.bind(("127.0.0.1", new_port))
                    except socket.error as e:
                        if e.errno == 98:
                            webinterface.add_alert(
                                "Invalid webinterface non_secure port, appears to be in use already."
                            )
                        else:
                            # something else raised the socket.error exception
                            webinterface.add_alert(
                                f"Invalid webinterface non_secure port, unable to access: {e}"
                            )
                        valid_submit = False
                    else:
                        webinterface._Configs.set("webinterface",
                                                  "nonsecure_port", new_port)
            except Exception as e:
                valid_submit = False
                webinterface.add_alert(
                    f"Invalid webinterface non_secure port: {e}")

            try:
                new_port = int(request.args.get("webinterface_secure_port")[0])
                if new_port == 0:
                    webinterface._Configs.set("webinterface", "secure_port",
                                              new_port)
                elif new_port == new_port:
                    webinterface._Configs.set("webinterface", "secure_port",
                                              new_port)
                else:
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    try:
                        s.bind(("127.0.0.1", new_port))
                    except socket.error as e:
                        if e.errno == 98:
                            webinterface.add_alert(
                                "Invalid webinterface secure port, appears to be in use already."
                            )
                        else:
                            # something else raised the socket.error exception
                            webinterface.add_alert(
                                f"Invalid webinterface secure port, unable to access: {e}"
                            )
                        valid_submit = False
                    else:
                        webinterface._Configs.set("webinterface",
                                                  "secure_port", new_port)
            except Exception as e:
                valid_submit = False
                webinterface.add_alert(
                    f"Invalid webinterface secure port: {e}")

            try:
                webinterface._Configs.set(
                    "localization", "degrees",
                    request.args.get("localization_degrees")[0])
            except:
                print("can't save degrees")
                pass

            configs = webinterface._Configs.get("*", "*")

            page = webinterface.get_template(
                request, webinterface.wi_dir + "/pages/configs/basic.html")
            return page.render(
                alerts=webinterface.get_alerts(),
                config=configs,
            )
Example #32
0
def route_devices(webapp):
    with webapp.subroute("/devices") as webapp:

        @webapp.route("/")
        @require_auth()
        def page_devices(webinterface, request, session):
            return webinterface.redirect(request, "/devices/index")

        @webapp.route("/index")
        @require_auth()
        def page_devices_index(webinterface, request, session):
            page = webinterface.get_template(request, webinterface._dir + "pages/devices/index.html")
            return page.render(
                alerts=webinterface.get_alerts(),
                devices=webinterface._Libraries["devices"]._devicesByUUID,
                devicetypes=webinterface._DeviceTypes.device_types_by_id,
            )

        @webapp.route("/add")
        @require_auth()
        def page_devices_add_select_device_type_get(webinterface, request, session):
            # session['add_device'] = {
            #     'start': time(),
            #     'id': random_string(length=10),
            #     'stage': 0,
            # }
            # webinterface.temp_data[session['add_device']['id']] = {}

            page = webinterface.get_template(request, webinterface._dir + "pages/devices/add_select_device_type.html")

            return page.render(
                alerts=webinterface.get_alerts(), device_types=webinterface._DeviceTypes.device_types_by_id
            )

        @webapp.route("/add_details", methods=["POST"])
        @require_auth()
        @inlineCallbacks
        def page_devices_add_post(webinterface, request, session):
            page = webinterface.get_template(request, webinterface._dir + "pages/devices/add_details.html")
            device_type_id = request.args.get("device_type_id")[0]
            try:
                device_type = webinterface._DeviceTypes[device_type_id]
            except Exception, e:
                webinterface.add_alert("Device Type ID was not found: %s" % device_type_id, "warning")
                returnValue(webinterface.redirect(request, "/devices/add"))

            json_output = None
            try:
                json_output = json.loads(request.args.get("json_output")[0])
                start_percent = request.args.get("start_percent")
                status = request.args.get("status")[0]
                if status == "disabled":
                    status = 0
                elif status == "enabled":
                    status = 1
                elif status == "deleted":
                    status = 2
                else:
                    webinterface.add_alert("Device status was set to an illegal value.", "warning")
                    returnValue(webinterface.redirect(request, "/devices"))

                pin_required = request.args.get("pin_required")[0]
                if pin_required == "disabled":
                    pin_required = 0
                elif pin_required == "enabled":
                    pin_required = 1
                    if request.args.get("pin_code")[0] == "":
                        webinterface.add_alert("Device requires a pin code, but none was set.", "warning")
                        returnValue(webinterface.redirect(request, "/devices"))
                else:
                    webinterface.add_alert("Device pin required was set to an illegal value.", "warning")
                    returnValue(webinterface.redirect(request, "/devices"))

                energy_usage = request.args.get("energy_usage")
                energy_map = {}
                for idx, percent in enumerate(start_percent):
                    try:
                        energy_map[float(float(percent) / 100)] = energy_usage[idx]
                    except:
                        pass

                energy_map = OrderedDict(sorted(energy_map.items(), key=lambda (x, y): float(x)))
                device = {
                    "device_id": json_output["device_id"],
                    "label": json_output["label"],
                    "description": json_output["description"],
                    "status": status,
                    "statistic_label": json_output["statistic_label"],
                    "device_type_id": json_output["device_type_id"],
                    "pin_required": pin_required,
                    "pin_code": json_output["pin_code"],
                    "pin_timeout": json_output["pin_timeout"],
                    "energy_type": json_output["energy_type"],
                    "energy_map": energy_map,
                    "variable_data": json_output["vars"],
                }
            except:
                device = {
                    "device_id": "",
                    "label": "",
                    "description": "",
                    "status": 1,
                    "statistic_label": "",
                    "device_type_id": device_type_id,
                    "pin_required": 0,
                    "pin_code": "",
                    "pin_timeout": 0,
                    "energy_type": "calc",
                    "energy_map": {0: 0, 1: 0},
                }

            if json_output is not None:
                try:
                    global_invoke_all("_device_before_add_", **{"called_by": webinterface, "device": device})
                except YomboHookStopProcessing as e:
                    webinterface.add_alert("Adding device was halted by '%s', reason: %s" % (e.name, e.message))
                    returnValue(webinterface.redirect(request, "/devices"))

                results = yield webinterface._Devices.add_device(device)

                if results["status"] == "success":
                    msg = {"header": "Device Added", "label": "Device added successfully", "description": ""}

                    webinterface._Notifications.add(
                        {
                            "title": "Restart Required",
                            "message": 'Device added. A system <strong><a  class="confirm-restart" href="#" title="Restart Yombo Gateway">restart is required</a></strong> to take affect.',
                            "source": "Web Interface",
                            "persist": False,
                            "priority": "high",
                            "always_show": True,
                            "always_show_allow_clear": False,
                            "id": "reboot_required",
                        }
                    )

                    page = webinterface.get_template(request, webinterface._dir + "pages/reboot_needed.html")
                    returnValue(page.render(alerts=webinterface.get_alerts(), msg=msg))
                else:
                    webinterface.add_alert("%s: %s" % (results["msg"], results["apimsghtml"]))
                    device["device_id"] = results["device_id"]

            var_groups = yield webinterface._Libraries["localdb"].get_variable_groups(
                "device_type", device["device_type_id"]
            )
            var_groups_final = []
            for group in var_groups:
                group = group.__dict__
                fields = yield webinterface._Libraries["localdb"].get_variable_fields_by_group(group["id"])
                fields_final = []
                for field in fields:
                    field = field.__dict__
                    fields_final.append(field)
                group["fields"] = fields_final
                var_groups_final.append(group)

            # print "final groups: %s" % var_groups_final
            returnValue(
                page.render(
                    alerts=webinterface.get_alerts(),
                    device=device,
                    dev_variables=var_groups_final,
                    commands=webinterface._Commands,
                )
            )

        @webapp.route("/delayed_commands")
        @require_auth()
        def page_devices_delayed_commands(webinterface, request, session):
            page = webinterface.get_template(request, webinterface._dir + "pages/devices/delayed_commands.html")
            print "delayed queue active: %s" % webinterface._Devices.delay_queue_active
            return page.render(
                alerts=webinterface.get_alerts(), delayed_commands=webinterface._Devices.delay_queue_active
            )

        @webapp.route("/details/<string:device_id>")
        @require_auth()
        def page_devices_details(webinterface, request, session, device_id):
            try:
                device = webinterface._Devices[device_id]
            except Exception, e:
                webinterface.add_alert("Device ID was not found.  %s" % e, "warning")
                return webinterface.redirect(request, "/devices/index")
            page = webinterface.get_template(request, webinterface._dir + "pages/devices/details.html")
            return page.render(
                alerts=webinterface.get_alerts(),
                device=device,
                devicetypes=webinterface._DeviceTypes,
                commands=webinterface._Commands,
            )
Example #33
0
    def set(self, key, value, value_type=None, function=None, arguments=None, gateway_id=None):
        """
        Set the value of a given state (key).

        **Hooks called**:

        * _states_set_ : Sends kwargs: *key* - The name of the state being set. *value* - The new value to set.

        :param key: Name of state to set.
        :param value: Value to set state to. Can be string, list, or dictionary.
        :param value_type: If set, allows a human filter to be applied for display.
        :param function: If this a living state, provide a function to be called to get value. Value will be used
          to set the initial value.
        :param arguments: kwarg (arguments) to send to function.
        :return: Value of state
        """
        # logger.debug("Saving state: {key} = {value}", key=key, value=value)
        if gateway_id is None:
            gateway_id = self.gateway_id

        search_chars = ['#', '+']
        if any(s in key for s in search_chars):
            raise YomboWarning("state keys cannot have # or + in them, reserved for searching.")

        if gateway_id not in self.__States:
            self.__States[gateway_id] = {}
        if key in self.__States[gateway_id]:
            is_new = False
            # If state is already set to value, we don't do anything.
            print("stats key exists for gateway... %s" % key)
            self.__States[gateway_id][key]['updated_at'] = int(round(time()))
            print("old (%s) == new (%s)" % (self.__States[gateway_id][key]['value'], value))

            if self.__States[gateway_id][key]['value'] == value:
                print("not setting state...it matches....")
                return
            self._Statistics.increment("lib.states.set.update", bucket_size=60, anon=True)
        else:
            print("New state: %s=%s" % (key, value))
            # logger.debug("Saving state: {key} = {value}", key=key, value=value)
            is_new = True
            self.__States[gateway_id][key] = {
                'gateway_id': gateway_id,
                'created_at': int(time()),
                'updated_at': int(time()),
                'live': False,
            }
            self._Statistics.increment("lib.states.set.new", bucket_size=60, anon=True)

        # Call any hooks
        try:
            state_changes = yield global_invoke_all('_states_preset_', **{'called_by': self,
                                                                          'key': key,
                                                                          'value': value,
                                                                          'gateway_id': gateway_id
                                                                          }
                                                    )
        except YomboHookStopProcessing as e:
            logger.warning("Not saving state '{state}'. Resource '{resource}' raised' YomboHookStopProcessing exception.",
                           state=key, resource=e.by_who)
            returnValue(None)

        self.__States[gateway_id][key]['value'] = value
        self.__States[gateway_id][key]['function'] = function
        self.__States[gateway_id][key]['arguments'] = arguments
        if is_new is True or value_type is not None:
            self.__States[gateway_id][key]['value_type'] = value_type
        if is_new is True or function is not None:
            if function is None:
                self.__States[gateway_id][key]['live'] = False
            else:
                self.__States[gateway_id][key]['live'] = True

        self.__States[gateway_id][key]['value_human'] = self.convert_to_human(value, value_type)

        # Call any hooks
        try:
            state_changes = yield global_invoke_all('_states_set_',
                                                    **{'called_by': self,
                                                       'key': key,
                                                       'value': value,
                                                       'value_full': self.__States[gateway_id][key],
                                                       'gateway_id': gateway_id})
        except YomboHookStopProcessing:
            pass


        if gateway_id == self.gateway_id:
            self.db_save_states_data.append((key, self.__States[gateway_id][key]))

        self.check_trigger(gateway_id, key, value)  # Check if any automation items need to fire!