Coverage for tutorials/ngsi_v2/e4_iot_thermal_zone_sensors/e4_iot_thermal_zone_sensors_solution.py: 0%
28 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-05 11:07 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-05 11:07 +0000
1"""
2# # Exercise 4: Virtual Thermal Zone
4# Create two virtual IoT devices. One of them represents the temperature
5# sensor for the air temperature of a thermal zone, whereas the second
6# represents a virtual weather station. Both devices publish their values to
7# the platform via MQTT. Use the simulation model of
8# e1_virtual_weatherstation.py
9#
10# The input sections are marked with 'ToDo'
11#
12# #### Steps to complete:
13# 1. Set up the missing parameters in the parameter section
14# 2. Create a service group and two corresponding devices
15# 3. Provision the service group and the devices
16# 4. Create an MQTT client using the filip.client.mqtt package and register
17# your service group and your devices
18# 5. Check if the IoT-Agent correctly creates the corresponding entities
19# 5. Create a function that publishes the simulated temperature via MQTT,
20# retrieves the entity data after each message and writes the values to a
21# history
22# 6. Run the simulation and plot the results
23"""
25# ## Import packages
26import json
27from pathlib import Path
28import time
29from urllib.parse import urlparse
30import matplotlib.pyplot as plt
31import paho.mqtt.client as mqtt
33# import from filip
34from filip.clients.ngsi_v2 import ContextBrokerClient, IoTAClient
35from filip.clients.mqtt import IoTAMQTTClient
36from filip.models.base import FiwareHeader
37from filip.models.ngsi_v2.iot import Device, DeviceAttribute, ServiceGroup
38from filip.utils.cleanup import clear_context_broker, clear_iot_agent
40# import simulation model
41from tutorials.ngsi_v2.simulation_model import SimulationModel
43# ## Parameters
44# ToDo: Enter your context broker host and port, e.g. http://localhost:1026.
45CB_URL = "http://localhost:1026"
46# ToDo: Enter your IoT-Agent host and port, e.g. http://localhost:4041.
47IOTA_URL = "http://localhost:4041"
48# ToDo: Enter your mqtt broker url, e.g. mqtt://test.mosquitto.org:1883.
49MQTT_BROKER_URL = "mqtt://localhost:1883"
50# ToDo: If required, enter your username and password.
51MQTT_USER = ""
52MQTT_PW = ""
54# ToDo: Change the name of your service to something unique. If you run
55# on a shared instance this is very important in order to avoid user
56# collisions. You will use this service through the whole tutorial.
57# If you forget to change it, an error will be raised!
58# FIWARE-Service
59SERVICE = "filip_tutorial"
60# FIWARE-Service path
61SERVICE_PATH = "/"
63# ToDo: Change the APIKEY to something unique. This represents the "token"
64# for IoT devices to connect (send/receive data) with the platform. In the
65# context of MQTT, APIKEY is linked with the topic used for communication.
66APIKEY = "your_apikey"
68# path to json-files to device configuration data for follow-up exercises
69WRITE_GROUPS_FILEPATH = Path("../e4_iot_thermal_zone_sensors_solution_groups.json")
70WRITE_DEVICES_FILEPATH = Path("../e4_iot_thermal_zone_sensors_solution_devices.json")
72# set parameters for the temperature simulation
73TEMPERATURE_MAX = 10 # maximal ambient temperature
74TEMPERATURE_MIN = -5 # minimal ambient temperature
75TEMPERATURE_ZONE_START = 20 # start value of the zone temperature
77T_SIM_START = 0 # simulation start time in seconds
78T_SIM_END = 24 * 60 * 60 # simulation end time in seconds
79COM_STEP = 60 * 60 * 0.25 # 15 min communication step in seconds
81# ## Main script
82if __name__ == "__main__":
83 # create a fiware header object
84 fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH)
85 # clear the state of your service and scope
86 clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header)
87 clear_context_broker(url=CB_URL, fiware_header=fiware_header)
89 # instantiate simulation model
90 sim_model = SimulationModel(
91 t_start=T_SIM_START,
92 t_end=T_SIM_END,
93 temp_max=TEMPERATURE_MAX,
94 temp_min=TEMPERATURE_MIN,
95 temp_start=TEMPERATURE_ZONE_START,
96 )
98 # define lists to store historical data
99 history_weather_station = []
100 history_zone_temperature_sensor = []
102 # create a service group with your api key
103 service_group = ServiceGroup(apikey=APIKEY, resource="/iot/json")
105 # ToDo: Create two IoTA-MQTT devices for the weather station and the zone
106 # temperature sensor. Also add the simulation time as `active attribute`
107 # to each device!
108 # create the weather station device
109 # create the `sim_time` attribute and add it to the weather station's attributes
110 t_sim = DeviceAttribute(name="sim_time", object_id="t_sim", type="Number")
112 weather_station = Device(
113 device_id="device:001",
114 entity_name="urn:ngsi-ld:WeatherStation:001",
115 entity_type="WeatherStation",
116 protocol="IoTA-JSON",
117 transport="MQTT",
118 apikey=APIKEY,
119 attributes=[t_sim],
120 commands=[],
121 )
123 # create a temperature attribute and add it via the api of the
124 # `device`-model. Use the `t_amb` as `object_id`. `object_id` specifies
125 # what key will be used in the MQTT Message payload
126 t_amb = DeviceAttribute(name="temperature", object_id="t_amb", type="Number")
128 weather_station.add_attribute(t_amb)
130 # ToDo: Create the zone temperature device and add the `t_sim` attribute upon
131 # creation.
132 zone_temperature_sensor = Device(
133 device_id="device:002",
134 entity_name="urn:ngsi-ld:TemperatureSensor:001",
135 entity_type="TemperatureSensor",
136 protocol="IoTA-JSON",
137 transport="MQTT",
138 apikey=APIKEY,
139 attributes=[t_sim],
140 commands=[],
141 )
143 # ToDo: Create the temperature attribute. Use the `t_zone` as `object_id`.
144 # `object_id` specifies what key will be used in the MQTT Message payload.
145 t_zone = DeviceAttribute(name="temperature", object_id="t_zone", type="Number")
147 zone_temperature_sensor.add_attribute(t_zone)
149 # ToDo: Create an IoTAClient.
150 iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header)
151 # ToDo: Provision service group and add it to your IoTAMQTTClient.
152 iotac.post_group(service_group=service_group, update=True)
153 # ToDo: Provision the devices at the IoTA-Agent.
154 # provision the weather station device
155 iotac.post_device(device=weather_station, update=True)
156 # ToDo: Provision the zone temperature device.
157 iotac.post_device(device=zone_temperature_sensor, update=True)
159 # ToDo: Create a context broker client.
160 # ToDo: Check in the context broker whether the entities corresponding to your
161 # devices were correctly created.
162 cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header)
163 # get weather station entity
164 print(
165 f"Weather station:\n{cbc.get_entity(weather_station.entity_name).model_dump_json(indent=2)}"
166 )
167 # ToDo: Get zone temperature sensor entity.
168 print(
169 f"Zone temperature sensor:\n{cbc.get_entity(zone_temperature_sensor.entity_name).model_dump_json(indent=2)}"
170 )
172 # ToDo: Create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient.
173 mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5)
174 # set user data if required
175 mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW)
176 # ToDo: Register the service group with your MQTT-Client.
177 mqttc.add_service_group(service_group=service_group)
178 # ToDo: Register devices with your MQTT-Client.
179 # register the weather station
180 mqttc.add_device(weather_station)
181 # ToDo: Register the zone temperature sensor.
182 mqttc.add_device(zone_temperature_sensor)
184 # The IoTAMQTTClient automatically creates outgoing topics from the
185 # device configuration during runtime. Hence, we need to construct them
186 # manually in order to subscribe to them. This is usually not required as
187 # only the platform should listen to the incoming traffic.
188 # If you want to listen subscribe to the following topics:
189 # "/json/<APIKEY>/<weather_station.device_id>/attrs"
190 # "/json/<APIKEY>/<zone_temperature_sensor.device_id>/attrs"
192 # ToDO: Connect to the MQTT broker and subscribe to your topic.
193 mqtt_url = urlparse(MQTT_BROKER_URL)
194 mqttc.connect(
195 host=mqtt_url.hostname,
196 port=mqtt_url.port,
197 keepalive=60,
198 bind_address="",
199 bind_port=0,
200 clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY,
201 properties=None,
202 )
203 # subscribe to topics
204 # subscribe to all incoming command topics for the registered devices
205 mqttc.subscribe()
206 # create a non-blocking thread for mqtt communication
207 mqttc.loop_start()
209 # ToDo: Create a loop that publishes a message every 100 milliseconds
210 # to the broker that holds the simulation time `sim_time` and the
211 # corresponding temperature `temperature`. You may use the `object_id`
212 # or the attribute name as a key in your payload.
213 for t_sim in range(
214 sim_model.t_start, sim_model.t_end + int(COM_STEP), int(COM_STEP)
215 ):
216 # publish the simulated ambient temperature
217 mqttc.publish(
218 device_id=weather_station.device_id,
219 payload={"temperature": sim_model.t_amb, "sim_time": sim_model.t_sim},
220 )
222 # ToDo: Publish the simulated zone temperature.
223 mqttc.publish(
224 device_id=zone_temperature_sensor.device_id,
225 payload={"temperature": sim_model.t_zone, "sim_time": sim_model.t_sim},
226 )
228 # simulation step for the next loop
229 sim_model.do_step(int(t_sim + COM_STEP))
230 # wait for one second before publishing the next values
231 time.sleep(0.1)
233 # get corresponding entities and store the data
234 weather_station_entity = cbc.get_entity(
235 entity_id=weather_station.entity_name,
236 entity_type=weather_station.entity_type,
237 )
238 # append the data to the local history
239 history_weather_station.append(
240 {
241 "sim_time": weather_station_entity.sim_time.value,
242 "temperature": weather_station_entity.temperature.value,
243 }
244 )
246 # ToDo: Get zone temperature sensor and store the data.
247 zone_temperature_sensor_entity = cbc.get_entity(
248 entity_id=zone_temperature_sensor.entity_name,
249 entity_type=zone_temperature_sensor.entity_type,
250 )
251 history_zone_temperature_sensor.append(
252 {
253 "sim_time": zone_temperature_sensor_entity.sim_time.value,
254 "temperature": zone_temperature_sensor_entity.temperature.value,
255 }
256 )
258 # close the mqtt listening thread
259 mqttc.loop_stop()
260 # disconnect the mqtt device
261 mqttc.disconnect()
263 # plot the results
264 fig, ax = plt.subplots()
265 t_simulation = [item["sim_time"] / 3600 for item in history_weather_station]
266 temperature = [item["temperature"] for item in history_weather_station]
267 ax.plot(t_simulation, temperature)
268 ax.title.set_text("Weather Station")
269 ax.set_xlabel("time in h")
270 ax.set_ylabel("ambient temperature in °C")
272 fig2, ax2 = plt.subplots()
273 t_simulation = [item["sim_time"] / 3600 for item in history_zone_temperature_sensor]
274 temperature = [item["temperature"] for item in history_zone_temperature_sensor]
275 ax2.plot(t_simulation, temperature)
276 ax2.title.set_text("Zone Temperature Sensor")
277 ax2.set_xlabel("time in h")
278 ax2.set_ylabel("zone temperature in °C")
280 plt.show()
282 # write devices and groups to file and clear server state
283 assert (
284 WRITE_DEVICES_FILEPATH.suffix == ".json"
285 ), f"Wrong file extension! {WRITE_DEVICES_FILEPATH.suffix}"
286 WRITE_DEVICES_FILEPATH.touch(exist_ok=True)
287 with WRITE_DEVICES_FILEPATH.open("w", encoding="utf-8") as f:
288 devices = [item.model_dump() for item in iotac.get_device_list()]
289 json.dump(devices, f, ensure_ascii=False, indent=2)
291 assert (
292 WRITE_GROUPS_FILEPATH.suffix == ".json"
293 ), f"Wrong file extension! {WRITE_GROUPS_FILEPATH.suffix}"
294 WRITE_GROUPS_FILEPATH.touch(exist_ok=True)
295 with WRITE_GROUPS_FILEPATH.open("w", encoding="utf-8") as f:
296 groups = [item.model_dump() for item in iotac.get_group_list()]
297 json.dump(groups, f, ensure_ascii=False, indent=2)
299 clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header)
300 clear_context_broker(url=CB_URL, fiware_header=fiware_header)