Exemple #1
0
    def setUp(self) -> None:
        """
        Setup test data
        Returns:
            None
        """
        self.fiware_header = FiwareHeader(
            service=settings.FIWARE_SERVICE,
            service_path=settings.FIWARE_SERVICEPATH)
        self.ql_client = QuantumLeapClient(url=settings.QL_URL,
                                           fiware_header=self.fiware_header)

        self.cb_client = ContextBrokerClient(url=settings.CB_URL,
                                             fiware_header=self.fiware_header)
Exemple #2
0
    def test_test_query_endpoints_with_args(self) -> None:
        """
        Test arguments for queries

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            for entity in create_entities():
                # test limit
                for limit in range(5000, 25000, 5000):
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      limit=limit)

                    logger.debug(records.json(indent=2))
                    logger.debug(records.to_pandas())
                    self.assertEqual(len(records.index), limit)

                # test last_n
                for last_n in range(5000, 25000, 5000):
                    limit = 15000
                    last_n_records = client.get_entity_by_id(
                        entity_id=entity.id,
                        attrs='temperature,co2',
                        limit=limit,
                        last_n=last_n)
                    self.assertGreater(last_n_records.index[0],
                                       records.index[0])
                    self.assertEqual(len(last_n_records.index),
                                     min(last_n, limit))

                # test offset
                old_records = None
                for offset in range(5000, 25000, 5000):
                    # with limit
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      offset=offset)

                    if old_records:
                        self.assertLess(old_records.index[0], records.index[0])
                    old_records = records

                old_records = None
                for offset in range(5000, 25000, 5000):
                    # test with last_n
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      offset=offset,
                                                      last_n=5)
                    if old_records:
                        self.assertGreater(old_records.index[0],
                                           records.index[0])
                    old_records = records
Exemple #3
0
    def __init__(self,
                 config: Union[str, Path, HttpClientConfig, Dict] = None,
                 session: Session = None,
                 fiware_header: FiwareHeader = None,
                 **kwargs):
        """
        Constructor for master client
        Args:
            config (Union[str, Path, Dict]): Configuration object
            session (request.Session): Session object
            fiware_header (FiwareHeader): Fiware header
            **kwargs: Optional arguments that ``request`` takes.
        """
        if config:
            self.config = config
        else:
            self.config = HttpClientConfig()

        super().__init__(session=session,
                         fiware_header=fiware_header,
                         **kwargs)

        # initialize sub clients
        self.cb = ContextBrokerClient(url=self.config.cb_url,
                                      session=self.session,
                                      fiware_header=self.fiware_headers,
                                      **self.kwargs)

        self.iota = IoTAClient(url=self.config.iota_url,
                               session=self.session,
                               fiware_header=self.fiware_headers,
                               **self.kwargs)

        self.timeseries = QuantumLeapClient(url=self.config.ql_url,
                                            session=self.session,
                                            fiware_header=self.fiware_headers,
                                            **self.kwargs)

        # from here on deprecated?
        auth_types = {
            'basicauth': self.__http_basic_auth,
            'digestauth': self.__http_digest_auth
        }
        # 'oauth2': self.__oauth2}

        if self.config.auth:
            assert self.config.auth['type'].lower() in auth_types.keys()
            self.__get_secrets_file(path=self.config.auth['secret'])
            auth_types[self.config.auth['type']]()

        self.__secrets = {
            "username": None,
            "password": None,
            "client_id": None,
            "client_secret": None
        }
Exemple #4
0
 def test_meta_endpoints(self) -> None:
     """
     Test meta data endpoints
     Returns:
         None
     """
     with QuantumLeapClient(
             url=settings.QL_URL,
             fiware_header=self.fiware_header) \
             as client:
         self.assertIsNotNone(client.get_version())
         self.assertIsNotNone(client.get_health())
Exemple #5
0
def clear_quantumleap(url: str, fiware_header: FiwareHeader):
    """
    Function deletes all data for a given fiware header
    Args:
        url: Url of the quantumleap service
        fiware_header: header of the tenant

    Returns:
        None
    """
    def handle_emtpy_db_exception(err: RequestException) -> None:
        """
        When the database is empty for request quantumleap returns a 404
        error with a error message. This will be handled here
        evaluating the empty database error as 'OK'

        Args:
            err: exception raised by delete function
        """
        if err.response.status_code == 404 \
                and err.response.json().get('error', None) == 'Not Found':
            pass
        else:
            raise

    # create client
    client = QuantumLeapClient(url=url, fiware_header=fiware_header)

    # clear data
    entities = []
    try:
        entities = client.get_entities()
    except RequestException as err:
        handle_emtpy_db_exception(err)

    # will be executed for all found entities
    for entity in entities:
        client.delete_entity(entity_id=entity.entityId,
                             entity_type=entity.entityType)
Exemple #6
0
    def test_query_endpoints_by_type(self) -> None:
        """
        Test queries by type with default values

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            entities = create_entities()

            for entity in entities:
                # get by type
                attrs_type = client.get_entity_by_type(entity_type=entity.type)
                for entity_id in attrs_type:
                    logger.debug(entity_id.to_pandas())

                self.assertEqual(
                    sum([len(entity_id.index) for entity_id in attrs_type]),
                    10000)

                attrs_values_type = client.get_entity_values_by_type(
                    entity_type=entity.type, )
                for entity_id in attrs_values_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([
                        len(entity_id.index) for entity_id in attrs_values_type
                    ]), 10000)

                attr_type = client.get_entity_attr_by_type(
                    entity_type=entity.type, attr_name="temperature")
                for entity_id in attr_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([len(entity_id.index) for entity_id in attr_type]),
                    10000)

                attr_values_type = client.get_entity_attr_values_by_type(
                    entity_type=entity.type, attr_name="temperature")
                for entity_id in attr_values_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([
                        len(entity_id.index) for entity_id in attr_values_type
                    ]), 10000)
