From 1f03a38290ef8d8fc81eafb3507cec995694713a Mon Sep 17 00:00:00 2001 From: Walther Date: Sat, 2 Mar 2024 19:25:55 +0100 Subject: [PATCH 01/30] chore: make exercise 2 solution more readable --- .../e2_healthcheck/e2_healthcheck_solution.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py index f710ad5b..0d4b315c 100644 --- a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py +++ b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py @@ -35,13 +35,13 @@ # ToDo: Create a single client for each service and check the service for # its version cbc = ContextBrokerClient(url=CB_URL) - print(cbc.get_version()) + print(f"Context Broker Client: {cbc.get_version()}") iotac = IoTAClient(url=IOTA_URL) - print(iotac.get_version()) + print(f"IoTA Client: {iotac.get_version()}") qlc = QuantumLeapClient(url=QL_URL) - print(qlc.get_version()) + print(f"Quantum Leap Client: {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) @@ -49,6 +49,6 @@ # 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()) + print(f"Multi Client (Context Broker): {multic.cb.get_version()}\n" + f"Multi Client (IoTA): {multic.iota.get_version()}\n" + f"Multi Client (Quantum Leap): {multic.timeseries.get_version()}") From 8a5857482a20c25add18105aafa1658ec660c02e Mon Sep 17 00:00:00 2001 From: Walther Date: Sun, 3 Mar 2024 17:06:01 +0100 Subject: [PATCH 02/30] fix: change deprecated methods --- .../e3_context_entities_solution.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py index a3cb0483..2b47d9d3 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py @@ -49,7 +49,7 @@ # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path # ToDo: Change the name of your service-path to something unique. If you run # on a shared instance this very important in order to avoid user # collisions. You will use this service path through the whole tutorial. @@ -114,7 +114,7 @@ entity_type=building.type) # print your `building model` as json - print(f"This is your building model: \n {building.json(indent=2)} \n") + print(f"This is your building model: \n {building.model_dump_json(indent=2)} \n") # ToDo: create a `opening hours` property and add it to the building object # in the context broker. Do not update the whole entity! In real @@ -136,7 +136,7 @@ hours = cbc.get_attribute_value(entity_id=building.id, entity_type=building.type, attr_name=opening_hours.name) - print(f"Your opening hours: {hours} \n" ) + print(f"Your opening hours: {hours} \n") # ToDo: modify the `opening hours` of the building cbc.update_attribute_value(entity_id=building.id, @@ -152,7 +152,7 @@ entity_type=building.type) # print your building - print(f"Your updated building model: \n {building.json(indent=2)}") + print(f"Your updated building model: \n {building.model_dump_json(indent=2)}") # ToDo: Create an entity of thermal zone and add a description property # to it @@ -174,15 +174,15 @@ # print all relationships of your thermal zone for relationship in thermal_zone.get_relationships(): - print(f"Relationship properties of your thermal zone mdoel: \n " - f"{relationship.json(indent=2)} \n") + print(f"Relationship properties of your thermal zone model: \n " + f"{relationship.model_dump_json(indent=2)} \n") # ToDo: Post your thermal zone model to the context broker cbc.post_entity(entity=thermal_zone) thermal_zone = cbc.get_entity(entity_id=thermal_zone.id, entity_type=thermal_zone.type) - # ToDo: Create and a property that references your thermal zone. Use the + # ToDo: Create and add a property that references your thermal zone. Use the # `Relationship` for type and `hasZone` for its name. Make sure that # your local model and the server model are in sync afterwards. ref_zone = NamedContextAttribute(name="hasZone", @@ -203,7 +203,7 @@ query = QueryString(qs=("refBuilding", "==", building.id)) for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the building: " - f"\n {entity.json(indent=2)}\n") + f"\n {entity.model_dump_json(indent=2)}\n") # ToDo: create a filter request that retrieves all entities from the # server`that have `hasZone' attribute that references your thermal zone @@ -214,14 +214,14 @@ query = QueryString(qs=("hasZone", "==", thermal_zone.id)) for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the thermal zone: " - f"\n {entity.json(indent=2)} \n") + f"\n {entity.model_dump_json(indent=2)} \n") # write entities to file and clear server state assert WRITE_ENTITIES_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_ENTITIES_FILEPATH.suffix}" WRITE_ENTITIES_FILEPATH.touch(exist_ok=True) with WRITE_ENTITIES_FILEPATH.open('w', encoding='utf-8') as f: - entities = [item.dict() for item in cbc.get_entity_list()] + entities = [item.model_dump() for item in cbc.get_entity_list()] json.dump(entities, f, ensure_ascii=False, indent=2) clear_context_broker(url=CB_URL, fiware_header=fiware_header) From 30674f6acffdb502c1457d439d80861ba2791652 Mon Sep 17 00:00:00 2001 From: Walther Date: Tue, 5 Mar 2024 19:39:13 +0100 Subject: [PATCH 03/30] fix: change deprecated methods and fix typos --- .../e4_iot_thermal_zone_sensors_solution.py | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py index e16d82dd..4bfa7930 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py @@ -1,9 +1,9 @@ """ # # Exercise 4: Virtual Thermal Zone -# Create two virtual IoT device. One of them represents the temperature +# Create two virtual IoT devices. One of them represents the temperature # sensor for the air temperature of a the thermal zone, whereas the second -# represents a virtual weather station. Both devices publish there values to +# represents a virtual weather station. Both devices publish their values to # the platform via MQTT. Use the simulation model of # e1_virtual_weatherstation.py # @@ -11,7 +11,7 @@ # # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create a service group and two devices +# 2. Create a service group and two corresponding devices # 3. Provision the service group and the devices # 4. Create an MQTT client using the filip.client.mqtt package and register # your service group and your devices @@ -46,20 +46,20 @@ IOTA_URL = "http://localhost:4041" # ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 MQTT_BROKER_URL = "mqtt://localhost:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path # ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! +# on a shared instance, this is very important in order to avoid user +# collisions. You will use this service path throughout the whole tutorial. +# If you forget to change it, an error will raise! SERVICE_PATH = '/your_path' APIKEY = SERVICE_PATH.strip('/') -# Path to json-files to device configuration data for follow up exercises +# Path to json-files to device configuration data for follow-up exercises WRITE_GROUPS_FILEPATH = Path( "../e4_iot_thermal_zone_sensors_solution_groups.json") WRITE_DEVICES_FILEPATH = Path( @@ -94,17 +94,16 @@ history_weather_station = [] history_zone_temperature_sensor = [] - # Create a service group and add it to your + # create a service group and add it to your service_group = ServiceGroup(apikey=APIKEY, resource="/iot/json") # ToDo: create two IoTA-MQTT devices for the weather station and the zone # temperature sensor. Also add the simulation time as `active attribute` # to each device! - # # create the weather station device - # create the simtime attribute and add during device creation - t_sim = DeviceAttribute(name='simtime', + # create the sim_time attribute and add it to the weather station's attributes + t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") @@ -118,7 +117,7 @@ commands=[]) # create a temperature attribute and add it via the api of the - # `device`-model. Use the 't_amb' as `object_id`. `object_id` specifies + # 'device'-model. Use the 't_amb' as 'object_id'. 'object_id' specifies # what key will be used in the MQTT Message payload t_amb = DeviceAttribute(name='temperature', object_id='t_amb', @@ -126,8 +125,8 @@ weather_station.add_attribute(t_amb) - # ToDo: create the zone temperature device add the t_sim attribute upon - # creation + # ToDo: Create the zone temperature device add the t_sim attribute upon + # creation. zone_temperature_sensor = Device(device_id='device:002', entity_name='urn:ngsi-ld:TemperatureSensor:001', entity_type='TemperatureSensor', @@ -137,52 +136,53 @@ attributes=[t_sim], commands=[]) # ToDo: Create the temperature attribute. Use the 't_zone' as `object_id`. - # `object_id` specifies what key will be used in the MQTT Message payload + # `object_id` specifies what key will be used in the MQTT Message payload. t_zone = DeviceAttribute(name='temperature', object_id='t_zone', type="Number") + zone_temperature_sensor.add_attribute(t_zone) - # ToDo: Create an IoTAClient + # ToDo: Create an IoTAClient. iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header) - # ToDo: Provision service group and add it to your IoTAMQTTClient + # ToDo: Provision service group and add it to your IoTAMQTTClient. iotac.post_group(service_group=service_group, update=True) - # ToDo: Provision the devices at the IoTA-Agent - # provision the WeatherStation device + # ToDo: Provision the devices at the IoTA-Agent. + # provision the weather station device iotac.post_device(device=weather_station, update=True) - # ToDo: provision the zone temperature device + # ToDo: Provision the zone temperature device. iotac.post_device(device=zone_temperature_sensor, update=True) - # ToDo: Check in the context broker if the entities corresponding to your - # devices where correctly created - # ToDo: Create a context broker client + # ToDo: Create a context broker client. + # ToDo: Check in the context broker whether the entities corresponding to your + # devices were correctly created. cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) - # Get WeatherStation entity - print(cbc.get_entity(weather_station.entity_name).json(indent=2)) - # Get ZoneTemperatureSensor entity - print(cbc.get_entity(zone_temperature_sensor.entity_name).json(indent=2)) + # get weather station entity + print(f"Weather station:\n{cbc.get_entity(weather_station.entity_name).model_dump_json(indent=2)}") + # get zone temperature sensor entity + print(f"Zone temperature sensor:\n{cbc.get_entity(zone_temperature_sensor.entity_name).model_dump_json(indent=2)}") - # ToDo: create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient + # ToDo: Create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient. mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5) # set user data if required mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) - # ToDo: Register the service group with your MQTT-Client + # ToDo: Register the service group with your MQTT-Client. mqttc.add_service_group(service_group=service_group) - # ToDo: Register devices with your MQTT-Client + # ToDo: Register devices with your MQTT-Client. # register the weather station mqttc.add_device(weather_station) - # ToDo: register the zone temperature sensor + # ToDo: register the zone temperature sensor. mqttc.add_device(zone_temperature_sensor) - # The IoTAMQTTClient automatically creates the outgoing topics from the + # The IoTAMQTTClient automatically creates outgoing topics from the # device configuration during runtime. Hence, we need to construct them - # manually in order to subscribe to them. This is usually not required as - # only the platform should listen to incoming traffic. - # if you want to listen subscribe to the following topics: + # manually in order to subscribe to them. This is usually not required as + # only the platform should listen to the incoming traffic. + # If you want to listen subscribe to the following topics: # "/json///attrs" # "/json///attrs" - # ToDO: connect to the mqtt broker and subscribe to your topic + # ToDO: Connect to the mqtt broker and subscribe to your topic. mqtt_url = urlparse(MQTT_BROKER_URL) mqttc.connect(host=mqtt_url.hostname, port=mqtt_url.port, @@ -197,45 +197,45 @@ # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every second 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. + # ToDo: Create a loop that publishes a message every 100 milliseconds + # to the broker that holds the simulation time "sim_time" and the + # corresponding temperature "temperature". You may use the 'object_id' + # or the attribute name as a key in your payload. 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}) + "sim_time": sim_model.t_sim}) - # ToDo: publish the simulated zone temperature + # ToDo: 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}) + "sim_time": sim_model.t_sim}) - # simulation step for next loop + # simulation step for the next loop sim_model.do_step(int(t_sim + COM_STEP)) # wait for one second before publishing the next values - time.sleep(1) + time.sleep(0.1) - # Get corresponding entities and write values to history + # get corresponding entities and store the data 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, + {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) - # ToDo: Get ZoneTemperatureSensor and write values to history + # ToDo: Get zone temperature sensor and store the data. 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, + {"sim_time": zone_temperature_sensor_entity.sim_time.value, "temperature": zone_temperature_sensor_entity.temperature.value}) # close the mqtt listening thread @@ -243,20 +243,20 @@ # disconnect the mqtt device mqttc.disconnect() - # plot results + # plot the results fig, ax = plt.subplots() - t_simulation = [item["simtime"] for item in history_weather_station] + t_simulation = [item["sim_time"]/3600 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') fig2, ax2 = plt.subplots() - t_simulation = [item["simtime"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax2.set_xlabel('time in s') + ax2.set_xlabel('time in h') ax2.set_ylabel('zone temperature in °C') plt.show() @@ -266,14 +266,14 @@ f"Wrong file extension! {WRITE_DEVICES_FILEPATH.suffix}" WRITE_DEVICES_FILEPATH.touch(exist_ok=True) with WRITE_DEVICES_FILEPATH.open('w', encoding='utf-8') as f: - devices = [item.dict() for item in iotac.get_device_list()] + devices = [item.model_dump() for item in iotac.get_device_list()] json.dump(devices, f, ensure_ascii=False, indent=2) assert WRITE_GROUPS_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_GROUPS_FILEPATH.suffix}" WRITE_GROUPS_FILEPATH.touch(exist_ok=True) with WRITE_GROUPS_FILEPATH.open('w', encoding='utf-8') as f: - groups = [item.dict() for item in iotac.get_group_list()] + groups = [item.model_dump() for item in iotac.get_group_list()] json.dump(groups, f, ensure_ascii=False, indent=2) clear_context_broker(url=CB_URL, fiware_header=fiware_header) From 463ab6811f9d47ca48425734a84173a330d33bca Mon Sep 17 00:00:00 2001 From: Walther Date: Thu, 7 Mar 2024 18:21:29 +0100 Subject: [PATCH 04/30] attempt: fix deprecated methods --- .../e5_iot_thermal_zone_control_solution.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index edd3b735..d557412c 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -38,7 +38,7 @@ from urllib.parse import urlparse from uuid import uuid4 import paho.mqtt.client as mqtt -from pydantic import parse_file_as +from pydantic import TypeAdapter import matplotlib.pyplot as plt # import from filip @@ -94,6 +94,12 @@ READ_DEVICES_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_solution_devices.json") +with open(READ_GROUPS_FILEPATH, 'r') as file: + json_groups = json.load(file) + +with open(READ_DEVICES_FILEPATH, 'r') as file: + json_devices = json.load(file) + # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature TEMPERATURE_MIN = -5 # minimal ambient temperature @@ -125,8 +131,10 @@ history_heater = [] # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) + ta1 = TypeAdapter(List[ServiceGroup]) + groups = ta1.validate_python(json_groups) + ta2 = TypeAdapter(List[Device]) + devices = ta2.validate_python(json_devices) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header) iotac.post_groups(service_groups=groups) @@ -167,7 +175,7 @@ heater_entity = cbc.get_entity(entity_id=heater.entity_name, entity_type=heater.entity_type) print(f"Your device entity before running the simulation: \n " - f"{heater_entity.json(indent=2)}") + f"{heater_entity.model_dump_json(indent=2)}") # create a MQTTv5 client with paho-mqtt and the known groups and devices. mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5, @@ -237,7 +245,7 @@ def on_measurement(client, obj, msg): """ Callback for measurement notifications """ - message = Message.parse_raw(msg.payload) + message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] # ToDo: retrieve the value of temperature attribute @@ -340,7 +348,7 @@ def on_measurement(client, obj, msg): mqttc.disconnect() print(cbc.get_entity(entity_id=heater.entity_name, - entity_type=heater.entity_type).json(indent=2)) + entity_type=heater.entity_type).model_dump_json(indent=2)) # plot results fig, ax = plt.subplots() @@ -372,21 +380,21 @@ def on_measurement(client, obj, msg): f"Wrong file extension! {WRITE_DEVICES_FILEPATH.suffix}" WRITE_DEVICES_FILEPATH.touch(exist_ok=True) with WRITE_DEVICES_FILEPATH.open('w', encoding='utf-8') as f: - devices = [item.dict() for item in iotac.get_device_list()] + devices = [item.model_dump() for item in iotac.get_device_list()] json.dump(devices, f, ensure_ascii=False, indent=2) assert WRITE_GROUPS_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_GROUPS_FILEPATH.suffix}" WRITE_GROUPS_FILEPATH.touch(exist_ok=True) with WRITE_GROUPS_FILEPATH.open('w', encoding='utf-8') as f: - groups = [item.dict() for item in iotac.get_group_list()] + groups = [item.model_dump() for item in iotac.get_group_list()] json.dump(groups, f, ensure_ascii=False, indent=2) assert WRITE_SUBSCRIPTIONS_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_SUBSCRIPTIONS_FILEPATH.suffix}" WRITE_SUBSCRIPTIONS_FILEPATH.touch(exist_ok=True) with WRITE_SUBSCRIPTIONS_FILEPATH.open('w', encoding='utf-8') as f: - subs = [item.dict() for item in cbc.get_subscription_list()] + subs = [item.model_dump() for item in cbc.get_subscription_list()] json.dump(subs, f, ensure_ascii=False, indent=2) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) From e5b7acde4aae9a05ce635064c374ea0fa69f1a15 Mon Sep 17 00:00:00 2001 From: Walther Date: Thu, 7 Mar 2024 18:30:08 +0100 Subject: [PATCH 05/30] feat: reduce time.sleep() and change x-axis plot result in exercise 1 --- .../e1_virtual_weatherstation_solution.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py index b2eaf1b0..5682a46e 100644 --- a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py +++ b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py @@ -31,13 +31,12 @@ # import simulation model from tutorials.ngsi_v2.simulation_model import SimulationModel - # ## Parameters # ToDo: Enter your mqtt broker url and port, e.g mqtt://test.mosquitto.org:1883 -MQTT_BROKER_URL = "mqtt://test.mosquitto.org:1883" +MQTT_BROKER_URL = "mqtt://test.mosquitto.org:1883" # ToDo: If required enter your username and password MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" # ToDo: Create a unique topic that your weather station will publish on, # e.g. by using a uuid @@ -52,7 +51,6 @@ T_SIM_END = 24 * 60 * 60 # simulation end time in seconds COM_STEP = 60 * 60 * 1 # 60 min communication step in seconds - # ## Main script if __name__ == '__main__': # instantiate simulation model @@ -68,6 +66,7 @@ mqttc = mqtt.Client(protocol=mqtt.MQTTv5) # set user data if required mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) + # ToDo: Define a callback function that will be executed when the client # receives message on a subscribed topic. It should decode your message # and store the information for later in our history @@ -83,7 +82,6 @@ def on_message(client, userdata, msg): history_weather_station.append(json.loads(payload)) - # add your callback function to the client. You can either use a global # or a topic specific callback with `mqttc.message_callback_add()` mqttc.on_message = on_message @@ -118,7 +116,7 @@ def on_message(client, userdata, msg): # simulation step for next loop sim_model.do_step(int(t_sim + COM_STEP)) - time.sleep(1) + time.sleep(0.1) # close the mqtt listening thread mqttc.loop_stop() @@ -127,9 +125,9 @@ def on_message(client, userdata, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["t_sim"] for item in history_weather_station] + t_simulation = [item["t_sim"]/3600 for item in history_weather_station] temperature = [item["t_amb"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') plt.show() From 4d16b241b7fda7cdd9a3f4ef7e716b63e80f01d0 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 19 Mar 2024 09:55:03 +0100 Subject: [PATCH 06/30] fix: cbHost send "None" when no specifying --- filip/models/ngsi_v2/iot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filip/models/ngsi_v2/iot.py b/filip/models/ngsi_v2/iot.py index 4f45c136..226a8e9e 100644 --- a/filip/models/ngsi_v2/iot.py +++ b/filip/models/ngsi_v2/iot.py @@ -219,7 +219,7 @@ def validate_cbHost(cls, value): Returns: timezone """ - return str(value) + return str(value) if value else value lazy: Optional[List[LazyDeviceAttribute]] = Field( default=[], desription="list of common lazy attributes of the device. For each " From 50205bcb81043f8691168f6c7529dc49515b3506 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 19 Mar 2024 09:56:03 +0100 Subject: [PATCH 07/30] fix: replace "simtime" with "sim_time" --- .../e5_iot_thermal_zone_control_solution.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index d557412c..d928651f 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -149,8 +149,8 @@ # ToDo: Create and additional device holding a command attribute and # post it to the IoT-Agent. It should be mapped to the `type` heater - # create the simtime attribute and add during device creation - t_sim = DeviceAttribute(name='simtime', + # create the sim_time attribute and add during device creation + t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") @@ -289,7 +289,7 @@ def on_measurement(client, obj, msg): mqttc.loop_start() # Create a loop that publishes every second a message to the broker - # that holds the simulation time "simtime" and the corresponding + # that holds the simulation time "sim_time" and the corresponding # temperature "temperature" the loop should. You may use the `object_id` # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, @@ -298,16 +298,16 @@ def on_measurement(client, obj, msg): # publish the simulated ambient temperature mqttc.publish(device_id=weather_station.device_id, payload={"temperature": sim_model.t_amb, - "simtime": sim_model.t_sim}) + "sim_time": 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}) + "sim_time": sim_model.t_sim}) - # publish the 'simtime' for the heater device + # publish the 'sim_time' for the heater device mqttc.publish(device_id=heater.device_id, - payload={"simtime": sim_model.t_sim}) + payload={"sim_time": sim_model.t_sim}) time.sleep(0.5) # simulation step for next loop @@ -322,7 +322,7 @@ def on_measurement(client, obj, msg): ) # append the data to the local history history_weather_station.append( - {"simtime": weather_station_entity.simtime.value, + {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) # Get ZoneTemperatureSensor and write values to history @@ -331,7 +331,7 @@ def on_measurement(client, obj, msg): entity_type=zone_temperature_sensor.entity_type ) history_zone_temperature_sensor.append( - {"simtime": zone_temperature_sensor_entity.simtime.value, + {"sim_time": zone_temperature_sensor_entity.sim_time.value, "temperature": zone_temperature_sensor_entity.temperature.value}) # Get ZoneTemperatureSensor and write values to history @@ -339,7 +339,7 @@ def on_measurement(client, obj, msg): entity_id=heater.entity_name, entity_type=heater.entity_type) history_heater.append( - {"simtime": heater_entity.simtime.value, + {"sim_time": heater_entity.sim_time.value, "on_off": heater_entity.heater_on_info.value}) # close the mqtt listening thread @@ -352,27 +352,28 @@ def on_measurement(client, obj, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["simtime"] for item in history_weather_station] + t_simulation = [item["sim_time"] for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) ax.set_xlabel('time in s') ax.set_ylabel('ambient temperature in °C') + plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["simtime"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"] for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) ax2.set_xlabel('time in s') ax2.set_ylabel('zone temperature in °C') + plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["simtime"] for item in history_heater] + t_simulation = [item["sim_time"] for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) ax3.set_xlabel('time in s') ax3.set_ylabel('on/off') - plt.show() # write devices and groups to file and clear server state From c2fa9cfd5c98c8cd9f76a59c5874a8e072cbfbd0 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 19 Mar 2024 09:56:23 +0100 Subject: [PATCH 08/30] chore: migrate to pydantic V2 --- .../e5_iot_thermal_zone_control.py | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py index 0b9be634..36478cc8 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py @@ -38,7 +38,7 @@ from urllib.parse import urlparse from uuid import uuid4 import paho.mqtt.client as mqtt -from pydantic import parse_file_as +from pydantic import TypeAdapter import matplotlib.pyplot as plt # import from filip @@ -94,6 +94,12 @@ READ_DEVICES_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_devices.json") +with open(READ_GROUPS_FILEPATH, 'r') as file: + json_groups = json.load(file) + +with open(READ_DEVICES_FILEPATH, 'r') as file: + json_devices = json.load(file) + # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature TEMPERATURE_MIN = -5 # minimal ambient temperature @@ -125,8 +131,10 @@ history_heater = [] # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) + ta1 = TypeAdapter(List[ServiceGroup]) + groups = ta1.validate_python(json_groups) + ta2 = TypeAdapter(List[Device]) + devices = ta2.validate_python(json_devices) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header) iotac.post_groups(service_groups=groups) @@ -141,8 +149,8 @@ # ToDo: Create and additional device holding a command attribute and # post it to the IoT-Agent. It should be mapped to the `type` heater - # create the simtime attribute and add during device creation - t_sim = DeviceAttribute(name='simtime', + # create the sim_time attribute and add during device creation + t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") @@ -167,7 +175,7 @@ heater_entity = cbc.get_entity(entity_id=heater.entity_name, entity_type=heater.entity_type) print(f"Your device entity before running the simulation: \n " - f"{heater_entity.json(indent=2)}") + f"{heater_entity.model_dump_json(indent=2)}") # create a MQTTv5 client with paho-mqtt and the known groups and devices. mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5, @@ -237,7 +245,7 @@ def on_measurement(client, obj, msg): """ Callback for measurement notifications """ - message = Message.parse_raw(msg.payload) + message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] # ToDo: retrieve the value of temperature attribute @@ -281,7 +289,7 @@ def on_measurement(client, obj, msg): mqttc.loop_start() # Create a loop that publishes every second a message to the broker - # that holds the simulation time "simtime" and the corresponding + # that holds the simulation time "sim_time" and the corresponding # temperature "temperature" the loop should. You may use the `object_id` # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, @@ -290,16 +298,16 @@ def on_measurement(client, obj, msg): # publish the simulated ambient temperature mqttc.publish(device_id=weather_station.device_id, payload={"temperature": sim_model.t_amb, - "simtime": sim_model.t_sim}) + "sim_time": 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}) + "sim_time": sim_model.t_sim}) - # publish the 'simtime' for the heater device + # publish the 'sim_time' for the heater device mqttc.publish(device_id=heater.device_id, - payload={"simtime": sim_model.t_sim}) + payload={"sim_time": sim_model.t_sim}) time.sleep(0.5) # simulation step for next loop @@ -314,7 +322,7 @@ def on_measurement(client, obj, msg): ) # append the data to the local history history_weather_station.append( - {"simtime": weather_station_entity.simtime.value, + {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) # Get ZoneTemperatureSensor and write values to history @@ -323,7 +331,7 @@ def on_measurement(client, obj, msg): entity_type=zone_temperature_sensor.entity_type ) history_zone_temperature_sensor.append( - {"simtime": zone_temperature_sensor_entity.simtime.value, + {"sim_time": zone_temperature_sensor_entity.sim_time.value, "temperature": zone_temperature_sensor_entity.temperature.value}) # Get ZoneTemperatureSensor and write values to history @@ -331,7 +339,7 @@ def on_measurement(client, obj, msg): entity_id=heater.entity_name, entity_type=heater.entity_type) history_heater.append( - {"simtime": heater_entity.simtime.value, + {"sim_time": heater_entity.sim_time.value, "on_off": heater_entity.heater_on_info.value}) # close the mqtt listening thread @@ -340,31 +348,32 @@ def on_measurement(client, obj, msg): mqttc.disconnect() print(cbc.get_entity(entity_id=heater.entity_name, - entity_type=heater.entity_type).json(indent=2)) + entity_type=heater.entity_type).model_dump_json(indent=2)) # plot results fig, ax = plt.subplots() - t_simulation = [item["simtime"] for item in history_weather_station] + t_simulation = [item["sim_time"] for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) ax.set_xlabel('time in s') ax.set_ylabel('ambient temperature in °C') + plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["simtime"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"] for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) ax2.set_xlabel('time in s') ax2.set_ylabel('zone temperature in °C') + plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["simtime"] for item in history_heater] + t_simulation = [item["sim_time"] for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) ax3.set_xlabel('time in s') ax3.set_ylabel('on/off') - plt.show() # write devices and groups to file and clear server state @@ -372,21 +381,21 @@ def on_measurement(client, obj, msg): f"Wrong file extension! {WRITE_DEVICES_FILEPATH.suffix}" WRITE_DEVICES_FILEPATH.touch(exist_ok=True) with WRITE_DEVICES_FILEPATH.open('w', encoding='utf-8') as f: - devices = [item.dict() for item in iotac.get_device_list()] + devices = [item.model_dump() for item in iotac.get_device_list()] json.dump(devices, f, ensure_ascii=False, indent=2) assert WRITE_GROUPS_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_GROUPS_FILEPATH.suffix}" WRITE_GROUPS_FILEPATH.touch(exist_ok=True) with WRITE_GROUPS_FILEPATH.open('w', encoding='utf-8') as f: - groups = [item.dict() for item in iotac.get_group_list()] + groups = [item.model_dump() for item in iotac.get_group_list()] json.dump(groups, f, ensure_ascii=False, indent=2) assert WRITE_SUBSCRIPTIONS_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_SUBSCRIPTIONS_FILEPATH.suffix}" WRITE_SUBSCRIPTIONS_FILEPATH.touch(exist_ok=True) with WRITE_SUBSCRIPTIONS_FILEPATH.open('w', encoding='utf-8') as f: - subs = [item.dict() for item in cbc.get_subscription_list()] + subs = [item.model_dump() for item in cbc.get_subscription_list()] json.dump(subs, f, ensure_ascii=False, indent=2) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) From 96d84a53b208e5f5ed41142de343b914445fc10e Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 19 Mar 2024 16:29:07 +0100 Subject: [PATCH 09/30] chore: change the convention for service_path and apikey --- .../e3_context_entities.py | 10 +++++----- .../e3_context_entities_solution.py | 12 ++++++------ .../e4_iot_thermal_zone_sensors.py | 16 ++++++++++------ .../e4_iot_thermal_zone_sensors_solution.py | 19 ++++++++++++------- .../e5_iot_thermal_zone_control.py | 19 ++++++++++++------- .../e5_iot_thermal_zone_control_solution.py | 19 ++++++++++++------- .../e6_timeseries_data/e6_timeseries_data.py | 16 ++++++++++------ .../e6_timeseries_data_solution.py | 19 ++++++++++++------- .../e7_semantic_iot/e7_semantic_iot.py | 8 ++++++-- .../e7_semantic_iot_solutions.py | 16 ++++++++++------ 10 files changed, 95 insertions(+), 59 deletions(-) diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py index 80c38752..02e6eb61 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py @@ -47,14 +47,14 @@ # ToDo: Enter your context broker host and port, e.g http://localhost:1026 CB_URL = "http://localhost:1026" +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/' +SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, # e.g. ../e3_my_entities.json diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py index 2b47d9d3..e65431a6 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py @@ -47,14 +47,14 @@ # ToDo: Enter your context broker host and port, e.g http://localhost:1026 CB_URL = "http://localhost:1026" -# FIWARE-Service -SERVICE = 'filip_tutorial' -# FIWARE-Service path -# ToDo: Change the name of your service-path to something unique. If you run +# ToDo: Change the name of your service to something unique. If you run # on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. +# collisions. You will use this service through the whole tutorial. # If you forget to change it an error will be raised! -SERVICE_PATH = '/your_path' +# FIWARE-Service +SERVICE = 'filip_tutorial' +# FIWARE-Servicepath +SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, # e.g. ./e3_my_entities.json diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py index efffa403..885dac39 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py @@ -49,15 +49,19 @@ # ToDo: If required enter your username and password MQTT_USER = "" MQTT_PW = "" +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' # Path to json-files to device configuration data for follow up exercises WRITE_GROUPS_FILEPATH = Path("../e4_iot_thermal_zone_sensors_groups.json") diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py index 4bfa7930..0e44927b 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py @@ -49,15 +49,20 @@ # ToDo: If required, enter your username and password MQTT_USER = "" MQTT_PW = "" + +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Service path -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance, this is very important in order to avoid user -# collisions. You will use this service path throughout the whole tutorial. -# If you forget to change it, an error will raise! -SERVICE_PATH = '/your_path' -APIKEY = SERVICE_PATH.strip('/') +# FIWARE-Servicepath +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' # Path to json-files to device configuration data for follow-up exercises WRITE_GROUPS_FILEPATH = Path( diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py index 36478cc8..ae0eb254 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py @@ -68,16 +68,21 @@ MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" # ToDo: If required enter your username and password MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" + +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index d928651f..2bb9eddc 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -68,16 +68,21 @@ MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" # ToDo: If required enter your username and password MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" + +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/your_path' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index d360c8e7..499bfa15 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -59,15 +59,19 @@ MQTT_USER = "" MQTT_PW = "" +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index 27696488..73b73f5b 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -56,17 +56,22 @@ MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" # ToDo: If required enter your username and password MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/your_path' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' + UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py index d166e965..c61f4993 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py @@ -54,8 +54,12 @@ # on a shared instance this very important in order to avoid user # collisions. You will use this service path through the whole tutorial. # If you forget to change it an error will be raised! -SERVICE_PATH = '/' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' # Path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py index 1256e30a..c10f9fee 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py @@ -47,15 +47,19 @@ # ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 IOTA_URL = "http://localhost:4041" +# ToDo: Change the name of your service to something unique. If you run +# on a shared instance this very important in order to avoid user +# collisions. You will use this service through the whole tutorial. +# If you forget to change it an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' # FIWARE-Servicepath -# ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user -# collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! -SERVICE_PATH = '/your_path' -APIKEY = SERVICE_PATH.strip('/') +SERVICE_PATH = '/' + +# ToDo: Change the APIKEY to something unique. This represent the "token" +# for IoT devices to connect (send/receive data ) with the platform. In the +# context of MQTT, APIKEY is linked with the topic used for communication. +APIKEY = 'your_apikey' # Path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ From 529a89a342167272d3394e58789fc2ad9c055da6 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 27 Mar 2024 11:16:02 +0100 Subject: [PATCH 10/30] fix: fix parsing and deprecated methods in exercise 6 solution --- .../e6_timeseries_data_solution.py | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index 73b73f5b..04ed5a31 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -14,6 +14,7 @@ """ # ## Import packages +import json from pathlib import Path import time from typing import List @@ -21,7 +22,7 @@ from uuid import uuid4 import pandas as pd import paho.mqtt.client as mqtt -from pydantic import parse_file_as +from pydantic import TypeAdapter import matplotlib.pyplot as plt # import from filip from filip.clients.ngsi_v2 import \ @@ -84,6 +85,15 @@ READ_SUBSCRIPTIONS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") +# Opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file, + open(READ_SUBSCRIPTIONS_FILEPATH, 'r') as subscriptions_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) + json_subscriptions = json.load(subscriptions_file) + + # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature TEMPERATURE_MIN = -5 # minimal ambient temperature @@ -91,7 +101,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds +COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -111,9 +121,10 @@ temp_start=TEMPERATURE_ZONE_START) # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) - sub = parse_file_as(List[Subscription], READ_SUBSCRIPTIONS_FILEPATH)[0] + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) + sub = TypeAdapter(List[Subscription]).validate_python(json_subscriptions)[0] + # sub = parse_file_as(List[Subscription], READ_SUBSCRIPTIONS_FILEPATH)[0] sub.notification.mqtt.topic = TOPIC_CONTROLLER sub.notification.mqtt.user = MQTT_USER sub.notification.mqtt.passwd = MQTT_PW @@ -141,6 +152,7 @@ # Implement a callback function that gets triggered when the # command is sent to the device. The incoming command schould update the # heater attribute of the simulation model + def on_command(client, obj, msg): """ Callback for incoming commands @@ -170,7 +182,7 @@ def on_measurement(client, obj, msg): """ Callback for measurement notifications """ - message = Message.parse_raw(msg.payload) + message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] # retrieve the value of temperature attribute @@ -238,7 +250,7 @@ def on_measurement(client, obj, msg): mqttc.loop_start() # Create a loop that publishes every second a message to the broker - # that holds the simulation time "simtime" and the corresponding + # that holds the simulation time "sim_time" and the corresponding # temperature "temperature" the loop should. You may use the `object_id` # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, @@ -247,16 +259,16 @@ def on_measurement(client, obj, msg): # publish the simulated ambient temperature mqttc.publish(device_id=weather_station.device_id, payload={"temperature": sim_model.t_amb, - "simtime": sim_model.t_sim}) + "sim_time": 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}) + "sim_time": sim_model.t_sim}) - # publish the 'simtime' for the heater device + # publish the 'sim_time' for the heater device mqttc.publish(device_id=heater.device_id, - payload={"simtime": sim_model.t_sim}) + payload={"sim_time": sim_model.t_sim}) time.sleep(1) # simulation step for next loop @@ -287,13 +299,13 @@ def on_measurement(client, obj, msg): # drop unnecessary index levels history_weather_station = history_weather_station.droplevel( level=("entityId", "entityType"), axis=1) - history_weather_station['simtime'] = pd.to_numeric( - history_weather_station['simtime'], downcast="float") + history_weather_station['sim_time'] = pd.to_numeric( + history_weather_station['sim_time'], downcast="float") history_weather_station['temperature'] = pd.to_numeric( history_weather_station['temperature'], downcast="float") # ToDo: plot the results fig, ax = plt.subplots() - ax.plot(history_weather_station['simtime'], + ax.plot(history_weather_station['sim_time'], history_weather_station['temperature']) ax.set_xlabel('time in s') ax.set_ylabel('ambient temperature in °C') @@ -313,13 +325,13 @@ def on_measurement(client, obj, msg): # ToDo: drop unnecessary index levels history_zone_temperature_sensor = history_zone_temperature_sensor.droplevel( level=("entityId", "entityType"), axis=1) - history_zone_temperature_sensor['simtime'] = pd.to_numeric( - history_zone_temperature_sensor['simtime'], downcast="float") + history_zone_temperature_sensor['sim_time'] = pd.to_numeric( + history_zone_temperature_sensor['sim_time'], downcast="float") history_zone_temperature_sensor['temperature'] = pd.to_numeric( history_zone_temperature_sensor['temperature'], downcast="float") # ToDo: plot the results fig2, ax2 = plt.subplots() - ax2.plot(history_zone_temperature_sensor['simtime'], + ax2.plot(history_zone_temperature_sensor['sim_time'], history_zone_temperature_sensor['temperature']) ax2.set_xlabel('time in s') ax2.set_ylabel('zone temperature in °C') @@ -340,13 +352,13 @@ def on_measurement(client, obj, msg): # ToDo: drop unnecessary index levels history_heater = history_heater.droplevel( level=("entityId", "entityType"), axis=1) - history_heater['simtime'] = pd.to_numeric( - history_heater['simtime'], downcast="float") + history_heater['sim_time'] = pd.to_numeric( + history_heater['sim_time'], downcast="float") history_heater['heater_on_info'] = pd.to_numeric( history_heater['heater_on_info'], downcast="float") # ToDo: plot the results fig3, ax3 = plt.subplots() - ax3.plot(history_heater['simtime'], + ax3.plot(history_heater['sim_time'], history_heater['heater_on_info']) ax3.set_xlabel('time in s') ax3.set_ylabel('set point') From 78e9ad1849227fcbdb0604abe014e4f46ca19cdd Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 27 Mar 2024 15:25:31 +0100 Subject: [PATCH 11/30] fix: fix parsing in exercise 7 solution --- .../e7_semantic_iot_solutions.py | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py index c10f9fee..1469c594 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py @@ -19,9 +19,10 @@ """ # ## Import packages +import json from pathlib import Path from typing import List -from pydantic import parse_file_as +from pydantic import TypeAdapter # import from filip from filip.clients.ngsi_v2 import \ @@ -69,6 +70,14 @@ READ_ENTITIES_FILEPATH = \ Path("../e3_context_entities_solution_entities.json") +# Opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file, + open(READ_ENTITIES_FILEPATH, 'r') as entities_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) + json_entities = json.load(entities_file) + # ## Main script if __name__ == '__main__': # create a fiware header object @@ -79,9 +88,9 @@ clear_context_broker(url=CB_URL, fiware_header=fiware_header) # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) - entities = parse_file_as(List[ContextEntity], READ_ENTITIES_FILEPATH) + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) + entities = TypeAdapter(List[ContextEntity]).validate_python(json_entities) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) for entity in entities: cbc.post_entity(entity=entity) @@ -175,22 +184,22 @@ heater.add_attribute(ref_thermal_zone) iotac.update_device(device=heater) - # ToDo: Add unit metadata to the temperature and simtime attributes of + # ToDo: Add unit metadata to the temperature and sim_time attributes of # all devices. Here we use unitcode information. If you can not find # your unit code you can use our unit models for help # get code from Unit model for seconds code = Unit(name="second [unit of time]").code - # add metadata to simtime attribute of the all devices - metadata_simtime = NamedMetadata(name="unitCode", + # add metadata to sim_time attribute of the all devices + metadata_sim_time = NamedMetadata(name="unitCode", type="Text", value=code) - attr_simtime = weather_station.get_attribute( - attribute_name="simtime" + attr_sim_time = weather_station.get_attribute( + attribute_name="sim_time" ) - attr_simtime.metadata = metadata_simtime - weather_station.update_attribute(attribute=attr_simtime) - zone_temperature_sensor.update_attribute(attribute=attr_simtime) - heater.update_attribute(attribute=attr_simtime) + attr_sim_time.metadata = metadata_sim_time + weather_station.update_attribute(attribute=attr_sim_time) + zone_temperature_sensor.update_attribute(attribute=attr_sim_time) + heater.update_attribute(attribute=attr_sim_time) # ToDo: get code from Unit model for degree celsius code = Unit(name="degree Celsius").code @@ -206,8 +215,8 @@ weather_station.update_attribute(attribute=attr_t_amb) metadata_t_zone = NamedMetadata(name="unitCode", - type="Text", - value=code) + type="Text", + value=code) attr_t_zone = zone_temperature_sensor.get_attribute( attribute_name="temperature") attr_t_zone.metadata = metadata_t_zone @@ -225,7 +234,7 @@ # ToDo: Retrieve all ContextEntites and print them entities = cbc.get_entity_list() for entity in entities: - print(entity.json(indent=2)) + print(entity.model_dump_json(indent=2)) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) clear_context_broker(url=CB_URL, fiware_header=fiware_header) From b910989ea3a208824d02960c0fa4ac69f197751d Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 27 Mar 2024 15:27:17 +0100 Subject: [PATCH 12/30] chore: make opening files consistent with as in other exercises --- .../e5_iot_thermal_zone_control_solution.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index 2bb9eddc..79a4f797 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -99,11 +99,11 @@ READ_DEVICES_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_solution_devices.json") -with open(READ_GROUPS_FILEPATH, 'r') as file: - json_groups = json.load(file) - -with open(READ_DEVICES_FILEPATH, 'r') as file: - json_devices = json.load(file) +# Opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature @@ -136,10 +136,8 @@ history_heater = [] # Create clients and restore devices and groups from file - ta1 = TypeAdapter(List[ServiceGroup]) - groups = ta1.validate_python(json_groups) - ta2 = TypeAdapter(List[Device]) - devices = ta2.validate_python(json_devices) + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header) iotac.post_groups(service_groups=groups) From e4bfc59effb9530d87863da4a1a7ce6121dbca44 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Thu, 4 Apr 2024 12:19:53 +0200 Subject: [PATCH 13/30] chore: format e1_virtual_weatherstation exercise and solution --- .../e1_virtual_weatherstation.py | 39 +++++++++---------- .../e1_virtual_weatherstation_solution.py | 32 +++++++-------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py index 33d2daab..a7797f15 100644 --- a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py +++ b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py @@ -3,7 +3,7 @@ # Create a virtual IoT device that simulates the ambient temperature and # publishes it via MQTT. The simulation function is already predefined. -# This exercise to give a simple introduction to the communication via MQTT. +# This exercise gives a simple introduction to the communication via MQTT. # The input sections are marked with 'ToDo' @@ -31,16 +31,15 @@ # import simulation model from tutorials.ngsi_v2.simulation_model import SimulationModel - # ## Parameters -# ToDo: Enter your mqtt broker url and port, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url and port, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL = "mqtt://test.mosquitto.org:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" # ToDo: Create a unique topic that your weather station will publish on, -# e.g. by using a uuid +# e.g. by using a uuid. UNIQUE_ID = str(uuid4()) TOPIC_WEATHER_STATION = f"fiware_workshop/{UNIQUE_ID}/weather_station" @@ -50,8 +49,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds - +COM_STEP = 60 * 60 # 60 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -64,14 +62,15 @@ # define a list for storing historical data history_weather_station = [] - # ToDo: create a MQTTv5 client with paho-mqtt + # ToDo: Create an MQTTv5 client with paho-mqtt. mqttc = ... # set user data if required mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) + # ToDo: Define a callback function that will be executed when the client # receives message on a subscribed topic. It should decode your message - # and store the information for later in our history - # Note: do not change function's signature + # and store the information for later in our history. + # Note: Do not change the function's signature! def on_message(client, userdata, msg): """ Callback function for incoming messages @@ -79,7 +78,7 @@ def on_message(client, userdata, msg): # decode the payload payload = msg.payload.decode('utf-8') # ToDo: Parse the payload using the `json` package and write it to - # the history + # the history. ... pass @@ -88,7 +87,7 @@ def on_message(client, userdata, msg): # or a topic specific callback with `mqttc.message_callback_add()` mqttc.on_message = on_message - # ToDO: connect to the mqtt broker and subscribe to your topic + # ToDo: Connect to the mqtt broker and subscribe to your topic. mqtt_url = urlparse(MQTT_BROKER_URL) ... @@ -98,27 +97,27 @@ def on_message(client, userdata, msg): - # ToDo: print and subscribe to the weather station topic + # ToDo: Print and subscribe to the weather station topic. print(f"WeatherStation topic:\n {TOPIC_WEATHER_STATION}") mqttc.subscribe(topic=TOPIC_WEATHER_STATION) # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every second a message to the broker + # ToDo: Create a loop that publishes every 20 milliseconds a message to the broker # that holds the simulation time "t_sim" and the corresponding temperature - # "t_amb" + # "t_amb". for t_sim in range(sim_model.t_start, int(sim_model.t_end + COM_STEP), int(COM_STEP)): - # ToDo: publish the simulated ambient temperature + # ToDo: Publish the simulated ambient temperature. ... # simulation step for next loop sim_model.do_step(int(t_sim + COM_STEP)) - time.sleep(1) + time.sleep(0.2) # close the mqtt listening thread mqttc.loop_stop() @@ -127,9 +126,9 @@ def on_message(client, userdata, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["t_sim"] for item in history_weather_station] + t_simulation = [item["t_sim"]/3600 for item in history_weather_station] temperature = [item["t_amb"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') plt.show() diff --git a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py index 5682a46e..e3043da4 100644 --- a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py +++ b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py @@ -3,7 +3,7 @@ # Create a virtual IoT device that simulates the ambient temperature and # publishes it via MQTT. The simulation function is already predefined. -# This exercise to give a simple introduction to the communication via MQTT. +# This exercise gives a simple introduction to the communication via MQTT. # The input sections are marked with 'ToDo' @@ -32,14 +32,14 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your mqtt broker url and port, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url and port, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL = "mqtt://test.mosquitto.org:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" MQTT_PW = "" # ToDo: Create a unique topic that your weather station will publish on, -# e.g. by using a uuid +# e.g. by using a uuid. UNIQUE_ID = str(uuid4()) TOPIC_WEATHER_STATION = f"fiware_workshop/{UNIQUE_ID}/weather_station" @@ -49,7 +49,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 1 # 60 min communication step in seconds +COM_STEP = 60 * 60 # 60 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -59,18 +59,18 @@ temp_max=TEMPERATURE_MAX, temp_min=TEMPERATURE_MIN) - # define lists to store historical data + # define a list for storing historical data history_weather_station = [] - # ToDo: create a MQTTv5 client with paho-mqtt + # ToDo: Create an MQTTv5 client with paho-mqtt. mqttc = mqtt.Client(protocol=mqtt.MQTTv5) # set user data if required mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) # ToDo: Define a callback function that will be executed when the client # receives message on a subscribed topic. It should decode your message - # and store the information for later in our history - # Note: do not change function's signature! + # and store the information for later in our history. + # Note: Do not change the function's signature! def on_message(client, userdata, msg): """ Callback function for incoming messages @@ -78,7 +78,7 @@ def on_message(client, userdata, msg): # decode the payload payload = msg.payload.decode('utf-8') # ToDo: Parse the payload using the `json` package and write it to - # the history + # the history. history_weather_station.append(json.loads(payload)) @@ -86,7 +86,7 @@ def on_message(client, userdata, msg): # or a topic specific callback with `mqttc.message_callback_add()` mqttc.on_message = on_message - # ToDO: connect to the mqtt broker and subscribe to your topic + # ToDo: Connect to the mqtt broker and subscribe to your topic. mqtt_url = urlparse(MQTT_BROKER_URL) mqttc.connect(host=mqtt_url.hostname, port=mqtt_url.port, @@ -96,27 +96,27 @@ def on_message(client, userdata, msg): clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY, properties=None) - # ToDo: print and subscribe to the weather station topic + # ToDo: Print and subscribe to the weather station topic. print(f"WeatherStation topic:\n {TOPIC_WEATHER_STATION}") mqttc.subscribe(topic=TOPIC_WEATHER_STATION) # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every second a message to the broker + # ToDo: Create a loop that publishes every 20 milliseconds a message to the broker # that holds the simulation time "t_sim" and the corresponding temperature - # "t_amb" + # "t_amb". for t_sim in range(sim_model.t_start, int(sim_model.t_end + COM_STEP), int(COM_STEP)): - # ToDo: publish the simulated ambient temperature + # ToDo: Publish the simulated ambient temperature. mqttc.publish(topic=TOPIC_WEATHER_STATION, payload=json.dumps({"t_amb": sim_model.t_amb, "t_sim": sim_model.t_sim})) # simulation step for next loop sim_model.do_step(int(t_sim + COM_STEP)) - time.sleep(0.1) + time.sleep(0.2) # close the mqtt listening thread mqttc.loop_stop() From 70112a6278ec489ca4cb79be5fe6865bc626e8e2 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Thu, 4 Apr 2024 12:31:45 +0200 Subject: [PATCH 14/30] chore: format e2_healthcheck exercise and solution --- .../ngsi_v2/e2_healthcheck/e2_healthcheck.py | 27 ++++++++++--------- .../e2_healthcheck/e2_healthcheck_solution.py | 16 +++++------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck.py b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck.py index 26b67e24..1ce7af44 100644 --- a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck.py +++ b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck.py @@ -1,14 +1,14 @@ """ # # Exercise 2: Service Health Check -# Create one or multiple filip clients and check if the corresponding services +# Create one or multiple FiLiP clients and check if the corresponding services # are up and running by accessing their version information. # The input sections are marked with 'ToDo' # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create filip ngsi_v2 clients for the individual services and check for +# 2. Create FiLiP ngsi_v2 clients for the individual services and check for # their version # 3. Create a config object for the ngsi_v2 multi client (HttpClient), # create the multi client and again check for services' versions @@ -23,29 +23,32 @@ QuantumLeapClient # ## Parameters -# ToDo: Enter your context broker url and port, e.g. http://localhost:1026 +# 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 +# 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 +# ToDo: Enter your QuantumLeap url and port, e.g. http://localhost:8668. QL_URL = "http://localhost:8668" # ## Main script if __name__ == "__main__": - # Create a single client for each service and check the service for - # its version + # ToDo: Create a single client for each service and check the service for + # its version. cbc = ContextBrokerClient(url=CB_URL) - print(cbc.get_version()) + print(f"Context Broker Client: {cbc.get_version()}") iotac = ... + print(f"IoTA Client: {iotac.get_version()}") qlc = ... + print(f"Quantum Leap Client: {qlc.get_version()}") - # ToDo: Create a configuration object for a multi client + # ToDo: Create a configuration object for a multi client. config = HttpClientConfig(...) - # ToDo: Create a multi client check again all services for their version + # ToDo: Create a multi client check again all services for their version. multic = HttpClient(config=config) - print(multic.cb.get_version()) - ... + print(f"Multi Client (Context Broker): {multic.cb.get_version()}\n" + f"Multi Client (IoTA): {multic.iota.get_version()}\n" + f"Multi Client (Quantum Leap): {multic.timeseries.get_version()}") diff --git a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py index 0d4b315c..9cda3daf 100644 --- a/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py +++ b/tutorials/ngsi_v2/e2_healthcheck/e2_healthcheck_solution.py @@ -1,14 +1,14 @@ """ # # Exercise 2: Service Health Check -# Create one or multiple filip clients and check if the corresponding services +# Create one or multiple FiLiP clients and check if the corresponding services # are up and running by accessing their version information. # The input sections are marked with 'ToDo' # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create filip ngsi_v2 clients for the individual services and check for +# 2. Create FiLiP ngsi_v2 clients for the individual services and check for # their version # 3. Create a config object for the ngsi_v2 multi client (HttpClient), # create the multi client and again check for services' versions @@ -23,17 +23,17 @@ QuantumLeapClient # ## Parameters -# ToDo: Enter your context broker url and port, e.g. http://localhost:1026 +# 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 +# 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 +# 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 + # its version. cbc = ContextBrokerClient(url=CB_URL) print(f"Context Broker Client: {cbc.get_version()}") @@ -43,10 +43,10 @@ qlc = QuantumLeapClient(url=QL_URL) print(f"Quantum Leap Client: {qlc.get_version()}") - # ToDo: Create a configuration object for a multi client + # 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 + # ToDo: Create a multi client check again all services for their version. multic = HttpClient(config=config) print(f"Multi Client (Context Broker): {multic.cb.get_version()}\n" From 93e16657d36d71c009f48d46c25ab2a4e3bbea12 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Thu, 4 Apr 2024 13:17:58 +0200 Subject: [PATCH 15/30] chore: format e3_context_entities exercise and solution --- .../e3_context_entities.py | 78 +++++++++---------- .../e3_context_entities_solution.py | 64 +++++++-------- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py index 02e6eb61..7f5558ef 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py @@ -1,11 +1,11 @@ """ # # Exercise 3: Context Entities and Relationships -# Create building context entity of type 'Building' according to FIWARE's +# Create a building context entity of type 'Building' according to FIWARE's # SmartData Models with the properties: `id`, `type`, `address`, `category`, # https://github.com/smart-data-models/dataModel.Building/blob/master/Building/doc/spec.md -# For the single properties check on the "Data Model description of +# For the single properties check the "Data Model description of # properties" section. The input sections are marked with 'ToDo' # #### Steps to complete: @@ -18,12 +18,12 @@ # ContextBroker. Afterwards, check if the Context Broker returns the # correct information about your building # 6. Create an `opening hours` attribute add them to the server -# 7. Retrieve the `opening hours` manipulate them and update the model at the +# 7. Retrieve the `opening hours`, manipulate them and update the model in the # server # 8. Repeat the procedure with a thermal zone. Currently, the smart data # models hold no definition of a thermal zone. Therefore, we first only add a # description attribute. -# 9. Add a `Relationship` attribute to your thermal zone with name +# 9. Add a `Relationship` attribute to your thermal zone with the name # `refBuilding` and type `Relationship` pointing to your building and post # the model to the context broker # 10. Add a `Relationship` attribute to your building with name @@ -44,16 +44,16 @@ from filip.utils.simple_ql import QueryString # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, @@ -78,7 +78,7 @@ type="Array", value=["office"]) - # ToDo: create a property `address` for your building. Follow the full yaml + # ToDo: Create a property `address` for your building. Follow the full yaml # description in the specifications. It reuses the specification from # here: https://schema.org/PostalAddress address = NamedContextAttribute(name="address", @@ -89,7 +89,7 @@ - # ToDo: create a `description` property for your building + # ToDo: Create a `description` property for your building. building_description = NamedContextAttribute(...) @@ -103,10 +103,10 @@ category, address]) - # ToDo: Create a context broker client and add the fiware_header + # ToDo: Create a context broker client and add the fiware_header. cbc = ... # ToDo: Send your building model to the context broker. Check the client - # for the proper function + # for proper functioning. ... # Update your local building model with the one from the server @@ -114,9 +114,9 @@ entity_type=building.type) # print your `building model` as json - print(f"This is your building model: \n {building.json(indent=2)} \n") + print(f"This is your building model: \n {building.model_dump_json(indent=2)} \n") - # ToDo: create a `opening hours` property and add it to the building object + # ToDo: Create an `opening hours` property and add it to the building object # in the context broker. Do not update the whole entity! In real # scenarios it might have been modified by other users. opening_hours = NamedContextAttribute(name="openingHours", @@ -132,13 +132,13 @@ entity_type=building.type, attrs=[opening_hours]) - # ToDo: retrieve and print the opening hours + # ToDo: Retrieve and print the property `opening hours`. hours = cbc.get_attribute_value(...) - print(f"Your opening hours: {hours} \n" ) + print(f"Your opening hours: {hours} \n") - # ToDo: modify the `opening hours` of the building + # ToDo: Modify the property `opening hours` of the building. cbc.update_attribute_value(...) @@ -152,10 +152,10 @@ entity_type=building.type) # print your building - print(f"Your updated building model: \n {building.json(indent=2)}") + print(f"Your updated building model: \n {building.model_dump_json(indent=2)} \n") - # ToDo: Create an entity of thermal zone and add a description property - # to it + # ToDo: Create an entity of the thermal zone and add a description property + # to it. thermal_zone = ContextEntity(id="ThermalZone:001", type="ThermalZone") @@ -165,8 +165,8 @@ "the entire building") thermal_zone.add_attributes(attrs=[thermal_zone_description]) - # ToDo: Create and a property that references your building model. Use the - # `Relationship` for type and `refBuilding` for its name + # ToDo: Create and add a property that references your building model. Use the + # `Relationship` for type and `refBuilding` for its name. ref_building = NamedContextAttribute(name="refBuilding", type="Relationship", value=building.id) @@ -174,15 +174,15 @@ # print all relationships of your thermal zone for relationship in thermal_zone.get_relationships(): - print(f"Relationship properties of your thermal zone mdoel: \n " - f"{relationship.json(indent=2)} \n") + print(f"Relationship properties of your thermal zone model: \n " + f"{relationship.model_dump_json(indent=2)} \n") - # ToDo: Post your thermal zone model to the context broker + # ToDo: Post your thermal zone model to the context broker. ... ... - # ToDo: Create and a property that references your thermal zone. Use the + # ToDo: Create and add a property that references your thermal zone. Use the # `Relationship` for type and `hasZone` for its name. Make sure that # your local model and the server model are in sync afterwards. ref_zone = NamedContextAttribute(...) @@ -194,34 +194,34 @@ building = cbc.get_entity(entity_id=building.id, entity_type=building.type) - # ToDo: create a filter request that retrieves all entities from the - # server`that have `refBuilding` attribute that references your building + # ToDo: Create a filter request that retrieves all entities from the + # server that have `refBuilding` attribute that reference your building # by using the FIWARE's simple query language. - # `filip.utils.simple_ql` module helps you to validate your query string - # 1. prepare the query string using the `filip.utils.simple_ql` - # 2. use the string in a context broker request and retrieve the entities. + # `filip.utils.simple_ql` module helps you to validate your query string. + # 1. Prepare the query string using the `filip.utils.simple_ql`. + # 2. Use the string in a context broker request and retrieve the entities. query = QueryString(qs=("refBuilding", "==", building.id)) for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the building: " - f"\n {entity.json(indent=2)}\n") + f"\n {entity.model_dump_json(indent=2)}\n") - # ToDo: create a filter request that retrieves all entities from the - # server`that have `hasZone' attribute that references your thermal zone + # ToDo: Create a filter request that retrieves all entities from the + # server that have `hasZone` attribute that reference your thermal zone # by using the FIWARE's simple query language. - # `filip.utils.simple_ql` module helps you to validate your query string - # 1. prepare the query string using the `filip.utils.simple_ql` - # 2. use the string in a context broker request and retrieve the entities. + # `filip.utils.simple_ql` module helps you to validate your query string. + # 1. Prepare the query string using the `filip.utils.simple_ql`. + # 2. Use the string in a context broker request and retrieve the entities. query = ... for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the thermal zone: " - f"\n {entity.json(indent=2)} \n") + f"\n {entity.model_dump_json(indent=2)} \n") # write entities to file and clear server state assert WRITE_ENTITIES_FILEPATH.suffix == '.json', \ f"Wrong file extension! {WRITE_ENTITIES_FILEPATH.suffix}" WRITE_ENTITIES_FILEPATH.touch(exist_ok=True) with WRITE_ENTITIES_FILEPATH.open('w', encoding='utf-8') as f: - entities = [item.dict() for item in cbc.get_entity_list()] + entities = [item.model_dump() for item in cbc.get_entity_list()] json.dump(entities, f, ensure_ascii=False, indent=2) clear_context_broker(url=CB_URL, fiware_header=fiware_header) diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py index e65431a6..90411102 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py @@ -1,11 +1,11 @@ """ # # Exercise 3: Context Entities and Relationships -# Create building context entity of type 'Building' according to FIWARE's +# Create a building context entity of type 'Building' according to FIWARE's # SmartData Models with the properties: `id`, `type`, `address`, `category`, # https://github.com/smart-data-models/dataModel.Building/blob/master/Building/doc/spec.md -# For the single properties check on the "Data Model description of +# For the single properties check the "Data Model description of # properties" section. The input sections are marked with 'ToDo' # #### Steps to complete: @@ -18,12 +18,12 @@ # ContextBroker. Afterwards, check if the Context Broker returns the # correct information about your building # 6. Create an `opening hours` attribute add them to the server -# 7. Retrieve the `opening hours` manipulate them and update the model at the +# 7. Retrieve the `opening hours`, manipulate them and update the model in the # server # 8. Repeat the procedure with a thermal zone. Currently, the smart data # models hold no definition of a thermal zone. Therefore, we first only add a # description attribute. -# 9. Add a `Relationship` attribute to your thermal zone with name +# 9. Add a `Relationship` attribute to your thermal zone with the name # `refBuilding` and type `Relationship` pointing to your building and post # the model to the context broker # 10. Add a `Relationship` attribute to your building with name @@ -44,20 +44,20 @@ from filip.utils.simple_ql import QueryString # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, -# e.g. ./e3_my_entities.json +# e.g. ../e3_my_entities.json WRITE_ENTITIES_FILEPATH = Path("../e3_context_entities_solution_entities.json") # ## Main script @@ -78,7 +78,7 @@ type="Array", value=["office"]) - # ToDo: create a property `address` for your building. Follow the full yaml + # ToDo: Create a property `address` for your building. Follow the full yaml # description in the specifications. It reuses the specification from # here: https://schema.org/PostalAddress address = NamedContextAttribute(name="address", @@ -90,7 +90,7 @@ "streetAddress": "Any Street 5" }) - # ToDo: create a `description` property for your building + # ToDo: Create a `description` property for your building. building_description = NamedContextAttribute(name="description", type="Text", value="Small office building " @@ -103,10 +103,10 @@ category, address]) - # ToDo: Create a context broker client and add the fiware_header + # ToDo: Create a context broker client and add the fiware_header. cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) # ToDo: Send your building model to the context broker. Check the client - # for the proper function + # for proper functioning. cbc.post_entity(entity=building) # Update your local building model with the one from the server @@ -116,7 +116,7 @@ # print your `building model` as json print(f"This is your building model: \n {building.model_dump_json(indent=2)} \n") - # ToDo: create a `opening hours` property and add it to the building object + # ToDo: Create an `opening hours` property and add it to the building object # in the context broker. Do not update the whole entity! In real # scenarios it might have been modified by other users. opening_hours = NamedContextAttribute(name="openingHours", @@ -132,13 +132,13 @@ entity_type=building.type, attrs=[opening_hours]) - # ToDo: retrieve and print the opening hours + # ToDo: Retrieve and print the property `opening hours`. hours = cbc.get_attribute_value(entity_id=building.id, entity_type=building.type, attr_name=opening_hours.name) print(f"Your opening hours: {hours} \n") - # ToDo: modify the `opening hours` of the building + # ToDo: Modify the property `opening hours` of the building. cbc.update_attribute_value(entity_id=building.id, entity_type=building.type, attr_name=opening_hours.name, @@ -152,10 +152,10 @@ entity_type=building.type) # print your building - print(f"Your updated building model: \n {building.model_dump_json(indent=2)}") + print(f"Your updated building model: \n {building.model_dump_json(indent=2)} \n") - # ToDo: Create an entity of thermal zone and add a description property - # to it + # ToDo: Create an entity of the thermal zone and add a description property + # to it. thermal_zone = ContextEntity(id="ThermalZone:001", type="ThermalZone") @@ -165,8 +165,8 @@ "the entire building") thermal_zone.add_attributes(attrs=[thermal_zone_description]) - # ToDo: Create and a property that references your building model. Use the - # `Relationship` for type and `refBuilding` for its name + # ToDo: Create and add a property that references your building model. Use the + # `Relationship` for type and `refBuilding` for its name. ref_building = NamedContextAttribute(name="refBuilding", type="Relationship", value=building.id) @@ -177,7 +177,7 @@ print(f"Relationship properties of your thermal zone model: \n " f"{relationship.model_dump_json(indent=2)} \n") - # ToDo: Post your thermal zone model to the context broker + # ToDo: Post your thermal zone model to the context broker. cbc.post_entity(entity=thermal_zone) thermal_zone = cbc.get_entity(entity_id=thermal_zone.id, entity_type=thermal_zone.type) @@ -194,23 +194,23 @@ building = cbc.get_entity(entity_id=building.id, entity_type=building.type) - # ToDo: create a filter request that retrieves all entities from the - # server`that have `refBuilding` attribute that references your building + # ToDo: Create a filter request that retrieves all entities from the + # server that have `refBuilding` attribute that reference your building # by using the FIWARE's simple query language. - # `filip.utils.simple_ql` module helps you to validate your query string - # 1. prepare the query string using the `filip.utils.simple_ql` - # 2. use the string in a context broker request and retrieve the entities. + # `filip.utils.simple_ql` module helps you to validate your query string. + # 1. Prepare the query string using the `filip.utils.simple_ql`. + # 2. Use the string in a context broker request and retrieve the entities. query = QueryString(qs=("refBuilding", "==", building.id)) for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the building: " f"\n {entity.model_dump_json(indent=2)}\n") - # ToDo: create a filter request that retrieves all entities from the - # server`that have `hasZone' attribute that references your thermal zone + # ToDo: Create a filter request that retrieves all entities from the + # server that have `hasZone` attribute that reference your thermal zone # by using the FIWARE's simple query language. - # `filip.utils.simple_ql` module helps you to validate your query string - # 1. prepare the query string using the `filip.utils.simple_ql` - # 2. use the string in a context broker request and retrieve the entities. + # `filip.utils.simple_ql` module helps you to validate your query string. + # 1. Prepare the query string using the `filip.utils.simple_ql`. + # 2. Use the string in a context broker request and retrieve the entities. query = QueryString(qs=("hasZone", "==", thermal_zone.id)) for entity in cbc.get_entity_list(q=query): print(f"All entities referencing the thermal zone: " From b9acf77cc02b3a2d971d8027ad43b25f95a145d6 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Thu, 4 Apr 2024 15:45:06 +0200 Subject: [PATCH 16/30] chore: format e4_iot_thermal_zone_sensors exercise and solution --- .../e4_iot_thermal_zone_sensors.py | 127 +++++++++--------- .../e4_iot_thermal_zone_sensors_solution.py | 53 ++++---- 2 files changed, 91 insertions(+), 89 deletions(-) diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py index 885dac39..e4029d3b 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py @@ -1,9 +1,9 @@ """ # # Exercise 4: Virtual Thermal Zone -# Create two virtual IoT device. One of them represents the temperature -# sensor for the air temperature of a the thermal zone, whereas the second -# represents a virtual weather station. Both devices publish there values to +# Create two virtual IoT devices. One of them represents the temperature +# sensor for the air temperature of a thermal zone, whereas the second +# represents a virtual weather station. Both devices publish their values to # the platform via MQTT. Use the simulation model of # e1_virtual_weatherstation.py # @@ -11,7 +11,7 @@ # # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create a service group and two devices +# 2. Create a service group and two corresponding devices # 3. Provision the service group and the devices # 4. Create an MQTT client using the filip.client.mqtt package and register # your service group and your devices @@ -31,7 +31,7 @@ import paho.mqtt.client as mqtt # import from filip -from filip.clients.ngsi_v2 import ContextBrokerClient +from filip.clients.ngsi_v2 import ContextBrokerClient, IoTAClient from filip.clients.mqtt import IoTAMQTTClient from filip.models.base import FiwareHeader from filip.models.ngsi_v2.iot import Device, DeviceAttribute, ServiceGroup @@ -40,30 +40,31 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g. http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL = "mqtt://localhost:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" + # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' -# Path to json-files to device configuration data for follow up exercises +# path to json-files to device configuration data for follow-up exercises WRITE_GROUPS_FILEPATH = Path("../e4_iot_thermal_zone_sensors_groups.json") WRITE_DEVICES_FILEPATH = Path("../e4_iot_thermal_zone_sensors_devices.json") @@ -96,17 +97,16 @@ history_weather_station = [] history_zone_temperature_sensor = [] - # Create a service group and add it to your + # create a service group with your api key service_group = ServiceGroup(apikey=APIKEY, resource="/iot/json") - # ToDo: create two IoTA-MQTT devices for the weather station and the zone + # ToDo: Create two IoTA-MQTT devices for the weather station and the zone # temperature sensor. Also add the simulation time as `active attribute` # to each device! - # # create the weather station device - # create the simtime attribute and add during device creation - t_sim = DeviceAttribute(name='simtime', + # create the `sim_time` attribute and add it to the weather station's attributes + t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") @@ -120,7 +120,7 @@ commands=[]) # create a temperature attribute and add it via the api of the - # `device`-model. Use the 't_amb' as `object_id`. `object_id` specifies + # `device`-model. Use the `t_amb` as `object_id`. `object_id` specifies # what key will be used in the MQTT Message payload t_amb = DeviceAttribute(name='temperature', object_id='t_amb', @@ -128,8 +128,8 @@ weather_station.add_attribute(t_amb) - # ToDo: create the zone temperature device add the t_sim attribute upon - # creation + # ToDo: Create the zone temperature device and add the `t_sim` attribute upon + # creation. zone_temperature_sensor = Device(...) @@ -138,51 +138,50 @@ - # ToDo: Create the temperature attribute. Use the 't_zone' as `object_id`. - # `object_id` specifies what key will be used in the MQTT Message payload + # ToDo: Create the temperature attribute. Use the `t_zone` as `object_id`. + # `object_id` specifies what key will be used in the MQTT Message payload. t_zone = DeviceAttribute(...) - zone_temperature_sensor.add_attribute(t_zone) - # ToDo: Create an IoTAClient + # ToDo: Create an IoTAClient. iotac = ... - # ToDo: Provision service group and add it to your IoTAMQTTClient + # ToDo: Provision service group and add it to your IoTAMQTTClient. ... - # ToDo: Provision the devices at the IoTA-Agent - # provision the WeatherStation device + # ToDo: Provision the devices at the IoTA-Agent. + # provision the weather station device iotac.post_device(device=weather_station, update=True) - # ToDo: provision the zone temperature device + # ToDo: Provision the zone temperature device. ... - # ToDo: Check in the context broker if the entities corresponding to your - # devices where correctly created - # ToDo: Create a context broker client + # ToDo: Create a context broker client. + # ToDo: Check in the context broker whether the entities corresponding to your + # devices were correctly created. cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) - # Get WeatherStation entity - print(cbc.get_entity(weather_station.entity_name).json(indent=2)) - # ToDo: Get ZoneTemperatureSensor entity + # get weather station entity + print(f"Weather station:\n{cbc.get_entity(weather_station.entity_name).model_dump_json(indent=2)}") + # ToDo: Get zone temperature sensor entity. print(...) - # ToDo: create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient + # ToDo: Create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient. mqttc = IoTAMQTTClient(protocol=...) - # ToDo: Register the service group with your MQTT-Client + # ToDo: Register the service group with your MQTT-Client. mqttc.add_service_group(service_group=service_group) - # ToDo: Register devices with your MQTT-Client + # ToDo: Register devices with your MQTT-Client. # register the weather station mqttc.add_device(weather_station) - # ToDo: register the zone temperature sensor + # ToDo: Register the zone temperature sensor. ... - # The IoTAMQTTClient automatically creates the outgoing topics from the + # The IoTAMQTTClient automatically creates outgoing topics from the # device configuration during runtime. Hence, we need to construct them - # manually in order to subscribe to them. This is usually not required as - # only the platform should listen to incoming traffic. - # if you want to listen subscribe to the following topics: + # manually in order to subscribe to them. This is usually not required as + # only the platform should listen to the incoming traffic. + # If you want to listen subscribe to the following topics: # "/json///attrs" # "/json///attrs" - # ToDO: connect to the mqtt broker and subscribe to your topic + # ToDO: Connect to the MQTT broker and subscribe to your topic. ... @@ -197,39 +196,39 @@ # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every second 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. + # ToDo: Create a loop that publishes a message every 100 milliseconds + # to the broker that holds the simulation time `sim_time` and the + # corresponding temperature `temperature`. You may use the `object_id` + # or the attribute name as a key in your payload. 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}) + "sim_time": sim_model.t_sim}) - # ToDo: publish the simulated zone temperature + # ToDo: Publish the simulated zone temperature. ... - # simulation step for next loop + # simulation step for the next loop sim_model.do_step(int(t_sim + COM_STEP)) # wait for one second before publishing the next values - time.sleep(1) + time.sleep(0.1) - # Get corresponding entities and write values to history + # get corresponding entities and store the data 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, + {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) - # ToDo: Get ZoneTemperatureSensor and write values to history + # ToDo: Get zone temperature sensor and store the data. zone_temperature_sensor_entity = ... @@ -244,20 +243,22 @@ # disconnect the mqtt device mqttc.disconnect() - # plot results + # plot the results fig, ax = plt.subplots() - t_simulation = [item["simtime"] for item in history_weather_station] + t_simulation = [item["sim_time"]/3600 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.title.set_text("Weather Station") + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') fig2, ax2 = plt.subplots() - t_simulation = [item["simtime"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax2.set_xlabel('time in s') + ax2.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in h') ax2.set_ylabel('zone temperature in °C') plt.show() diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py index 0e44927b..aadd9743 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py @@ -2,7 +2,7 @@ # # Exercise 4: Virtual Thermal Zone # Create two virtual IoT devices. One of them represents the temperature -# sensor for the air temperature of a the thermal zone, whereas the second +# sensor for the air temperature of a thermal zone, whereas the second # represents a virtual weather station. Both devices publish their values to # the platform via MQTT. Use the simulation model of # e1_virtual_weatherstation.py @@ -40,35 +40,33 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g. http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL = "mqtt://localhost:1883" -# ToDo: If required, enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" MQTT_PW = "" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' -# Path to json-files to device configuration data for follow-up exercises -WRITE_GROUPS_FILEPATH = Path( - "../e4_iot_thermal_zone_sensors_solution_groups.json") -WRITE_DEVICES_FILEPATH = Path( - "../e4_iot_thermal_zone_sensors_solution_devices.json") +# path to json-files to device configuration data for follow-up exercises +WRITE_GROUPS_FILEPATH = Path("../e4_iot_thermal_zone_sensors_solution_groups.json") +WRITE_DEVICES_FILEPATH = Path("../e4_iot_thermal_zone_sensors_solution_devices.json") # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature @@ -99,15 +97,15 @@ history_weather_station = [] history_zone_temperature_sensor = [] - # create a service group and add it to your + # create a service group with your api key service_group = ServiceGroup(apikey=APIKEY, resource="/iot/json") - # ToDo: create two IoTA-MQTT devices for the weather station and the zone + # ToDo: Create two IoTA-MQTT devices for the weather station and the zone # temperature sensor. Also add the simulation time as `active attribute` # to each device! # create the weather station device - # create the sim_time attribute and add it to the weather station's attributes + # create the `sim_time` attribute and add it to the weather station's attributes t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") @@ -122,7 +120,7 @@ commands=[]) # create a temperature attribute and add it via the api of the - # 'device'-model. Use the 't_amb' as 'object_id'. 'object_id' specifies + # `device`-model. Use the `t_amb` as `object_id`. `object_id` specifies # what key will be used in the MQTT Message payload t_amb = DeviceAttribute(name='temperature', object_id='t_amb', @@ -130,7 +128,7 @@ weather_station.add_attribute(t_amb) - # ToDo: Create the zone temperature device add the t_sim attribute upon + # ToDo: Create the zone temperature device and add the `t_sim` attribute upon # creation. zone_temperature_sensor = Device(device_id='device:002', entity_name='urn:ngsi-ld:TemperatureSensor:001', @@ -140,7 +138,8 @@ apikey=APIKEY, attributes=[t_sim], commands=[]) - # ToDo: Create the temperature attribute. Use the 't_zone' as `object_id`. + + # ToDo: Create the temperature attribute. Use the `t_zone` as `object_id`. # `object_id` specifies what key will be used in the MQTT Message payload. t_zone = DeviceAttribute(name='temperature', object_id='t_zone', @@ -164,7 +163,7 @@ cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) # get weather station entity print(f"Weather station:\n{cbc.get_entity(weather_station.entity_name).model_dump_json(indent=2)}") - # get zone temperature sensor entity + # ToDo: Get zone temperature sensor entity. print(f"Zone temperature sensor:\n{cbc.get_entity(zone_temperature_sensor.entity_name).model_dump_json(indent=2)}") # ToDo: Create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient. @@ -176,7 +175,7 @@ # ToDo: Register devices with your MQTT-Client. # register the weather station mqttc.add_device(weather_station) - # ToDo: register the zone temperature sensor. + # ToDo: Register the zone temperature sensor. mqttc.add_device(zone_temperature_sensor) # The IoTAMQTTClient automatically creates outgoing topics from the @@ -187,7 +186,7 @@ # "/json///attrs" # "/json///attrs" - # ToDO: Connect to the mqtt broker and subscribe to your topic. + # ToDO: Connect to the MQTT broker and subscribe to your topic. mqtt_url = urlparse(MQTT_BROKER_URL) mqttc.connect(host=mqtt_url.hostname, port=mqtt_url.port, @@ -203,8 +202,8 @@ mqttc.loop_start() # ToDo: Create a loop that publishes a message every 100 milliseconds - # to the broker that holds the simulation time "sim_time" and the - # corresponding temperature "temperature". You may use the 'object_id' + # to the broker that holds the simulation time `sim_time` and the + # corresponding temperature `temperature`. You may use the `object_id` # or the attribute name as a key in your payload. for t_sim in range(sim_model.t_start, sim_model.t_end + int(COM_STEP), @@ -253,6 +252,7 @@ t_simulation = [item["sim_time"]/3600 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) + ax.title.set_text("Weather Station") ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') @@ -261,6 +261,7 @@ temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) + ax2.title.set_text("Zone Temperature Sensor") ax2.set_xlabel('time in h') ax2.set_ylabel('zone temperature in °C') From 00ea4e0a47d974280e3da67ef96d52eb557e35ef Mon Sep 17 00:00:00 2001 From: Trgovac Date: Tue, 9 Apr 2024 16:30:06 +0200 Subject: [PATCH 17/30] chore: format e5_iot_thermal_zone_control exercise and solution --- .../e5_iot_thermal_zone_control.py | 127 +++++++++--------- .../e5_iot_thermal_zone_control_solution.py | 111 +++++++-------- 2 files changed, 119 insertions(+), 119 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py index ae0eb254..bd6fcfc7 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py @@ -3,7 +3,7 @@ # Create a virtual IoT device that simulates a heater for your # thermal zone. The heater can be turned on and off via a simple hysteresis -# controller. The devices from e4_iot_thermal_zone_sensors.py will loaded +# controller. The devices from e4_iot_thermal_zone_sensors.py will be loaded # from the stored *.json-files. # The input sections are marked with 'ToDo' @@ -13,18 +13,18 @@ # 2. Retrieve the service group and device configurations of already existing # devices from the IoT-Agent # 3. Create a third device configuration for a heater holding a command -# for turning it `on` and `off`device and post it to the server +# for turning it `on` and `off` and post it to the server # 4. Create an MQTT client using the filip.client.mqtt package and register # your service group and your devices # 4. Define a callback function that will be executed when the client -# receives a command. Decode the message and set the update the state in +# receives a command. Decode the message and set the update state in # simulation model. Afterwards, acknowledge the command using the api of the # IoTAMQTTClient. # 5. Add the callback for your heater device to the IoTAMQTTClient # 6. Create an MQTT subscription for asynchronous communication that # gets triggered when the temperature attribute changes. # 7. Write a second callback that represents your controller. It should get -# triggered when the MQTTClient receive a notification message due to your +# triggered when the MQTTClient receives a notification message due to your # subscription. Add the callback to your MQTTClient using the original # paho-api (`message_callback_add`) # 8. Run the simulation and plot @@ -44,7 +44,7 @@ # import from filip from filip.clients.ngsi_v2 import ContextBrokerClient, IoTAClient from filip.clients.mqtt import IoTAMQTTClient -from filip.models.base import FiwareHeader +from filip.models.base import DataType, FiwareHeader from filip.models.ngsi_v2.context import NamedCommand from filip.models.ngsi_v2.subscriptions import Subscription, Message from filip.models.ngsi_v2.iot import \ @@ -58,52 +58,52 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL_EXPOSED = "mqtt://localhost:1883" -# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883 +# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883. MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" MQTT_PW = "" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# Path to json-files to store entity data for follow up exercises +# path to json-files to store entity data for follow up exercises WRITE_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_groups.json") WRITE_DEVICES_FILEPATH = \ Path("../e5_iot_thermal_zone_control_devices.json") WRITE_SUBSCRIPTIONS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_subscriptions.json") -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_groups.json") READ_DEVICES_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_devices.json") -with open(READ_GROUPS_FILEPATH, 'r') as file: - json_groups = json.load(file) - -with open(READ_DEVICES_FILEPATH, 'r') as file: - json_devices = json.load(file) +# opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature @@ -112,7 +112,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds +COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -135,36 +135,34 @@ history_zone_temperature_sensor = [] history_heater = [] - # Create clients and restore devices and groups from file - ta1 = TypeAdapter(List[ServiceGroup]) - groups = ta1.validate_python(json_groups) - ta2 = TypeAdapter(List[Device]) - devices = ta2.validate_python(json_devices) + # create clients and also restore devices and groups from file + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header) iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # ToDo: Get the device configurations from the server + # ToDo: Get the device configurations from the server. weather_station = iotac.get_device(device_id="device:001") zone_temperature_sensor = ... - # ToDo: Get the service group configurations from the server + # ToDo: Get the service group configurations from the server. group = iotac.get_group(resource="/iot/json", apikey=...) - # ToDo: Create and additional device holding a command attribute and - # post it to the IoT-Agent. It should be mapped to the `type` heater - # create the sim_time attribute and add during device creation + # ToDo: Create an additional device holding a command attribute and + # post it to the IoT-Agent. It should be mapped to the `type` heater. + # create the sim_time attribute and add it during device creation t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") - # ToDo: create the command attribute of name `heater_on` (currently it is - # not possible to add metadata here) + # ToDo: Create the command attribute of name `heater_on` (currently it is + # not possible to add metadata here). cmd = DeviceCommand(name=..., type=...) - # ToDo: create the device configuration and send it to the server name it + # ToDo: Create the device configuration and send it to the server. heater = Device(...) @@ -176,7 +174,7 @@ iotac.post_device(device=heater) - # ToDo: Check the entity that corresponds to your device + # ToDo: Check the entity that corresponds to your device. heater_entity = cbc.get_entity(entity_id=heater.entity_name, entity_type=heater.entity_type) print(f"Your device entity before running the simulation: \n " @@ -192,27 +190,27 @@ mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) # ToDo: Implement a callback function that gets triggered when the - # command is sent to the device. The incoming command schould update the - # heater attribute of the simulation model + # command is sent to the device. The incoming command should update the + # heater attribute of the simulation model. def on_command(client, obj, msg): """ Callback for incoming commands """ - # Decode the message payload using the libraries builtin encoders + # decode the message payload using the libraries builtin encoders apikey, device_id, payload = \ client.get_encoder(PayloadProtocol.IOTA_JSON).decode_message( msg=msg) # map the command value to the simulation sim_model.heater_on = payload[cmd.name] - # ToDo: acknowledge the command. Here command are usually single + # ToDo: Acknowledge the command. In this case commands are usually single # messages. The first key is equal to the commands name. client.publish(device_id=device_id, command_name=..., payload=...) # ToDo: Add the command callback to your MQTTClient. This will get - # triggered for the specified device_id + # triggered for the specified device_id. mqttc.add_command_callback(device_id=..., callback=...) @@ -239,13 +237,13 @@ def on_command(client, obj, msg): }, "throttling": 0 } - # Generate Subscription object for validation and post it + # generate Subscription object for validation and post it subscription = Subscription(**subscription) subscription_id = cbc.post_subscription(subscription=subscription) # ToDo: You need to implement a controller that controls the # heater state with respect to the zone temperature. This will be - # implemented with asynchronous communication using MQTT-Subscriptions + # implemented with asynchronous communication using MQTT-Subscriptions. def on_measurement(client, obj, msg): """ Callback for measurement notifications @@ -253,12 +251,9 @@ def on_measurement(client, obj, msg): message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] - # ToDo: retrieve the value of temperature attribute + # ToDo: Retrieve the value of temperature attribute. temperature = ... - # ToDo: device if you want update your command - # Note that this could also be substitute by a conditional - # subscription update = True if temperature <= 19: state = 1 @@ -266,7 +261,8 @@ def on_measurement(client, obj, msg): state = 0 else: update = False - # ToDo: send the command to the heater entity + + # ToDo: Send the command to the heater entity. if update: command = NamedCommand(name=cmd.name, value=state) cbc.post_command(...) @@ -293,9 +289,9 @@ def on_measurement(client, obj, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # Create a loop that publishes every second a message to the broker - # that holds the simulation time "sim_time" and the corresponding - # temperature "temperature" the loop should. You may use the `object_id` + # ToDo: Create a loop that publishes a message every 100 milliseconds + # to the broker that holds the simulation time "sim_time" and the + # corresponding temperature "temperature". You may use the `object_id` # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, sim_model.t_end + int(COM_STEP), @@ -314,13 +310,13 @@ def on_measurement(client, obj, msg): mqttc.publish(device_id=heater.device_id, payload={"sim_time": sim_model.t_sim}) - time.sleep(0.5) + time.sleep(0.1) # 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(0.5) + # wait for 0.1 second before publishing the next values + time.sleep(0.1) - # Get corresponding entities and write values to history + # 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 @@ -330,7 +326,7 @@ def on_measurement(client, obj, msg): {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) - # Get ZoneTemperatureSensor and write values to history + # get zone temperature sensor 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 @@ -339,7 +335,7 @@ def on_measurement(client, obj, msg): {"sim_time": zone_temperature_sensor_entity.sim_time.value, "temperature": zone_temperature_sensor_entity.temperature.value}) - # Get ZoneTemperatureSensor and write values to history + # get zone temperature sensor and write values to history heater_entity = cbc.get_entity( entity_id=heater.entity_name, entity_type=heater.entity_type) @@ -357,27 +353,30 @@ def on_measurement(client, obj, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["sim_time"] for item in history_weather_station] + t_simulation = [item["sim_time"]/3600 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.title.set_text("Weather Station") + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["sim_time"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax2.set_xlabel('time in s') + ax.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in h') ax2.set_ylabel('zone temperature in °C') plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["sim_time"] for item in history_heater] + t_simulation = [item["sim_time"]/3600 for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) - ax3.set_xlabel('time in s') + ax.title.set_text("Heater") + ax3.set_xlabel('time in h') ax3.set_ylabel('on/off') plt.show() diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index 79a4f797..16def606 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -3,7 +3,7 @@ # Create a virtual IoT device that simulates a heater for your # thermal zone. The heater can be turned on and off via a simple hysteresis -# controller. The devices from e4_iot_thermal_zone_sensors.py will loaded +# controller. The devices from e4_iot_thermal_zone_sensors.py will be loaded # from the stored *.json-files. # The input sections are marked with 'ToDo' @@ -13,18 +13,18 @@ # 2. Retrieve the service group and device configurations of already existing # devices from the IoT-Agent # 3. Create a third device configuration for a heater holding a command -# for turning it `on` and `off`device and post it to the server +# for turning it `on` and `off` and post it to the server # 4. Create an MQTT client using the filip.client.mqtt package and register # your service group and your devices # 4. Define a callback function that will be executed when the client -# receives a command. Decode the message and set the update the state in +# receives a command. Decode the message and set the update state in # simulation model. Afterwards, acknowledge the command using the api of the # IoTAMQTTClient. # 5. Add the callback for your heater device to the IoTAMQTTClient # 6. Create an MQTT subscription for asynchronous communication that # gets triggered when the temperature attribute changes. # 7. Write a second callback that represents your controller. It should get -# triggered when the MQTTClient receive a notification message due to your +# triggered when the MQTTClient receives a notification message due to your # subscription. Add the callback to your MQTTClient using the original # paho-api (`message_callback_add`) # 8. Run the simulation and plot @@ -58,48 +58,48 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL_EXPOSED = "mqtt://localhost:1883" -# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883 +# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883. MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" MQTT_PW = "" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# Path to json-files to store entity data for follow up exercises +# path to json-files to store entity data for follow up exercises WRITE_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_groups.json") WRITE_DEVICES_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_devices.json") WRITE_SUBSCRIPTIONS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_solution_groups.json") READ_DEVICES_FILEPATH = \ Path("../e4_iot_thermal_zone_sensors_solution_devices.json") -# Opening the files +# opening the files with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, open(READ_DEVICES_FILEPATH, 'r') as devices_file): json_groups = json.load(groups_file) @@ -112,7 +112,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds +COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -135,7 +135,7 @@ history_zone_temperature_sensor = [] history_heater = [] - # Create clients and restore devices and groups from file + # create clients and also restore devices and groups from file groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) devices = TypeAdapter(List[Device]).validate_python(json_devices) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) @@ -143,26 +143,26 @@ iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # ToDo: Get the device configurations from the server + # ToDo: 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") - # ToDo: Get the service group configurations from the server + # ToDo: Get the service group configurations from the server. group = iotac.get_group(resource="/iot/json", apikey=APIKEY) - # ToDo: Create and additional device holding a command attribute and - # post it to the IoT-Agent. It should be mapped to the `type` heater - # create the sim_time attribute and add during device creation + # ToDo: Create an additional device holding a command attribute and + # post it to the IoT-Agent. It should be mapped to the `type` heater. + # create the sim_time attribute and add it during device creation t_sim = DeviceAttribute(name='sim_time', object_id='t_sim', type="Number") - # ToDo: create the command attribute of name `heater_on` (currently it is - # not possible to add metadata here) + # ToDo: Create the command attribute of name `heater_on` (currently it is + # not possible to add metadata here). cmd = DeviceCommand(name="heater_on", type=DataType.BOOLEAN) - # ToDo: create the device configuration and send it to the server + # ToDo: Create the device configuration and send it to the server. heater = Device(device_id="device:003", entity_name="urn:ngsi-ld:Heater:001", entity_type="Heater", @@ -174,7 +174,7 @@ iotac.post_device(device=heater) - # ToDo: Check the entity that corresponds to your device + # ToDo: Check the entity that corresponds to your device. heater_entity = cbc.get_entity(entity_id=heater.entity_name, entity_type=heater.entity_type) print(f"Your device entity before running the simulation: \n " @@ -190,27 +190,27 @@ mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW) # ToDo: Implement a callback function that gets triggered when the - # command is sent to the device. The incoming command schould update the - # heater attribute of the simulation model + # command is sent to the device. The incoming command should update the + # heater attribute of the simulation model. def on_command(client, obj, msg): """ Callback for incoming commands """ - # Decode the message payload using the libraries builtin encoders + # decode the message payload using the libraries builtin encoders apikey, device_id, payload = \ client.get_encoder(PayloadProtocol.IOTA_JSON).decode_message( msg=msg) # map the command value to the simulation sim_model.heater_on = payload[cmd.name] - # ToDo: acknowledge the command. Here command are usually single + # ToDo: Acknowledge the command. In this case commands are usually single # messages. The first key is equal to the commands name. client.publish(device_id=device_id, command_name=next(iter(payload)), payload=payload) # ToDo: Add the command callback to your MQTTClient. This will get - # triggered for the specified device_id + # triggered for the specified device_id. mqttc.add_command_callback(device_id=heater.device_id, callback=on_command) @@ -237,13 +237,13 @@ def on_command(client, obj, msg): }, "throttling": 0 } - # Generate Subscription object for validation and post it + # generate Subscription object for validation and post it subscription = Subscription(**subscription) subscription_id = cbc.post_subscription(subscription=subscription) # ToDo: You need to implement a controller that controls the # heater state with respect to the zone temperature. This will be - # implemented with asynchronous communication using MQTT-Subscriptions + # implemented with asynchronous communication using MQTT-Subscriptions. def on_measurement(client, obj, msg): """ Callback for measurement notifications @@ -251,12 +251,9 @@ def on_measurement(client, obj, msg): message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] - # ToDo: retrieve the value of temperature attribute + # ToDo: Retrieve the value of temperature attribute. temperature = updated_zone_temperature_sensor.temperature.value - # ToDo: device if you want update your command - # Note that this could also be substitute by a conditional - # subscription update = True if temperature <= 19: state = 1 @@ -264,7 +261,8 @@ def on_measurement(client, obj, msg): state = 0 else: update = False - # ToDo: send the command to the heater entity + + # ToDo: Send the command to the heater entity. if update: command = NamedCommand(name=cmd.name, value=state) cbc.post_command(entity_id=heater.entity_name, @@ -291,9 +289,9 @@ def on_measurement(client, obj, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # Create a loop that publishes every second a message to the broker - # that holds the simulation time "sim_time" and the corresponding - # temperature "temperature" the loop should. You may use the `object_id` + # ToDo: Create a loop that publishes a message every 100 milliseconds + # to the broker that holds the simulation time "sim_time" and the + # corresponding temperature "temperature". You may use the `object_id` # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, sim_model.t_end + int(COM_STEP), @@ -312,13 +310,13 @@ def on_measurement(client, obj, msg): mqttc.publish(device_id=heater.device_id, payload={"sim_time": sim_model.t_sim}) - time.sleep(0.5) + time.sleep(0.1) # 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(0.5) + # wait for 0.1 second before publishing the next values + time.sleep(0.1) - # Get corresponding entities and write values to history + # 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 @@ -328,7 +326,7 @@ def on_measurement(client, obj, msg): {"sim_time": weather_station_entity.sim_time.value, "temperature": weather_station_entity.temperature.value}) - # Get ZoneTemperatureSensor and write values to history + # get zone temperature sensor 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 @@ -337,7 +335,7 @@ def on_measurement(client, obj, msg): {"sim_time": zone_temperature_sensor_entity.sim_time.value, "temperature": zone_temperature_sensor_entity.temperature.value}) - # Get ZoneTemperatureSensor and write values to history + # get zone temperature sensor and write values to history heater_entity = cbc.get_entity( entity_id=heater.entity_name, entity_type=heater.entity_type) @@ -355,27 +353,30 @@ def on_measurement(client, obj, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["sim_time"] for item in history_weather_station] + t_simulation = [item["sim_time"]/3600 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) - ax.set_xlabel('time in s') + ax.title.set_text("Weather Station") + ax.set_xlabel('time in h') ax.set_ylabel('ambient temperature in °C') plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["sim_time"] for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax2.set_xlabel('time in s') + ax.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in h') ax2.set_ylabel('zone temperature in °C') plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["sim_time"] for item in history_heater] + t_simulation = [item["sim_time"]/3600 for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) - ax3.set_xlabel('time in s') + ax.title.set_text("Heater") + ax3.set_xlabel('time in h') ax3.set_ylabel('on/off') plt.show() From 8416c707e72aab18b54cdc072bfa315a1ad69695 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 10 Apr 2024 12:06:05 +0200 Subject: [PATCH 18/30] chore: format e6_timeseries_data exercise and solution --- .../e6_timeseries_data/e6_timeseries_data.py | 149 ++++++++++-------- .../e6_timeseries_data_solution.py | 111 +++++++------ 2 files changed, 133 insertions(+), 127 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 499bfa15..9ae5c509 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -7,13 +7,14 @@ # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create a quantumleap client that create subscription that get triggered +# 2. Create a quantumleap client that creates subscription that gets triggered # by the updates on your context entities # 3. Run the simulation # 4. Retrieve the data via QuantumLeap and visualize it """ # ## Import packages +import json from pathlib import Path import time from typing import List @@ -21,9 +22,8 @@ from uuid import uuid4 import pandas as pd import paho.mqtt.client as mqtt -from pydantic import parse_file_as +from pydantic import TypeAdapter import matplotlib.pyplot as plt - # import from filip from filip.clients.ngsi_v2 import \ ContextBrokerClient, \ @@ -45,38 +45,38 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. QL_URL = "http://localhost:8668" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL_EXPOSED = "mqtt://localhost:1883" -# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://mosquitto:1883. MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" -MQTT_PW = "" +MQTT_PW = "" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_groups.json") READ_DEVICES_FILEPATH = \ @@ -84,6 +84,14 @@ READ_SUBSCRIPTIONS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") +# opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file, + open(READ_SUBSCRIPTIONS_FILEPATH, 'r') as subscriptions_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) + json_subscriptions = json.load(subscriptions_file) + # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature TEMPERATURE_MIN = -5 # minimal ambient temperature @@ -91,7 +99,7 @@ T_SIM_START = 0 # simulation start time in seconds T_SIM_END = 24 * 60 * 60 # simulation end time in seconds -COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds +COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds # ## Main script if __name__ == '__main__': @@ -110,10 +118,10 @@ temp_min=TEMPERATURE_MIN, temp_start=TEMPERATURE_ZONE_START) - # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) - sub = parse_file_as(List[Subscription], READ_SUBSCRIPTIONS_FILEPATH)[0] + # create clients and restore devices and groups from file + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) + sub = TypeAdapter(List[Subscription]).validate_python(json_subscriptions)[0] sub.notification.mqtt.topic = TOPIC_CONTROLLER sub.notification.mqtt.user = MQTT_USER sub.notification.mqtt.passwd = MQTT_PW @@ -123,24 +131,24 @@ iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # Get the group and device configurations from the server + # get the group and device configurations from the server group = iotac.get_group(resource="/iot/json", apikey=APIKEY) 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") # create a MQTTv5 client with paho-mqtt and the known groups and - # devices. + # devices. mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5, devices=[weather_station, zone_temperature_sensor, heater], service_groups=[group]) - # ToDo: set user data if required + # ToDo: 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 schould update the - # heater attribute of the simulation model + # command is sent to the device. The incoming command should update the + # heater attribute of the simulation model def on_command(client, obj, msg): """ Callback for incoming commands @@ -152,33 +160,30 @@ def on_command(client, obj, msg): sim_model.heater_on = payload[heater.commands[0].name] - # acknowledge the command. Here command are usually single - # messages. The first key is equal to the commands name. + # Acknowledge the command. Here commands are usually single + # messages. The first key is equal to the commands name. 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 + # triggered for the specified device_id. mqttc.add_command_callback(device_id=heater.device_id, callback=on_command) # You need to implement a controller that controls the - # heater state with respect to the zone temperature. This will be - # implemented with asynchronous communication using MQTT-Subscriptions + # heater state with respect to the zone temperature. This will be + # implemented with asynchronous communication using MQTT-Subscriptions def on_measurement(client, obj, msg): """ Callback for measurement notifications """ - message = Message.parse_raw(msg.payload) + message = Message.model_validate_json(msg.payload) updated_zone_temperature_sensor = message.data[0] # retrieve the value of temperature attribute temperature = updated_zone_temperature_sensor.temperature.value - # device if you want update your command - # Note that this could also be substitute by a conditional - # subscription update = True if temperature <= 19: state = 1 @@ -196,10 +201,10 @@ def on_measurement(client, obj, msg): mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement) - # ToDo: create a quantumleap client + # ToDo: Create a quantumleap client. qlc = QuantumLeapClient(...) - # ToDO: create a http subscriptions that get triggered by updates of your + # ToDo: Create 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, @@ -221,6 +226,7 @@ def on_measurement(client, obj, msg): bind_port=0, clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY, properties=None) + # subscribe to topics # subscribe to all incoming command topics for the registered devices mqttc.subscribe() @@ -229,32 +235,32 @@ def on_measurement(client, obj, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # Create a loop that publishes every second 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. + # Create a loop that publishes a message every 0.3 seconds to the broker + # that holds the simulation time "sim_time" and the corresponding + # temperature "temperature". You may use the `object_id` + # or the attribute name as key in your payload. 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}) + "sim_time": 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}) + "sim_time": sim_model.t_sim}) - # publish the 'simtime' for the heater device + # publish the 'sim_time' for the heater device mqttc.publish(device_id=heater.device_id, - payload={"simtime": sim_model.t_sim}) + payload={"sim_time": sim_model.t_sim}) - time.sleep(1) + time.sleep(0.3) # 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(1) + # wait for 0.3 seconds before publishing the next values + time.sleep(0.3) # close the mqtt listening thread mqttc.loop_stop() @@ -265,8 +271,8 @@ def on_measurement(client, obj, msg): time.sleep(10) # ToDo: Retrieve the historic data from QuantumLeap, convert them to a - # pandas dataframe and plot them - # Retrieve the data for the weather station + # pandas dataframe and plot them. + # retrieve the data for the weather station history_weather_station = qlc.get_entity_by_id( entity_id=weather_station.entity_name, entity_type=weather_station.entity_type, @@ -279,38 +285,40 @@ def on_measurement(client, obj, msg): # drop unnecessary index levels history_weather_station = history_weather_station.droplevel( level=("entityId", "entityType"), axis=1) - history_weather_station['simtime'] = pd.to_numeric( - history_weather_station['simtime'], downcast="float") + history_weather_station['sim_time'] = pd.to_numeric( + history_weather_station['sim_time'], downcast="float") history_weather_station['temperature'] = pd.to_numeric( history_weather_station['temperature'], downcast="float") - # ToDo: plot the results + # ToDo: Plot the results. fig, ax = plt.subplots() - ax.plot(history_weather_station['simtime'], + ax.plot(history_weather_station['sim_time']/60, history_weather_station['temperature']) - ax.set_xlabel('time in s') + ax.title.set_text("Weather Station") + ax.set_xlabel('time in min') ax.set_ylabel('ambient temperature in °C') plt.show() - # ToDo: Retrieve the data for the zone temperature + # ToDo: Retrieve the data for the zone temperature. history_zone_temperature_sensor = ... - # ToDo: convert to pandas dataframe and print it + # ToDo: Convert to pandas dataframe and print it. history_zone_temperature_sensor = ... - # ToDo: drop unnecessary index levels + # ToDo: Drop unnecessary index levels. history_zone_temperature_sensor = ... - history_zone_temperature_sensor['simtime'] = pd.to_numeric( - history_zone_temperature_sensor['simtime'], downcast="float") + history_zone_temperature_sensor['sim_time'] = pd.to_numeric( + history_zone_temperature_sensor['sim_time'], downcast="float") history_zone_temperature_sensor['temperature'] = pd.to_numeric( history_zone_temperature_sensor['temperature'], downcast="float") - # plot the results + # ToDo: Plot the results. fig2, ax2 = plt.subplots() - ax2.plot(history_zone_temperature_sensor['simtime'], + ax2.plot(history_zone_temperature_sensor['sim_time']/60, history_zone_temperature_sensor['temperature']) - ax2.set_xlabel('time in s') + ax2.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in min') ax2.set_ylabel('zone temperature in °C') plt.show() - # ToDo: Retrieve the data for the heater + # ToDo: Retrieve the data for the heater. history_heater = ... # convert to pandas dataframe and print it @@ -318,18 +326,19 @@ def on_measurement(client, obj, msg): history_heater = history_heater.replace(' ', 0) print(history_heater) - # drop unnecessary index levels + # ToDo: Drop unnecessary index levels. history_heater = history_heater.droplevel( level=("entityId", "entityType"), axis=1) - history_heater['simtime'] = pd.to_numeric( - history_heater['simtime'], downcast="float") + history_heater['sim_time'] = pd.to_numeric( + history_heater['sim_time'], downcast="float") history_heater['heater_on_info'] = pd.to_numeric( history_heater['heater_on_info'], downcast="float") - # plot the results + # ToDo: Plot the results. fig3, ax3 = plt.subplots() - ax3.plot(history_heater['simtime'], + ax3.plot(history_heater['sim_time']/60, history_heater['heater_on_info']) - ax3.set_xlabel('time in s') + ax3.title.set_text("Heater") + ax3.set_xlabel('time in min') ax3.set_ylabel('set point') plt.show() diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index 04ed5a31..f20a72b0 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -7,7 +7,7 @@ # #### Steps to complete: # 1. Set up the missing parameters in the parameter section -# 2. Create a quantumleap client that create subscription that get triggered +# 2. Create a quantumleap client that creates subscription that gets triggered # by the updates on your context entities # 3. Run the simulation # 4. Retrieve the data via QuantumLeap and visualize it @@ -45,39 +45,38 @@ from tutorials.ngsi_v2.simulation_model import SimulationModel # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. QL_URL = "http://localhost:8668" -# ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://test.mosquitto.org:1883. MQTT_BROKER_URL_EXPOSED = "mqtt://localhost:1883" -# ToDo: Enter your mqtt broker url, e.g mqtt://mosquitto:1883 +# ToDo: Enter your mqtt broker url, e.g. mqtt://mosquitto:1883. MQTT_BROKER_URL_INTERNAL = "mqtt://mosquitto:1883" -# ToDo: If required enter your username and password +# ToDo: If required, enter your username and password. MQTT_USER = "" MQTT_PW = "" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' - UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_groups.json") READ_DEVICES_FILEPATH = \ @@ -85,7 +84,7 @@ READ_SUBSCRIPTIONS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") -# Opening the files +# opening the files with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, open(READ_DEVICES_FILEPATH, 'r') as devices_file, open(READ_SUBSCRIPTIONS_FILEPATH, 'r') as subscriptions_file): @@ -93,7 +92,6 @@ json_devices = json.load(devices_file) json_subscriptions = json.load(subscriptions_file) - # set parameters for the temperature simulation TEMPERATURE_MAX = 10 # maximal ambient temperature TEMPERATURE_MIN = -5 # minimal ambient temperature @@ -120,11 +118,10 @@ temp_min=TEMPERATURE_MIN, temp_start=TEMPERATURE_ZONE_START) - # Create clients and restore devices and groups from file + # create clients and restore devices and groups from file groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) devices = TypeAdapter(List[Device]).validate_python(json_devices) sub = TypeAdapter(List[Subscription]).validate_python(json_subscriptions)[0] - # sub = parse_file_as(List[Subscription], READ_SUBSCRIPTIONS_FILEPATH)[0] sub.notification.mqtt.topic = TOPIC_CONTROLLER sub.notification.mqtt.user = MQTT_USER sub.notification.mqtt.passwd = MQTT_PW @@ -134,25 +131,24 @@ iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # Get the group and device configurations from the server + # get the group and device configurations from the server group = iotac.get_group(resource="/iot/json", apikey=APIKEY) 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") # create a MQTTv5 client with paho-mqtt and the known groups and - # devices. + # devices. mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5, devices=[weather_station, zone_temperature_sensor, heater], service_groups=[group]) - # ToDo: set user data if required + # ToDo: 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 schould update the - # heater attribute of the simulation model - + # command is sent to the device. The incoming command should update the + # heater attribute of the simulation model def on_command(client, obj, msg): """ Callback for incoming commands @@ -164,20 +160,20 @@ def on_command(client, obj, msg): sim_model.heater_on = payload[heater.commands[0].name] - # acknowledge the command. Here command are usually single - # messages. The first key is equal to the commands name. + # Acknowledge the command. Here commands are usually single + # messages. The first key is equal to the commands name. 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 + # triggered for the specified device_id. mqttc.add_command_callback(device_id=heater.device_id, callback=on_command) # You need to implement a controller that controls the - # heater state with respect to the zone temperature. This will be - # implemented with asynchronous communication using MQTT-Subscriptions + # heater state with respect to the zone temperature. This will be + # implemented with asynchronous communication using MQTT-Subscriptions def on_measurement(client, obj, msg): """ Callback for measurement notifications @@ -188,9 +184,6 @@ def on_measurement(client, obj, msg): # retrieve the value of temperature attribute temperature = updated_zone_temperature_sensor.temperature.value - # device if you want update your command - # Note that this could also be substitute by a conditional - # subscription update = True if temperature <= 19: state = 1 @@ -208,10 +201,10 @@ def on_measurement(client, obj, msg): mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement) - # ToDo: create a quantumleap client + # 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 + # ToDo: Create 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, @@ -241,6 +234,7 @@ def on_measurement(client, obj, msg): bind_port=0, clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY, properties=None) + # subscribe to topics # subscribe to all incoming command topics for the registered devices mqttc.subscribe() @@ -249,10 +243,10 @@ def on_measurement(client, obj, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # Create a loop that publishes every second a message to the broker - # that holds the simulation time "sim_time" and the corresponding - # temperature "temperature" the loop should. You may use the `object_id` - # or the attribute name as key in your payload. + # Create a loop that publishes a message every 0.3 seconds to the broker + # that holds the simulation time "sim_time" and the corresponding + # temperature "temperature". You may use the `object_id` + # or the attribute name as key in your payload. for t_sim in range(sim_model.t_start, sim_model.t_end + int(COM_STEP), int(COM_STEP)): @@ -270,11 +264,11 @@ def on_measurement(client, obj, msg): mqttc.publish(device_id=heater.device_id, payload={"sim_time": sim_model.t_sim}) - time.sleep(1) + time.sleep(0.3) # 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(1) + # wait for 0.3 seconds before publishing the next values + time.sleep(0.3) # close the mqtt listening thread mqttc.loop_stop() @@ -285,8 +279,8 @@ def on_measurement(client, obj, msg): time.sleep(10) # ToDo: Retrieve the historic data from QuantumLeap, convert them to a - # pandas dataframe and plot them - # Retrieve the data for the weather station + # pandas dataframe and plot them. + # retrieve the data for the weather station history_weather_station = qlc.get_entity_by_id( entity_id=weather_station.entity_name, entity_type=weather_station.entity_type, @@ -303,41 +297,43 @@ def on_measurement(client, obj, msg): history_weather_station['sim_time'], downcast="float") history_weather_station['temperature'] = pd.to_numeric( history_weather_station['temperature'], downcast="float") - # ToDo: plot the results + # ToDo: Plot the results. fig, ax = plt.subplots() - ax.plot(history_weather_station['sim_time'], + ax.plot(history_weather_station['sim_time']/60, history_weather_station['temperature']) - ax.set_xlabel('time in s') + ax.title.set_text("Weather Station") + ax.set_xlabel('time in min') ax.set_ylabel('ambient temperature in °C') plt.show() - # ToDo: Retrieve the data for the zone temperature + # ToDo: Retrieve the data for the zone temperature. history_zone_temperature_sensor = qlc.get_entity_by_id( entity_id=zone_temperature_sensor.entity_name, entity_type=zone_temperature_sensor.entity_type, last_n=10000 ) - # ToDo: convert to pandas dataframe and print it + # ToDo: Convert to pandas dataframe and print it. history_zone_temperature_sensor = \ history_zone_temperature_sensor.to_pandas() print(history_zone_temperature_sensor) - # ToDo: drop unnecessary index levels + # ToDo: Drop unnecessary index levels. history_zone_temperature_sensor = history_zone_temperature_sensor.droplevel( level=("entityId", "entityType"), axis=1) history_zone_temperature_sensor['sim_time'] = pd.to_numeric( history_zone_temperature_sensor['sim_time'], downcast="float") history_zone_temperature_sensor['temperature'] = pd.to_numeric( history_zone_temperature_sensor['temperature'], downcast="float") - # ToDo: plot the results + # ToDo: Plot the results. fig2, ax2 = plt.subplots() - ax2.plot(history_zone_temperature_sensor['sim_time'], + ax2.plot(history_zone_temperature_sensor['sim_time']/60, history_zone_temperature_sensor['temperature']) - ax2.set_xlabel('time in s') + ax2.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in min') ax2.set_ylabel('zone temperature in °C') plt.show() - # ToDo: Retrieve the data for the heater + # ToDo: Retrieve the data for the heater. history_heater = qlc.get_entity_by_id( entity_id=heater.entity_name, entity_type=heater.entity_type, @@ -349,18 +345,19 @@ def on_measurement(client, obj, msg): history_heater = history_heater.replace(' ', 0) print(history_heater) - # ToDo: drop unnecessary index levels + # ToDo: Drop unnecessary index levels. history_heater = history_heater.droplevel( level=("entityId", "entityType"), axis=1) history_heater['sim_time'] = pd.to_numeric( history_heater['sim_time'], downcast="float") history_heater['heater_on_info'] = pd.to_numeric( history_heater['heater_on_info'], downcast="float") - # ToDo: plot the results + # ToDo: Plot the results. fig3, ax3 = plt.subplots() - ax3.plot(history_heater['sim_time'], + ax3.plot(history_heater['sim_time']/60, history_heater['heater_on_info']) - ax3.set_xlabel('time in s') + ax3.title.set_text("Heater") + ax3.set_xlabel('time in min') ax3.set_ylabel('set point') plt.show() From 17901f6aadc2e677fbfcfea96348f00513ff8b63 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 10 Apr 2024 12:08:28 +0200 Subject: [PATCH 19/30] chore: format e7_semantic_iot exercise and solution --- .../e7_semantic_iot/e7_semantic_iot.py | 116 ++++++++++-------- .../e7_semantic_iot_solutions.py | 70 +++++------ 2 files changed, 97 insertions(+), 89 deletions(-) diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py index c61f4993..a3685cdc 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py @@ -2,7 +2,7 @@ # # Exercise 7: Semantic IoT Systems # # We now want to add a semantic meaning to our measurements. Therefore we -# semantically connect to the context entities that we created in +# semantically connect the context entities that we created in # `e3_context_entities.py` # The input sections are marked with 'ToDo' @@ -14,14 +14,15 @@ # 3. Add relationships that connect the thermal zone temperature sensor and # the heater to the building, and vice versa # 4. Retrieve all entities and print them -# 5. Congratulations you are now ready to build your own semantic systems for +# 5. Congratulations! You are now ready to build your own semantic systems. For # advanced semantic functions check on our semantics examples """ # ## Import packages +import json from pathlib import Path from typing import List -from pydantic import parse_file_as +from pydantic import TypeAdapter # import from filip from filip.clients.ngsi_v2 import \ @@ -42,32 +43,40 @@ clear_iot_agent # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path # ToDo: Change the name of your service-path to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this is very important in order to avoid user # collisions. You will use this service path through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_groups.json") + Path("../e5_iot_thermal_zone_control_solution_groups.json") READ_DEVICES_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_devices.json") + Path("../e5_iot_thermal_zone_control_solution_devices.json") READ_ENTITIES_FILEPATH = \ - Path("../e3_context_entities_entities.json") + Path("../e3_context_entities_solution_entities.json") + +# opening the files +with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, + open(READ_DEVICES_FILEPATH, 'r') as devices_file, + open(READ_ENTITIES_FILEPATH, 'r') as entities_file): + json_groups = json.load(groups_file) + json_devices = json.load(devices_file) + json_entities = json.load(entities_file) # ## Main script if __name__ == '__main__': @@ -78,10 +87,10 @@ clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) clear_context_broker(url=CB_URL, fiware_header=fiware_header) - # Create clients and restore devices and groups from file - groups = parse_file_as(List[ServiceGroup], READ_GROUPS_FILEPATH) - devices = parse_file_as(List[Device], READ_DEVICES_FILEPATH) - entities = parse_file_as(List[ContextEntity], READ_ENTITIES_FILEPATH) + # create clients and restore devices and groups from file + groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) + devices = TypeAdapter(List[Device]).validate_python(json_devices) + entities = TypeAdapter(List[ContextEntity]).validate_python(json_entities) cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header) for entity in entities: cbc.post_entity(entity=entity) @@ -90,15 +99,15 @@ iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # ToDo: Retrieve all iot resources from the IoT-Agent - # Get the group and device configurations from the server + # ToDo: Retrieve all iot resources from the IoT-Agent. + # get the group and device configurations from the server group = iotac.get_group(resource="/iot/json", apikey=APIKEY) 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") # ToDo: Get context entities from the Context Broker - # (exclude the IoT device ones) + # (exclude the IoT device ones). building = cbc.get_entity(entity_id="urn:ngsi-ld:building:001", entity_type="Building") thermal_zone = cbc.get_entity(entity_id="ThermalZone:001", @@ -107,7 +116,7 @@ # ToDo: Semantically connect the weather station and the building. By # adding a `hasWeatherStation` attribute of type `Relationship`. For the # connection from the weather station to the building add a static - # attribute to the weather station + # attribute to the weather station. # create the context attribute for the building and add it to the # building entity @@ -131,23 +140,22 @@ # zone by adding a `hasTemperatureSensor` attribute of type # `Relationship` to the thermal zone entity. # For the connection from the sensor to the zone add a static - # attribute to the temperature sensor device + # attribute to the temperature sensor device. - # ToDo: create the context attribute for the thermal zone and add it to the - # thermal zone entity - has_sensor = ... + # ToDo: Create a context attribute for the thermal zone and add it to the + # thermal zone entity. + has_sensor = ... thermal_zone.add_attributes(...) - # ToDo: create a static attribute that connects the zone temperature zone to - # the thermal zone + # ToDo: Create a static attribute that connects the zone temperature zone to + # the thermal zone. cbc.update_entity(entity=thermal_zone) ref_thermal_zone = StaticDeviceAttribute(...) - zone_temperature_sensor.add_attribute(ref_thermal_zone) iotac.update_device(device=zone_temperature_sensor) @@ -155,18 +163,18 @@ # zone by adding a `hasTemperatureSensor` attribute of type # `Relationship` to the thermal zone entity. # For the connection from the sensor to the zone add a static - # attribute to the temperature sensor device + # attribute to the temperature sensor device. - # ToDo: create the context attribute for the thermal zone and add it to the - # thermal zone entity + # ToDo: Create a context attribute for the thermal zone and add it to the + # thermal zone entity. has_heater = ... thermal_zone.add_attributes(...) - # ToDo: create a static attribute that connects the zone temperature zone to - # the thermal zone + # ToDo: Create a static attribute that connects the zone temperature zone to + # the thermal zone. cbc.update_entity(entity=thermal_zone) ref_thermal_zone = ... @@ -175,27 +183,27 @@ heater.add_attribute(ref_thermal_zone) iotac.update_device(device=heater) - # ToDo: Add unit metadata to the temperature and simtime attributes of - # all devices. Here we use unitcode information. If you can not find - # your unit code you can use our unit models for help + # ToDo: Add unit metadata to the temperature and sim_time attributes of + # all devices. Here we use unit code information. If you can not find + # your unit code, you can use our unit models for help. # get code from Unit model for seconds code = Unit(name="second [unit of time]").code - # add metadata to simtime attribute of the all devices - metadata_simtime = NamedMetadata(name="unitCode", - type="Text", - value=code) - attr_simtime = weather_station.get_attribute( - attribute_name="simtime" + # add metadata to sim_time attribute of the all devices + metadata_sim_time = NamedMetadata(name="unitCode", + type="Text", + value=code) + attr_sim_time = weather_station.get_attribute( + attribute_name="sim_time" ) - attr_simtime.metadata = metadata_simtime - weather_station.update_attribute(attribute=attr_simtime) - zone_temperature_sensor.update_attribute(attribute=attr_simtime) - heater.update_attribute(attribute=attr_simtime) + attr_sim_time.metadata = metadata_sim_time + weather_station.update_attribute(attribute=attr_sim_time) + zone_temperature_sensor.update_attribute(attribute=attr_sim_time) + heater.update_attribute(attribute=attr_sim_time) - # ToDo: get code from Unit model for degree celsius + # ToDo: Get code from Unit model for degree celsius. code = Unit(name="degree Celsius").code - # ToDo: add metadata to temperature attribute of the weather - # station and the zone temperature sensor + # ToDo: Add metadata to temperature attribute of the weather + # station and the zone temperature sensor. metadata_t_amb = NamedMetadata(...) attr_t_amb = weather_station.get_attribute( attribute_name="temperature" @@ -209,8 +217,8 @@ attr_t_zone.metadata = ... zone_temperature_sensor.update_attribute(...) - # currently adding metadata via updating does not work perfectly. - # Therefore, we do a delete and update + # Currently adding metadata via updating does not work perfectly, + # therefore, we delete and update them. iotac.delete_device(device_id=weather_station.device_id) iotac.post_device(device=weather_station) iotac.delete_device(device_id=zone_temperature_sensor.device_id) @@ -218,10 +226,10 @@ iotac.delete_device(device_id=heater.device_id) iotac.post_device(device=heater) - # ToDo: Retrieve all ContextEntites and print them + # ToDo: Retrieve all Context Entities and print them. entities = ... for entity in entities: - print(entity.json(indent=2)) + print(entity.model_dump_json(indent=2)) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) clear_context_broker(url=CB_URL, fiware_header=fiware_header) diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py index 1469c594..4f232d30 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot_solutions.py @@ -2,7 +2,7 @@ # # Exercise 7: Semantic IoT Systems # # We now want to add a semantic meaning to our measurements. Therefore we -# semantically connect to the context entities that we created in +# semantically connect the context entities that we created in # `e3_context_entities.py` # The input sections are marked with 'ToDo' @@ -14,7 +14,7 @@ # 3. Add relationships that connect the thermal zone temperature sensor and # the heater to the building, and vice versa # 4. Retrieve all entities and print them -# 5. Congratulations you are now ready to build your own semantic systems for +# 5. Congratulations! You are now ready to build your own semantic systems. For # advanced semantic functions check on our semantics examples """ @@ -43,26 +43,26 @@ clear_iot_agent # ## Parameters -# ToDo: Enter your context broker host and port, e.g http://localhost:1026 +# ToDo: Enter your context broker host and port, e.g. http://localhost:1026. CB_URL = "http://localhost:1026" -# ToDo: Enter your IoT-Agent host and port, e.g http://localhost:4041 +# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041. IOTA_URL = "http://localhost:4041" # ToDo: Change the name of your service to something unique. If you run -# on a shared instance this very important in order to avoid user +# on a shared instance this very is important in order to avoid user # collisions. You will use this service through the whole tutorial. -# If you forget to change it an error will be raised! +# If you forget to change it, an error will be raised! # FIWARE-Service SERVICE = 'filip_tutorial' -# FIWARE-Servicepath +# FIWARE-Service path SERVICE_PATH = '/' -# ToDo: Change the APIKEY to something unique. This represent the "token" -# for IoT devices to connect (send/receive data ) with the platform. In the +# ToDo: Change the APIKEY to something unique. This represents the "token" +# for IoT devices to connect (send/receive data) with the platform. In the # context of MQTT, APIKEY is linked with the topic used for communication. APIKEY = 'your_apikey' -# Path to read json-files from previous exercises +# path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_groups.json") READ_DEVICES_FILEPATH = \ @@ -70,7 +70,7 @@ READ_ENTITIES_FILEPATH = \ Path("../e3_context_entities_solution_entities.json") -# Opening the files +# opening the files with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, open(READ_DEVICES_FILEPATH, 'r') as devices_file, open(READ_ENTITIES_FILEPATH, 'r') as entities_file): @@ -87,7 +87,7 @@ clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) clear_context_broker(url=CB_URL, fiware_header=fiware_header) - # Create clients and restore devices and groups from file + # create clients and restore devices and groups from file groups = TypeAdapter(List[ServiceGroup]).validate_python(json_groups) devices = TypeAdapter(List[Device]).validate_python(json_devices) entities = TypeAdapter(List[ContextEntity]).validate_python(json_entities) @@ -99,15 +99,15 @@ iotac.post_groups(service_groups=groups) iotac.post_devices(devices=devices) - # ToDo: Retrieve all iot resources from the IoT-Agent - # Get the group and device configurations from the server + # ToDo: Retrieve all iot resources from the IoT-Agent. + # get the group and device configurations from the server group = iotac.get_group(resource="/iot/json", apikey=APIKEY) 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") # ToDo: Get context entities from the Context Broker - # (exclude the IoT device ones) + # (exclude the IoT device ones). building = cbc.get_entity(entity_id="urn:ngsi-ld:building:001", entity_type="Building") thermal_zone = cbc.get_entity(entity_id="ThermalZone:001", @@ -140,18 +140,18 @@ # zone by adding a `hasTemperatureSensor` attribute of type # `Relationship` to the thermal zone entity. # For the connection from the sensor to the zone add a static - # attribute to the temperature sensor device + # attribute to the temperature sensor device. - # ToDo: create the context attribute for the thermal zone and add it to the - # thermal zone entity + # ToDo: Create a context attribute for the thermal zone and add it to the + # thermal zone entity. has_sensor = NamedContextAttribute( name="hasTemperatureSensor", type="Relationship", value=zone_temperature_sensor.entity_name) thermal_zone.add_attributes(attrs=[has_sensor]) - # ToDo: create a static attribute that connects the zone temperature zone to - # the thermal zone + # ToDo: Create a static attribute that connects the zone temperature zone to + # the thermal zone. cbc.update_entity(entity=thermal_zone) ref_thermal_zone = StaticDeviceAttribute(name="refThermalZone", @@ -164,18 +164,18 @@ # zone by adding a `hasTemperatureSensor` attribute of type # `Relationship` to the thermal zone entity. # For the connection from the sensor to the zone add a static - # attribute to the temperature sensor device + # attribute to the temperature sensor device. - # ToDo: create the context attribute for the thermal zone and add it to the - # thermal zone entity + # ToDo: Create a context attribute for the thermal zone and add it to the + # thermal zone entity. has_heater = NamedContextAttribute( name="hasHeater", type="Relationship", value=heater.entity_name) thermal_zone.add_attributes(attrs=[has_heater]) - # ToDo: create a static attribute that connects the zone temperature zone to - # the thermal zone + # ToDo: Create a static attribute that connects the zone temperature zone to + # the thermal zone. cbc.update_entity(entity=thermal_zone) ref_thermal_zone = StaticDeviceAttribute(name="refThermalZone", @@ -185,14 +185,14 @@ iotac.update_device(device=heater) # ToDo: Add unit metadata to the temperature and sim_time attributes of - # all devices. Here we use unitcode information. If you can not find - # your unit code you can use our unit models for help + # all devices. Here we use unit code information. If you can not find + # your unit code, you can use our unit models for help. # get code from Unit model for seconds code = Unit(name="second [unit of time]").code # add metadata to sim_time attribute of the all devices metadata_sim_time = NamedMetadata(name="unitCode", - type="Text", - value=code) + type="Text", + value=code) attr_sim_time = weather_station.get_attribute( attribute_name="sim_time" ) @@ -201,10 +201,10 @@ zone_temperature_sensor.update_attribute(attribute=attr_sim_time) heater.update_attribute(attribute=attr_sim_time) - # ToDo: get code from Unit model for degree celsius + # ToDo: Get code from Unit model for degree celsius. code = Unit(name="degree Celsius").code - # ToDo: add metadata to temperature attribute of the weather - # station and the zone temperature sensor + # ToDo: Add metadata to temperature attribute of the weather + # station and the zone temperature sensor. metadata_t_amb = NamedMetadata(name="unitCode", type="Text", value=code) @@ -222,8 +222,8 @@ attr_t_zone.metadata = metadata_t_zone zone_temperature_sensor.update_attribute(attribute=attr_t_zone) - # currently adding metadata via updating does not work perfectly. - # Therefore, we do a delete and update + # Currently adding metadata via updating does not work perfectly, + # therefore, we delete and update them. iotac.delete_device(device_id=weather_station.device_id) iotac.post_device(device=weather_station) iotac.delete_device(device_id=zone_temperature_sensor.device_id) @@ -231,7 +231,7 @@ iotac.delete_device(device_id=heater.device_id) iotac.post_device(device=heater) - # ToDo: Retrieve all ContextEntites and print them + # ToDo: Retrieve all Context Entities and print them. entities = cbc.get_entity_list() for entity in entities: print(entity.model_dump_json(indent=2)) From 5fe94683b1bd6e74b20979e3b3d887679010f46a Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 10 Apr 2024 12:30:38 +0200 Subject: [PATCH 20/30] format: fix typo in exercise 1 --- .../e1_virtual_weatherstation/e1_virtual_weatherstation.py | 2 +- .../e1_virtual_weatherstation_solution.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py index a7797f15..3d4afef1 100644 --- a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py +++ b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation.py @@ -104,7 +104,7 @@ def on_message(client, userdata, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every 20 milliseconds a message to the broker + # ToDo: Create a loop that publishes every 0.2 seconds a message to the broker # that holds the simulation time "t_sim" and the corresponding temperature # "t_amb". for t_sim in range(sim_model.t_start, diff --git a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py index e3043da4..6ae704cd 100644 --- a/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py +++ b/tutorials/ngsi_v2/e1_virtual_weatherstation/e1_virtual_weatherstation_solution.py @@ -103,7 +103,7 @@ def on_message(client, userdata, msg): # create a non-blocking thread for mqtt communication mqttc.loop_start() - # ToDo: Create a loop that publishes every 20 milliseconds a message to the broker + # ToDo: Create a loop that publishes every 0.2 seconds a message to the broker # that holds the simulation time "t_sim" and the corresponding temperature # "t_amb". for t_sim in range(sim_model.t_start, From 0a610a553e972e1c7b1331d71e0c4f1f845bafca Mon Sep 17 00:00:00 2001 From: Trgovac Date: Wed, 10 Apr 2024 12:36:31 +0200 Subject: [PATCH 21/30] fix: fix plotting in exercise 5 --- .../e5_iot_thermal_zone_control.py | 16 ++++++++-------- .../e5_iot_thermal_zone_control_solution.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py index bd6fcfc7..fa20dfb5 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py @@ -353,30 +353,30 @@ def on_measurement(client, obj, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_weather_station] + t_simulation = [item["sim_time"]/60 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) ax.title.set_text("Weather Station") - ax.set_xlabel('time in h') + ax.set_xlabel('time in min') ax.set_ylabel('ambient temperature in °C') plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/60 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax.title.set_text("Zone Temperature Sensor") - ax2.set_xlabel('time in h') + ax2.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in min') ax2.set_ylabel('zone temperature in °C') plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_heater] + t_simulation = [item["sim_time"]/60 for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) - ax.title.set_text("Heater") - ax3.set_xlabel('time in h') + ax3.title.set_text("Heater") + ax3.set_xlabel('time in min') ax3.set_ylabel('on/off') plt.show() diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index 16def606..6e76482d 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -353,30 +353,30 @@ def on_measurement(client, obj, msg): # plot results fig, ax = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_weather_station] + t_simulation = [item["sim_time"]/60 for item in history_weather_station] temperature = [item["temperature"] for item in history_weather_station] ax.plot(t_simulation, temperature) ax.title.set_text("Weather Station") - ax.set_xlabel('time in h') + ax.set_xlabel('time in min') ax.set_ylabel('ambient temperature in °C') plt.show() fig2, ax2 = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] + t_simulation = [item["sim_time"]/60 for item in history_zone_temperature_sensor] temperature = [item["temperature"] for item in history_zone_temperature_sensor] ax2.plot(t_simulation, temperature) - ax.title.set_text("Zone Temperature Sensor") - ax2.set_xlabel('time in h') + ax2.title.set_text("Zone Temperature Sensor") + ax2.set_xlabel('time in min') ax2.set_ylabel('zone temperature in °C') plt.show() fig3, ax3 = plt.subplots() - t_simulation = [item["sim_time"]/3600 for item in history_heater] + t_simulation = [item["sim_time"]/60 for item in history_heater] on_off = [item["on_off"] for item in history_heater] ax3.plot(t_simulation, on_off) - ax.title.set_text("Heater") - ax3.set_xlabel('time in h') + ax3.title.set_text("Heater") + ax3.set_xlabel('time in min') ax3.set_ylabel('on/off') plt.show() From e029962ac7f80dad2f2318c5e805e4a4ed37b289 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Thu, 11 Apr 2024 12:26:42 +0200 Subject: [PATCH 22/30] fix: syntax error --- tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 9ae5c509..48eef3a3 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -85,9 +85,9 @@ Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") # opening the files -with (open(READ_GROUPS_FILEPATH, 'r') as groups_file, - open(READ_DEVICES_FILEPATH, 'r') as devices_file, - open(READ_SUBSCRIPTIONS_FILEPATH, 'r') as subscriptions_file): +with open(READ_GROUPS_FILEPATH, 'r') as groups_file, \ + open(READ_DEVICES_FILEPATH, 'r') as devices_file, \ + open(READ_SUBSCRIPTIONS_FILEPATH, 'r') as subscriptions_file: json_groups = json.load(groups_file) json_devices = json.load(devices_file) json_subscriptions = json.load(subscriptions_file) From cf1659c8d0a3207b50003a816f6946b1075c8a11 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 12 Apr 2024 12:59:53 +0200 Subject: [PATCH 23/30] chore: replace the deprecated qlc post_subscription with the one in cbc --- .../e6_timeseries_data/e6_timeseries_data.py | 45 ++++++++++---- .../e6_timeseries_data_solution.py | 58 ++++++++++++------- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 48eef3a3..9caa568d 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -32,7 +32,8 @@ from filip.clients.mqtt import IoTAMQTTClient from filip.models.base import FiwareHeader from filip.models.ngsi_v2.context import NamedCommand -from filip.models.ngsi_v2.subscriptions import Subscription, Message +from filip.models.ngsi_v2.subscriptions import Subscription, Message, Subject, \ + Notification from filip.models.ngsi_v2.iot import \ Device, \ PayloadProtocol, \ @@ -201,21 +202,42 @@ def on_measurement(client, obj, msg): mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement) - # ToDo: Create a quantumleap client. - qlc = QuantumLeapClient(...) # ToDo: Create 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) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': weather_station.entity_name, + 'type': weather_station.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) - qlc.post_subscription(...) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': zone_temperature_sensor.entity_name, + 'type': zone_temperature_sensor.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) - qlc.post_subscription(...) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': heater.entity_name, + 'type': heater.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) # connect to the mqtt broker and subscribe to your topic mqtt_url = urlparse(MQTT_BROKER_URL_EXPOSED) @@ -270,6 +292,9 @@ def on_measurement(client, obj, msg): # wait until all data is available time.sleep(10) + # ToDo: Create a quantumleap client. + qlc = QuantumLeapClient(...) + # ToDo: Retrieve the historic data from QuantumLeap, convert them to a # pandas dataframe and plot them. # retrieve the data for the weather station diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index f20a72b0..36af4bc5 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -32,7 +32,8 @@ from filip.clients.mqtt import IoTAMQTTClient from filip.models.base import FiwareHeader from filip.models.ngsi_v2.context import NamedCommand -from filip.models.ngsi_v2.subscriptions import Subscription, Message +from filip.models.ngsi_v2.subscriptions import Subscription, Message, Notification, \ + Subject from filip.models.ngsi_v2.iot import \ Device, \ PayloadProtocol, \ @@ -201,29 +202,41 @@ def on_measurement(client, obj, msg): 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 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) - - 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) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': weather_station.entity_name, + 'type': weather_station.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': zone_temperature_sensor.entity_name, + 'type': zone_temperature_sensor.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': heater.entity_name, + 'type': heater.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling = 0) + ) # connect to the mqtt broker and subscribe to your topic mqtt_url = urlparse(MQTT_BROKER_URL_EXPOSED) @@ -278,6 +291,9 @@ def on_measurement(client, obj, msg): # wait until all data is available time.sleep(10) + # ToDo: Create a quantumleap client. + qlc = QuantumLeapClient(url=QL_URL, fiware_header=fiware_header) + # ToDo: Retrieve the historic data from QuantumLeap, convert them to a # pandas dataframe and plot them. # retrieve the data for the weather station From 3bd8e2f2baba37e4907124d7f9e96251e3408ad9 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Tue, 16 Apr 2024 16:14:16 +0200 Subject: [PATCH 24/30] fix: fix opening files for Date: Tue, 16 Apr 2024 16:41:28 +0200 Subject: [PATCH 25/30] chore: make created files disjunct between exercises and solutions --- .../ngsi_v2/e3_context_entities/e3_context_entities.py | 7 +++++-- .../e3_context_entities/e3_context_entities_solution.py | 5 ++++- tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py | 6 +++--- tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py | 6 +++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py index 7f5558ef..509f9316 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities.py @@ -57,8 +57,11 @@ SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, -# e.g. ../e3_my_entities.json -WRITE_ENTITIES_FILEPATH = Path("") +# e.g. ../e3_my_entities.json. Files that are used in exercises and files +# that are used in solutions are different from each other so be careful +# when working with them. You can of course change the paths as you wish, +# but it is recommended to use the already given path names. +WRITE_ENTITIES_FILEPATH = Path("../e3_context_entities.json") # ## Main script if __name__ == '__main__': diff --git a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py index 90411102..69cb7d1f 100644 --- a/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py +++ b/tutorials/ngsi_v2/e3_context_entities/e3_context_entities_solution.py @@ -57,7 +57,10 @@ SERVICE_PATH = '/' # ToDo: Path to json-files to store entity data for follow up exercises, -# e.g. ../e3_my_entities.json +# e.g. ../e3_my_entities.json. Files that are used in exercises and files +# that are used in solutions are different from each other so be careful +# when working with them. You can of course change the paths as you wish, +# but it is recommended to use the already given path names. WRITE_ENTITIES_FILEPATH = Path("../e3_context_entities_solution_entities.json") # ## Main script diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 9caa568d..97989aa2 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -79,11 +79,11 @@ # path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_solution_groups.json") + Path("../e5_iot_thermal_zone_control_groups.json") READ_DEVICES_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_solution_devices.json") + Path("../e5_iot_thermal_zone_control_devices.json") READ_SUBSCRIPTIONS_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_solution_subscriptions.json") + Path("../e5_iot_thermal_zone_control_subscriptions.json") # opening the files with open(READ_GROUPS_FILEPATH, 'r') as groups_file, \ diff --git a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py index a1704591..5c536a1f 100644 --- a/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py +++ b/tutorials/ngsi_v2/e7_semantic_iot/e7_semantic_iot.py @@ -64,11 +64,11 @@ # path to read json-files from previous exercises READ_GROUPS_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_solution_groups.json") + Path("../e5_iot_thermal_zone_control_groups.json") READ_DEVICES_FILEPATH = \ - Path("../e5_iot_thermal_zone_control_solution_devices.json") + Path("../e5_iot_thermal_zone_control_devices.json") READ_ENTITIES_FILEPATH = \ - Path("../e3_context_entities_solution_entities.json") + Path("../e3_context_entities.json") # opening the files with open(READ_GROUPS_FILEPATH, 'r') as groups_file, \ From 01b6a906cc31b9373962a2284a3b74db96f374fb Mon Sep 17 00:00:00 2001 From: Trgovac Date: Tue, 16 Apr 2024 16:46:34 +0200 Subject: [PATCH 26/30] chore: fix small typos and pep-8 errors in exercise 5 and 6 --- .../e5_iot_thermal_zone_control.py | 2 +- .../e5_iot_thermal_zone_control_solution.py | 2 +- .../ngsi_v2/e6_timeseries_data/e6_timeseries_data.py | 8 ++++---- .../e6_timeseries_data/e6_timeseries_data_solution.py | 7 ++++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py index a632c37b..ff250032 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control.py @@ -86,7 +86,7 @@ UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# path to json-files to store entity data for follow up exercises +# path to json-files to store entity data for follow-up exercises WRITE_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_groups.json") WRITE_DEVICES_FILEPATH = \ diff --git a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py index ebd976a6..d05bccdf 100644 --- a/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py +++ b/tutorials/ngsi_v2/e5_iot_thermal_zone_control/e5_iot_thermal_zone_control_solution.py @@ -86,7 +86,7 @@ UNIQUE_ID = str(uuid4()) TOPIC_CONTROLLER = f"fiware_workshop/{UNIQUE_ID}/controller" print(TOPIC_CONTROLLER) -# path to json-files to store entity data for follow up exercises +# path to json-files to store entity data for follow-up exercises WRITE_GROUPS_FILEPATH = \ Path("../e5_iot_thermal_zone_control_solution_groups.json") WRITE_DEVICES_FILEPATH = \ diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 97989aa2..a7ba977c 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -150,6 +150,7 @@ # Implement a callback function that gets triggered when the # command is sent to the device. The incoming command should update the # heater attribute of the simulation model + def on_command(client, obj, msg): """ Callback for incoming commands @@ -202,7 +203,6 @@ def on_measurement(client, obj, msg): mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement) - # ToDo: Create http subscriptions that get triggered by updates of your # device attributes. Note that you can also post the same subscription # by the context broker. @@ -214,7 +214,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) cbc.post_subscription(subscription=Subscription( @@ -225,7 +225,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) cbc.post_subscription(subscription=Subscription( @@ -236,7 +236,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) # connect to the mqtt broker and subscribe to your topic diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index 0499ba53..3c25d94f 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -150,6 +150,7 @@ # Implement a callback function that gets triggered when the # command is sent to the device. The incoming command should update the # heater attribute of the simulation model + def on_command(client, obj, msg): """ Callback for incoming commands @@ -213,7 +214,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) cbc.post_subscription(subscription=Subscription( @@ -224,7 +225,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) cbc.post_subscription(subscription=Subscription( @@ -235,7 +236,7 @@ def on_measurement(client, obj, msg): notification=Notification(**{ 'http': {'url': 'http://quantumleap:8668/v2/notify'} }), - throttling = 0) + throttling=0) ) # connect to the mqtt broker and subscribe to your topic From bc26f9e28a76fff394b8a4a4a195d509f863cac4 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 30 Apr 2024 11:24:28 +0200 Subject: [PATCH 27/30] fix: change the order of the clearing functions --- .../e4_iot_thermal_zone_sensors.py | 2 +- .../e4_iot_thermal_zone_sensors_solution.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py index e4029d3b..a1f80c7a 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors.py @@ -83,8 +83,8 @@ fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH) # clear the state of your service and scope - clear_context_broker(url=CB_URL, fiware_header=fiware_header) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) + clear_context_broker(url=CB_URL, fiware_header=fiware_header) # instantiate simulation model sim_model = SimulationModel(t_start=T_SIM_START, diff --git a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py index aadd9743..74e7de09 100644 --- a/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py +++ b/tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py @@ -83,8 +83,8 @@ fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH) # clear the state of your service and scope - clear_context_broker(url=CB_URL, fiware_header=fiware_header) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) + clear_context_broker(url=CB_URL, fiware_header=fiware_header) # instantiate simulation model sim_model = SimulationModel(t_start=T_SIM_START, @@ -282,5 +282,5 @@ groups = [item.model_dump() for item in iotac.get_group_list()] json.dump(groups, f, ensure_ascii=False, indent=2) - clear_context_broker(url=CB_URL, fiware_header=fiware_header) clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) + clear_context_broker(url=CB_URL, fiware_header=fiware_header) From 8317608ae87a50ef52351f84af53bfc37d5d0e44 Mon Sep 17 00:00:00 2001 From: Trgovac Date: Tue, 30 Apr 2024 12:43:36 +0200 Subject: [PATCH 28/30] fix: revert changes to exercise 6 --- .../e6_timeseries_data/e6_timeseries_data.py | 47 +++++++++++---- .../e6_timeseries_data_solution.py | 59 ++++++++++++------- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index 85eac620..e6ef506f 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -32,7 +32,8 @@ from filip.clients.mqtt import IoTAMQTTClient from filip.models.base import FiwareHeader from filip.models.ngsi_v2.context import NamedCommand -from filip.models.ngsi_v2.subscriptions import Subscription, Message +from filip.models.ngsi_v2.subscriptions import Subscription, Message, Subject, \ + Notification from filip.models.ngsi_v2.iot import \ Device, \ PayloadProtocol, \ @@ -202,21 +203,42 @@ def on_measurement(client, obj, msg): mqttc.message_callback_add(sub=TOPIC_CONTROLLER, callback=on_measurement) - # ToDo: Create a quantumleap client. - qlc = QuantumLeapClient(...) - # ToDo: Create http subscriptions that get triggered by updates of your # device attributes. Note that you can only post the subscription # to 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(...) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': weather_station.entity_name, + 'type': weather_station.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': zone_temperature_sensor.entity_name, + 'type': zone_temperature_sensor.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) - qlc.post_subscription(...) + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': heater.entity_name, + 'type': heater.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) # connect to the mqtt broker and subscribe to your topic mqtt_url = urlparse(MQTT_BROKER_URL_EXPOSED) @@ -271,6 +293,9 @@ def on_measurement(client, obj, msg): # wait until all data is available time.sleep(10) + # Todo: Create a quantumleap client. + qlc = QuantumLeapClient(...) + # ToDo: Retrieve the historic data from QuantumLeap, convert them to a # pandas dataframe and plot them. # retrieve the data for the weather station diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py index 0a307b24..b1978407 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data_solution.py @@ -32,7 +32,8 @@ from filip.clients.mqtt import IoTAMQTTClient from filip.models.base import FiwareHeader from filip.models.ngsi_v2.context import NamedCommand -from filip.models.ngsi_v2.subscriptions import Subscription, Message +from filip.models.ngsi_v2.subscriptions import Subscription, Message, Notification, \ + Subject from filip.models.ngsi_v2.iot import \ Device, \ PayloadProtocol, \ @@ -202,29 +203,42 @@ def on_measurement(client, obj, msg): 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 http subscriptions that get triggered by updates of your # device attributes. Note that you can only post the subscription # to 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) - - 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) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': weather_station.entity_name, + 'type': weather_station.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': zone_temperature_sensor.entity_name, + 'type': zone_temperature_sensor.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) + + cbc.post_subscription(subscription=Subscription( + subject=Subject(**{ + 'entities': [{'id': heater.entity_name, + 'type': heater.entity_type}] + }), + notification=Notification(**{ + 'http': {'url': 'http://quantumleap:8668/v2/notify'} + }), + throttling=0) + ) # connect to the mqtt broker and subscribe to your topic mqtt_url = urlparse(MQTT_BROKER_URL_EXPOSED) @@ -279,6 +293,9 @@ def on_measurement(client, obj, msg): # wait until all data is available time.sleep(10) + # ToDo: Create a quantumleap client. + qlc = QuantumLeapClient(url=QL_URL, fiware_header=fiware_header) + # ToDo: Retrieve the historic data from QuantumLeap, convert them to a # pandas dataframe and plot them. # retrieve the data for the weather station From 8a9a7470a6ffc50c7067d7fa720cb84def29aa16 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 30 Apr 2024 14:25:55 +0200 Subject: [PATCH 29/30] chore: remove some examples in e6 --- .../e6_timeseries_data/e6_timeseries_data.py | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py index e6ef506f..3e7e9174 100644 --- a/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py +++ b/tutorials/ngsi_v2/e6_timeseries_data/e6_timeseries_data.py @@ -206,7 +206,7 @@ def on_measurement(client, obj, msg): # ToDo: Create http subscriptions that get triggered by updates of your # device attributes. Note that you can only post the subscription # to the context broker. - + # Subscription for weather station cbc.post_subscription(subscription=Subscription( subject=Subject(**{ 'entities': [{'id': weather_station.entity_name, @@ -218,26 +218,16 @@ def on_measurement(client, obj, msg): throttling=0) ) + # Subscription for zone temperature sensor cbc.post_subscription(subscription=Subscription( - subject=Subject(**{ - 'entities': [{'id': zone_temperature_sensor.entity_name, - 'type': zone_temperature_sensor.entity_type}] - }), - notification=Notification(**{ - 'http': {'url': 'http://quantumleap:8668/v2/notify'} - }), - throttling=0) + ... + ) ) + # Subscription for heater cbc.post_subscription(subscription=Subscription( - subject=Subject(**{ - 'entities': [{'id': heater.entity_name, - 'type': heater.entity_type}] - }), - notification=Notification(**{ - 'http': {'url': 'http://quantumleap:8668/v2/notify'} - }), - throttling=0) + ... + ) ) # connect to the mqtt broker and subscribe to your topic From 80eaa748fe97d1896063d14763fd0dca31b59cf3 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Tue, 30 Apr 2024 14:29:59 +0200 Subject: [PATCH 30/30] docs: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d75c62c8..8e3e7236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - add: tutorials for multi-entity ([#260](https://github.com/RWTH-EBC/FiLiP/pull/260)) - add: add ``update_entity_relationships`` to allow relationship update ([#271](https://github.com/RWTH-EBC/FiLiP/pull/271)) - add: flag to determine the deletion of registration when clearing the CB ([#267](https://github.com/RWTH-EBC/FiLiP/pull/267)) +- fix: rework tutorials for pydantic v2 ([#259](https://github.com/RWTH-EBC/FiLiP/pull/259)) ### v0.4.1 - fix: Session added as optional parameter to enable tls communication with clients ([#249](https://github.com/RWTH-EBC/FiLiP/pull/249))