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

1""" 

2# # Exercise 4: Virtual Thermal Zone 

3 

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""" 

24 

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 

32 

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 

39 

40# import simulation model 

41from tutorials.ngsi_v2.simulation_model import SimulationModel 

42 

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 = "" 

53 

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 = "/" 

62 

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" 

67 

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") 

71 

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 

76 

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 

80 

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) 

88 

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 ) 

97 

98 # define lists to store historical data 

99 history_weather_station = [] 

100 history_zone_temperature_sensor = [] 

101 

102 # create a service group with your api key 

103 service_group = ServiceGroup(apikey=APIKEY, resource="/iot/json") 

104 

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") 

111 

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 ) 

122 

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") 

127 

128 weather_station.add_attribute(t_amb) 

129 

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 ) 

142 

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") 

146 

147 zone_temperature_sensor.add_attribute(t_zone) 

148 

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) 

158 

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 ) 

171 

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) 

183 

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" 

191 

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() 

208 

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 ) 

221 

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 ) 

227 

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) 

232 

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 ) 

245 

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 ) 

257 

258 # close the mqtt listening thread 

259 mqttc.loop_stop() 

260 # disconnect the mqtt device 

261 mqttc.disconnect() 

262 

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") 

271 

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") 

279 

280 plt.show() 

281 

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) 

290 

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) 

298 

299 clear_iot_agent(url=IOTA_URL, fiware_header=fiware_header) 

300 clear_context_broker(url=CB_URL, fiware_header=fiware_header)