Exemple #7
0
def create_time_series_data(num_records: int = 50000):
    """
    creates large testing data sets that should remain on the server.
    This is mainly to reduce time for testings
    """
    fiware_header = FiwareHeader(service=settings.FIWARE_SERVICE,
                                 service_path="/static")

    with QuantumLeapClient(url=settings.QL_URL, fiware_header=fiware_header) \
            as client:

        for i in range(num_records):
            notification_message = Message(data=create_entities(),
                                           subscriptionId="test")
            client.post_notification(notification_message)
Exemple #8
0
    def test_entity_context(self) -> None:
        """
        Test entities endpoint
        Returns:
            None
        """
        entities = create_entities()
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header) \
                as client:
            notification_message = Message(data=entities,
                                           subscriptionId="test")
            client.post_notification(notification_message)

            time.sleep(1)
            entities = client.get_entities(entity_type=entities[0].type)
            for entity in entities:
                logger.debug(entity.json(indent=2))
Exemple #9
0
    def test_query_endpoints_by_id(self) -> None:
        """
        Test queries with default values

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            entities = create_entities()

            with self.assertRaises(requests.RequestException):
                client.get_entity_by_id(entity_id=entities[0].id,
                                        entity_type='MyType')
            for entity in entities:
                # get by id
                attrs_id = client.get_entity_by_id(entity_id=entity.id,
                                                   aggr_period='minute',
                                                   aggr_method='avg',
                                                   attrs='temperature,co2')
                logger.debug(attrs_id.json(indent=2))
                logger.debug(attrs_id.to_pandas())

                attrs_values_id = client.get_entity_values_by_id(
                    entity_id=entity.id)
                logger.debug(attrs_values_id.to_pandas())
                self.assertEqual(len(attrs_values_id.index), 10000)

                attr_id = client.get_entity_attr_by_id(entity_id=entity.id,
                                                       attr_name="temperature")
                logger.debug(attr_id.to_pandas())
                self.assertEqual(len(attr_id.index), 10000)

                attr_values_id = client.get_entity_attr_values_by_id(
                    entity_id=entity.id, attr_name="temperature")
                logger.debug(attr_values_id.to_pandas())
                self.assertEqual(len(attrs_values_id.index), 10000)
Exemple #10
0
    def test_input_endpoints(self) -> None:
        """
        Test input endpoint
        Returns:
            None
        """
        entities = create_entities()
        for entity in entities:
            self.cb_client.post_entity(entity)

        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header) \
                as client:
            notification_message = Message(data=entities,
                                           subscriptionId="test")
            client.post_subscription(cb_url=settings.CB_URL,
                                     ql_url=settings.QL_URL,
                                     entity_id=entities[0].id)
            client.post_notification(notification_message)
        time.sleep(1)
Exemple #11
0
    # different clients. It is also useful in combination with OAuth2Session
    # objects that handle authentication mechanisms and third party libraries.
    # Please be aware that you need to do the session handling yourself.
    # Hence, always create the session by using python's context protocol or
    # manually close the connection.
    with requests.Session() as s:
        cb_client = ContextBrokerClient(session=s, fiware_header=fiware_header)
        print(f"OCB Version: {cb_client.get_version()}")

    # # 3 Version information
    #
    # Independent of the selected mode, the version of the client can always be
    # accessed as follows:
    iota_client = IoTAClient(fiware_header=fiware_header)
    print(f"Iot-Agent Version: {iota_client.get_version()}")
    ql_client = QuantumLeapClient(fiware_header=fiware_header)
    print(f"QuantumLeap Version: {ql_client.get_version()}")

    # # 4 URL
    #
    # Additional to the FiwareHeader each client needs an URL, that points
    # to the Fiware-server.
    #
    # ## 4.1 Environment variables
    #
    # As shown above the client does not need to be given explicitly. If no URL
    # is given to the client, it is extracted from the environment variables
    #
    # ## 4.2 Direct Provision
    #
    # Instead of using an .env.filip or environment variables you can also
Exemple #12
0
class TestTimeSeries(unittest.TestCase):
    """
    Test class for time series api client
    """
    def setUp(self) -> None:
        """
        Setup test data
        Returns:
            None
        """
        self.fiware_header = FiwareHeader(
            service=settings.FIWARE_SERVICE,
            service_path=settings.FIWARE_SERVICEPATH)
        self.ql_client = QuantumLeapClient(url=settings.QL_URL,
                                           fiware_header=self.fiware_header)

        self.cb_client = ContextBrokerClient(url=settings.CB_URL,
                                             fiware_header=self.fiware_header)

    def test_meta_endpoints(self) -> None:
        """
        Test meta data endpoints
        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header) \
                as client:
            self.assertIsNotNone(client.get_version())
            self.assertIsNotNone(client.get_health())

    @clean_test(fiware_service=settings.FIWARE_SERVICE,
                fiware_servicepath=settings.FIWARE_SERVICEPATH,
                cb_url=settings.CB_URL,
                ql_url=settings.QL_URL)
    def test_input_endpoints(self) -> None:
        """
        Test input endpoint
        Returns:
            None
        """
        entities = create_entities()
        for entity in entities:
            self.cb_client.post_entity(entity)

        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header) \
                as client:
            notification_message = Message(data=entities,
                                           subscriptionId="test")
            client.post_subscription(cb_url=settings.CB_URL,
                                     ql_url=settings.QL_URL,
                                     entity_id=entities[0].id)
            client.post_notification(notification_message)
        time.sleep(1)

    @clean_test(fiware_service=settings.FIWARE_SERVICE,
                fiware_servicepath=settings.FIWARE_SERVICEPATH,
                ql_url=settings.QL_URL)
    def test_entity_context(self) -> None:
        """
        Test entities endpoint
        Returns:
            None
        """
        entities = create_entities()
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header) \
                as client:
            notification_message = Message(data=entities,
                                           subscriptionId="test")
            client.post_notification(notification_message)

            time.sleep(1)
            entities = client.get_entities(entity_type=entities[0].type)
            for entity in entities:
                logger.debug(entity.json(indent=2))

    def test_query_endpoints_by_id(self) -> None:
        """
        Test queries with default values

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            entities = create_entities()

            with self.assertRaises(requests.RequestException):
                client.get_entity_by_id(entity_id=entities[0].id,
                                        entity_type='MyType')
            for entity in entities:
                # get by id
                attrs_id = client.get_entity_by_id(entity_id=entity.id,
                                                   aggr_period='minute',
                                                   aggr_method='avg',
                                                   attrs='temperature,co2')
                logger.debug(attrs_id.json(indent=2))
                logger.debug(attrs_id.to_pandas())

                attrs_values_id = client.get_entity_values_by_id(
                    entity_id=entity.id)
                logger.debug(attrs_values_id.to_pandas())
                self.assertEqual(len(attrs_values_id.index), 10000)

                attr_id = client.get_entity_attr_by_id(entity_id=entity.id,
                                                       attr_name="temperature")
                logger.debug(attr_id.to_pandas())
                self.assertEqual(len(attr_id.index), 10000)

                attr_values_id = client.get_entity_attr_values_by_id(
                    entity_id=entity.id, attr_name="temperature")
                logger.debug(attr_values_id.to_pandas())
                self.assertEqual(len(attrs_values_id.index), 10000)

    def test_query_endpoints_by_type(self) -> None:
        """
        Test queries by type with default values

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            entities = create_entities()

            for entity in entities:
                # get by type
                attrs_type = client.get_entity_by_type(entity_type=entity.type)
                for entity_id in attrs_type:
                    logger.debug(entity_id.to_pandas())

                self.assertEqual(
                    sum([len(entity_id.index) for entity_id in attrs_type]),
                    10000)

                attrs_values_type = client.get_entity_values_by_type(
                    entity_type=entity.type, )
                for entity_id in attrs_values_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([
                        len(entity_id.index) for entity_id in attrs_values_type
                    ]), 10000)

                attr_type = client.get_entity_attr_by_type(
                    entity_type=entity.type, attr_name="temperature")
                for entity_id in attr_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([len(entity_id.index) for entity_id in attr_type]),
                    10000)

                attr_values_type = client.get_entity_attr_values_by_type(
                    entity_type=entity.type, attr_name="temperature")
                for entity_id in attr_values_type:
                    logger.debug(entity_id.to_pandas())
                self.assertEqual(
                    sum([
                        len(entity_id.index) for entity_id in attr_values_type
                    ]), 10000)

    def test_test_query_endpoints_with_args(self) -> None:
        """
        Test arguments for queries

        Returns:
            None
        """
        with QuantumLeapClient(
                url=settings.QL_URL,
                fiware_header=self.fiware_header.copy(
                    update={'service_path': '/static'})) \
                as client:

            for entity in create_entities():
                # test limit
                for limit in range(5000, 25000, 5000):
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      limit=limit)

                    logger.debug(records.json(indent=2))
                    logger.debug(records.to_pandas())
                    self.assertEqual(len(records.index), limit)

                # test last_n
                for last_n in range(5000, 25000, 5000):
                    limit = 15000
                    last_n_records = client.get_entity_by_id(
                        entity_id=entity.id,
                        attrs='temperature,co2',
                        limit=limit,
                        last_n=last_n)
                    self.assertGreater(last_n_records.index[0],
                                       records.index[0])
                    self.assertEqual(len(last_n_records.index),
                                     min(last_n, limit))

                # test offset
                old_records = None
                for offset in range(5000, 25000, 5000):
                    # with limit
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      offset=offset)

                    if old_records:
                        self.assertLess(old_records.index[0], records.index[0])
                    old_records = records

                old_records = None
                for offset in range(5000, 25000, 5000):
                    # test with last_n
                    records = client.get_entity_by_id(entity_id=entity.id,
                                                      attrs='temperature,co2',
                                                      offset=offset,
                                                      last_n=5)
                    if old_records:
                        self.assertGreater(old_records.index[0],
                                           records.index[0])
                    old_records = records

    def tearDown(self) -> None:
        """
        Clean up server
        Returns:
            None
        """
        clear_all(fiware_header=self.fiware_header,
                  cb_url=settings.CB_URL,
                  ql_url=settings.QL_URL)

        self.ql_client.close()
        self.cb_client.close()
