Example #1
0
    def topic_to_topology(self, topic):
        """
        Decode MQTT topic segments implementing the »basic strategy«.

        The topology hierarchy is directly specified by the MQTT topic and is
        made up of a two path segments::

            realm / node

        The topology identifiers are specified as:

            - "realm" is the designated root realm. You should prefix the topic name
              with this label when opting in for all features of the telemetry platform.
              For other purposes, feel free to publish to any MQTT topic you like.

            - "node" is the node identifier. Choose anything you like. This usually
              gets transmitted from an embedded device node.
        """

        # decode the topic
        m = self.matcher.match(topic)
        if m:
            address = SmartBunch(m.groupdict())
        else:
            address = {}

        return address
Example #2
0
    def topology_to_storage(self, topology, message_type=None):
        """
        Encode topology segment identifiers to database address.

        A database server usually has the concept of multiple databases,
        each with multiple tables. With other databases than RDBMS,
        they might be named differently, but the concept in general
        is the same.

        The topology information (realm, node) will
        get mapped to the database name and measurement name to
        compute the storage location for measurements.

            - realm + node     = database name
            - sensors | events = table name

        """

        # Derive database table suffix from message type.
        table_suffix = self.get_table_suffix(topology, message_type)

        # Use topology information as blueprint for storage address.
        storage = SmartBunch(topology)

        # Format and sanitize all input parameters used for database addressing.
        # Todo: Investigate using tags additionally to / instead of only "storage.measurement".
        sanitize = self.sanitize_db_identifier
        storage.label = sanitize('{}'.format(storage.node))
        storage.database = sanitize('{}_{}'.format(storage.realm,
                                                   storage.node))
        storage.measurement = sanitize('{}'.format(table_suffix))
        storage.measurement_events = sanitize('{}'.format('events'))

        return storage
Example #3
0
    def classify(self):

        log.debug(u'Classifying beekeeper fields {fieldnames}', fieldnames=self.fieldnames)

        # TODO: Can we account for multiple occurrences of "weightX" fields for mapping more than one scale?

        weight_synonyms = u'(weight|wght|gewicht)'
        temperature_synonyms = u'(temperature|temp|temperatur)'
        outside_synonyms = u'(outside|out|air|außen|aussen)'

        candidates = {
            'weight_total': [
                self.from_words(weight_synonyms, 'total', exclude=['stddev']),
                self.from_words(weight_synonyms, exclude=['stddev']),
            ],
            'temperature_outside': [
                self.from_words(temperature_synonyms, outside_synonyms),
                self.from_words(temperature_synonyms),
            ],
            'temperature_inside': [
                self.from_words(temperature_synonyms, 'inside'),
                self.from_words(temperature_synonyms),
            ],
        }
        #pprint(candidates)

        results = SmartBunch()
        for name, patterns in candidates.items():
            fieldname = self.find_match(patterns)
            if fieldname is not None:
                results[name] = fieldname

        log.info(u'Classified beekeeper fields "{fields}" from "{fieldnames}"', fields=results.dump(), fieldnames=self.fieldnames)

        return results
Example #4
0
    def topology_to_storage(self, topology):
        """
        Encode topology segment identifiers to database address.

        A database server usually has the concept of multiple databases,
        each with multiple tables. With other databases than RDBMS,
        they might be named differently, but the concept in general
        is the same.

        When mapping the topology quadruple (realm, network, gateway, node) in the form of:

            - realm + network = database name
            - gateway + node  = table name

        We have a perfect fit for computing the slot where to store the measurements.

        """

        # Todo: Investigate using tags additionally to / instead of database.measurement
        # Todo: Move specific stuff about WeeWX or Tasmota to some device-specific knowledgebase.

        # data:     Regular endpoint
        # loop:     WeeWX
        # SENSOR:   Sonoff-Tasmota
        if topology.slot.startswith('data') or topology.slot.startswith('loop') \
                or topology.slot.endswith('SENSOR') or topology.slot.endswith('STATE'):
            suffix = 'sensors'

        elif topology.slot.startswith('event'):
            suffix = 'events'

        else:
            suffix = 'unknown'

        # Use topology information as blueprint for storage address
        storage = SmartBunch(topology)

        # Format and sanitize all input parameters used for database addressing
        sanitize = self.sanitize_db_identifier
        storage.label = sanitize('{}-{}'.format(storage.gateway, storage.node))
        storage.database = sanitize('{}_{}'.format(storage.realm,
                                                   storage.network))
        storage.measurement = sanitize('{}_{}_{}'.format(
            storage.gateway, storage.node, suffix))
        storage.measurement_events = sanitize('{}_{}_{}'.format(
            storage.gateway, storage.node, 'events'))

        return storage