Exemple #13
0
            state = 1
        elif temperature >= 21:
            state = 0
        else:
            update = False
        # send the command to the heater entity
        if update:
            command = NamedCommand(name=heater.commands[0].name, value=state)
            cbc.post_command(entity_id=heater.entity_name,
                             entity_type=heater.entity_type,
                             command=command)

    mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement)

    # ToDo: create a quantumleap client
    qlc = QuantumLeapClient(...)

    # ToDO: create a http subscriptions that get triggered by updates of your
    #  device attributes. Note that you can also post the same subscription
    #  by the context broker.
    qlc.post_subscription(entity_id=weather_station.entity_name,
                          entity_type=weather_station.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)

    qlc.post_subscription(...)

    qlc.post_subscription(...)

    # connect to the mqtt broker and subscribe to your topic
logger = logging.getLogger(__name__)

if __name__ == "__main__":
    # ## 1 Setup
    #
    # A QuantumLeapClient and a ContextBrokerClient are created to access
    # FIWARE in the space given by the FiwareHeader. For more information see:
    # e01_http_clients.py

    fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH)

    # clear all existing data
    clear_all(fiware_header=fiware_header, cb_url=CB_URL, ql_url=QL_URL)

    ql_client = QuantumLeapClient(url=QL_URL, fiware_header=fiware_header)
    print("Quantum Leap " + ql_client.get_version()["version"] + " at url " +
          ql_client.base_url)

    cb_client = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header)

    print("Context broker version " +
          cb_client.get_version()["orion"]["version"] + " at url " +
          cb_client.base_url)

    # ## 2 Interact with QL
    #
    # ### 2.1 Create a ContextEntity to work with
    #
    # for more details see: e01_ngsi_v2_context_basics.py
    hall = {
# ## Parameters
# ToDo: Enter your context broker url and port, e.g. http://localhost:1026
CB_URL = "http://localhost:1026"
# ToDo: Enter your IoT-Agent url and port, e.g. http://localhost:4041
IOTA_URL = "http://localhost:4041"
# ToDo: Enter your QuantumLeap url and port, e.g. http://localhost:8668
QL_URL = "http://localhost:8668"

# ## Main script
if __name__ == "__main__":
    # ToDo: Create a single client for each service and check the service for
    #  its version
    cbc = ContextBrokerClient(url=CB_URL)
    print(cbc.get_version())

    iotac = IoTAClient(url=IOTA_URL)
    print(iotac.get_version())

    qlc = QuantumLeapClient(url=QL_URL)
    print(qlc.get_version())

    # ToDo: Create a configuration object for a multi client
    config = HttpClientConfig(cb_url=CB_URL, iota_url=IOTA_URL, ql_url=QL_URL)

    # ToDo: Create a multi client check again all services for their version
    multic = HttpClient(config=config)

    print(multic.cb.get_version())
    print(multic.iota.get_version())
    print(multic.timeseries.get_version())
Exemple #16
0
def simulation(
    TEMPERATURE_MAX=10,  # maximal ambient temperature
    TEMPERATURE_MIN=-5,  # minimal ambient temperature
    TEMPERATURE_ZONE_START=10,  # start value of the zone temperature
    T_SIM_START=0,  # simulation start time in seconds
    T_SIM_END=24 * 60 * 60,  # simulation end time in seconds
    COM_STEP=60 * 15,  # 1 min communication step in seconds
    SLEEP_TIME=0.2  # sleep time between every simulation step
):
    # create a fiware header object
    fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH)

    # instantiate simulation model
    sim_model = SimulationModel(t_start=T_SIM_START,
                                t_end=T_SIM_END,
                                temp_max=TEMPERATURE_MAX,
                                temp_min=TEMPERATURE_MIN,
                                temp_start=TEMPERATURE_ZONE_START)

    # Create clients and restore devices and groups from files
    groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH)
    devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH)
    cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header)
    iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header)
    iotac.post_groups(service_groups=groups, update=True)
    iotac.post_devices(devices=devices, update=True)

    # Get the device configurations from the server
    weather_station = iotac.get_device(device_id="device:001")
    zone_temperature_sensor = iotac.get_device(device_id="device:002")
    heater = iotac.get_device(device_id="device:003")

    # Get the service group configurations from the server
    group = iotac.get_group(resource="/iot/json", apikey=APIKEY)

    #  Create a http subscriptions that get triggered by updates of your
    #  device attributes and send data to Quantum Leap.
    qlc = QuantumLeapClient(url=QL_URL, fiware_header=fiware_header)

    qlc.post_subscription(entity_id=weather_station.entity_name,
                          entity_type=weather_station.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)

    qlc.post_subscription(entity_id=zone_temperature_sensor.entity_name,
                          entity_type=zone_temperature_sensor.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)

    qlc.post_subscription(entity_id=heater.entity_name,
                          entity_type=heater.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)

    # create a MQTTv5 client with paho-mqtt and the known groups and devices.
    mqttc = IoTAMQTTClient(
        protocol=mqtt.MQTTv5,
        devices=[weather_station, zone_temperature_sensor, heater],
        service_groups=[group])
    # set user data if required
    mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW)

    #  Implement a callback function that gets triggered when the
    #  command is sent to the device. The incoming command should update the
    #  heater power of the simulation model
    def on_command(client, obj, msg):
        """
        Callback for incoming commands
        """
        # Decode the message payload using the libraries builtin encoders
        apikey, device_id, payload = \
            client.get_encoder(PayloadProtocol.IOTA_JSON).decode_message(
                msg=msg)
        # Update the heating power of the simulation model
        sim_model.heater_power = payload["heater_power"]

        # Acknowledge the command.
        client.publish(device_id=device_id,
                       command_name=next(iter(payload)),
                       payload=payload)

    # Add the command callback to your MQTTClient. This will get
    #  triggered for the specified device_id
    mqttc.add_command_callback(device_id=heater.device_id, callback=on_command)

    # connect to the mqtt broker and subscribe to your topic
    mqtt_url = urlparse(MQTT_BROKER_URL_EXPOSED)
    mqttc.connect(host=mqtt_url.hostname,
                  port=mqtt_url.port,
                  keepalive=60,
                  bind_address="",
                  bind_port=0,
                  clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY,
                  properties=None)
    # subscribe to all incoming command topics for the registered devices
    mqttc.subscribe()

    # create a non-blocking thread for mqtt communication
    mqttc.loop_start()

    # define lists to store historical data
    history_weather_station = []
    history_zone_temperature_sensor = []
    history_heater_power = []

    # simulation without heater
    # Create a loop that publishes regularly a message to the broker
    #  that holds the simulation time "simtime" and the corresponding
    #  temperature "temperature" the loop should. You may use the `object_id`
    #  or the attribute name as key in your payload.
    print("Simulation starts")
    for t_sim in range(sim_model.t_start, sim_model.t_end + int(COM_STEP),
                       int(COM_STEP)):
        # publish the simulated ambient temperature
        mqttc.publish(device_id=weather_station.device_id,
                      payload={
                          "temperature": sim_model.t_amb,
                          "simtime": sim_model.t_sim
                      })

        # publish the simulated zone temperature
        mqttc.publish(device_id=zone_temperature_sensor.device_id,
                      payload={
                          "temperature": sim_model.t_zone,
                          "simtime": sim_model.t_sim
                      })

        # publish the 'simtime' for the heater device
        mqttc.publish(device_id=heater.device_id,
                      payload={"simtime": sim_model.t_sim})

        # simulation step for next loop
        sim_model.do_step(int(t_sim + COM_STEP))
        # wait for one second before publishing the next values
        time.sleep(SLEEP_TIME)

        # Get corresponding entities and write values to history
        weather_station_entity = cbc.get_entity(
            entity_id=weather_station.entity_name,
            entity_type=weather_station.entity_type)
        # append the data to the local history
        history_weather_station.append({
            "simtime":
            weather_station_entity.simtime.value,
            "temperature":
            weather_station_entity.temperature.value
        })

        # Get ZoneTemperatureSensor and write values to history
        zone_temperature_sensor_entity = cbc.get_entity(
            entity_id=zone_temperature_sensor.entity_name,
            entity_type=zone_temperature_sensor.entity_type)
        history_zone_temperature_sensor.append({
            "simtime":
            zone_temperature_sensor_entity.simtime.value,
            "temperature":
            zone_temperature_sensor_entity.temperature.value
        })

        # Get ZoneTemperatureSensor and write values to history
        heater_entity = cbc.get_entity(entity_id=heater.entity_name,
                                       entity_type=heater.entity_type)
        history_heater_power.append({
            "simtime": heater_entity.simtime.value,
            "heater_power": sim_model.heater_power
        })

    # close the mqtt listening thread
    mqttc.loop_stop()
    # disconnect the mqtt device
    mqttc.disconnect()

    clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header)
    clear_context_broker(url=CB_URL, fiware_header=fiware_header)

    return history_weather_station, history_zone_temperature_sensor, history_heater_power
            state = 1
        elif temperature >= 21:
            state = 0
        else:
            update = False
        # send the command to the heater entity
        if update:
            command = NamedCommand(name=heater.commands[0].name, value=state)
            cbc.post_command(entity_id=heater.entity_name,
                             entity_type=heater.entity_type,
                             command=command)

    mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement)

    # ToDo: create a quantumleap client
    qlc = QuantumLeapClient(url=QL_URL, fiware_header=fiware_header)

    # ToDO: create a http subscriptions that get triggered by updates of your
    #  device attributes. Note that you can also post the same subscription
    #  by the context broker.
    qlc.post_subscription(entity_id=weather_station.entity_name,
                          entity_type=weather_station.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)

    qlc.post_subscription(entity_id=zone_temperature_sensor.entity_name,
                          entity_type=zone_temperature_sensor.entity_type,
                          cb_url="http://orion:1026",
                          ql_url="http://quantumleap:8668",
                          throttling=0)