Example #5
0
    def topic_to_topology(self, topic):
        """
        Decode MQTT topic segments implementing the »quadruple hierarchy strategy«.

        The topology hierarchy is directly specified by the MQTT topic and is
        made up of a minimum of four identifiers describing the core structure::

            realm / network / gateway / node

        The topology identifiers are specified as:

            - "realm" is the designated root realm. You should prefix the topic name
              with this label when opting in for all features of the telemetry platform.
              For other purposes, feel free to publish to any MQTT topic you like.

            - "network" is your personal realm. Choose anything you like or use an
              `Online GUID Generator <https://www.guidgenerator.com/>`_ to gain
              maximum uniqueness.

            - "gateway" is your gateway identifier. Choose anything you like.
              This does not have to be very unique, so you might use labels
              having the names of sites. While you are the owner of this
              namespace hierarchy, remember these labels might be visible on
              the collaborative ether, though.
              So the best thing would be to give kind of nicknames to your
              sites which don't identify their location.

            - "1" is your node identifier. Choose anything you like. This usually
              gets transmitted from an embedded device node. Remember one device node
              might have multiple sensors attached, which is beyond the scope of the
              collection platform: We just accept bunches of named measurement values,
              no matter which sensors they might originate from.
              In other words: We don't need nor favor numeric sensor identifiers,
              let's give them names!
        """

        # regular expression pattern for decoding MQTT topic address segments
        pattern = r'^(?P<realm>.+?)/(?P<network>.+?)/(?P<gateway>.+?)/(?P<node>.+?)(?:/(?P<slot>.+?))?$'

        # decode the topic
        p = re.compile(pattern)
        m = p.match(topic)
        if m:
            address = SmartBunch(m.groupdict())
        else:
            address = {}

        return address
Example #6
0
    def get_dashboard_identity(self, storage_location, topology=None):

        # Compute effective topology information
        topology = topology or {}
        realm = topology.get('realm', 'default')
        network = topology.get('network', storage_location.database)

        # Derive dashboard uid and name from topology information
        identity = SmartBunch(
            uid=u'{realm}-{network}-instant'.format(realm=realm, network=network),
            name=u'{realm}-{network}'.format(realm=realm, network=network),
            title=u'{realm}-{network}'.format(realm=realm, network=network),
            # TODO: Use real title after fully upgrading to new Grafana API (i.e. don't use get-by-slug anymore!)
            #title=u'Hiveeyes Rohdaten im Netzwerk ' + network,
        )
        #print identity.prettify()

        return identity
Example #7
0
    def get_dashboard_identity(self, storage_location, topology=None):

        # Compute effective topology information
        topology = topology or {}
        realm = topology.get('realm', 'default')
        network = topology.get('network', storage_location.database)

        # Derive dashboard uid and name from topology information
        nodename = u'{gateway} / {node}'.format(gateway=topology.gateway, node=topology.node)
        identity = SmartBunch(
            #uid=u'{realm}-{network}-instant'.format(realm=realm, network=network),
            name=self.strategy.topology_to_label(topology),
            title=self.strategy.topology_to_label(topology),
            # TODO: Use real title after fully upgrading to new Grafana API (i.e. don't use get-by-slug anymore!)
            # title=u'Hiveeyes Umweltcockpit für Meßknoten {nodename} im Netzwerk {network}'.format(nodename=nodename, network=network),
            # title=u'Hiveeyes Ertragscockpit für Meßknoten {nodename} im Netzwerk {network}'.format(nodename=nodename, network=network),
        )
        #print identity.prettify()

        return identity
Example #8
0
    def get_dashboard_identity(self, storage_location, topology=None):

        # Compute effective topology information
        topology = topology or {}
        realm = topology.get('realm', 'default')

        if 'network' in topology:
            name = topology.network
        else:
            name = topology.node

        # Derive dashboard uid and name from topology information
        identity = SmartBunch(
            #uid=u'{realm}-{name}-instant'.format(realm=realm, name=name),
            name=u'{realm}-{name}'.format(realm=realm, name=name),
            title=u'{realm}-{name}'.format(realm=realm, name=name),
            # TODO: Use real title after fully upgrading to new Grafana API (i.e. don't use get-by-slug anymore!)
            #title=u'Raw data for realm={realm} network={network}'.format(realm=realm, network=network),
        )
        #print identity.prettify()

        return identity
Example #9
0
    def topic_to_topology(self, topic):
        """
        Decode MQTT topic segments implementing the »quadruple hierarchy strategy«.

        The topology hierarchy is directly specified by the MQTT topic and is
        made up of a minimum of four identifiers describing the core structure::

            realm / network / gateway / node

        The topology identifiers are specified as:

            - "realm" is the designated root realm. You should prefix the topic name
              with this label when opting in for all features of the telemetry platform.
              For other purposes, feel free to publish to any MQTT topic you like.

            - "network" is your personal realm. Choose anything you like or use an
              `Online GUID Generator <https://www.guidgenerator.com/>`_ to gain
              maximum uniqueness.

            - "gateway" is your gateway identifier. Choose anything you like.
              This does not have to be very unique, so you might use labels
              having the names of sites. While you are the owner of this
              namespace hierarchy, remember these labels might be visible on
              the collaborative ether, though. You might want to assign nicknames
              to your sites to not identify their location.

            - "node" is the node identifier. Choose anything you like. This usually
              gets transmitted from an embedded device node.
        """

        # Decode the topic.
        m = self.matcher.match(topic)
        if m:
            address = SmartBunch(m.groupdict())
        else:
            address = {}

        return address