|
1 | 1 | """
|
2 | 2 | # # Exercise 4: Virtual Thermal Zone
|
3 | 3 |
|
4 |
| -# Create two virtual IoT device. One of them represents the temperature |
| 4 | +# Create two virtual IoT devices. One of them represents the temperature |
5 | 5 | # sensor for the air temperature of a the thermal zone, whereas the second
|
6 |
| -# represents a virtual weather station. Both devices publish there values to |
| 6 | +# represents a virtual weather station. Both devices publish their values to |
7 | 7 | # the platform via MQTT. Use the simulation model of
|
8 | 8 | # e1_virtual_weatherstation.py
|
9 | 9 | #
|
10 | 10 | # The input sections are marked with 'ToDo'
|
11 | 11 | #
|
12 | 12 | # #### Steps to complete:
|
13 | 13 | # 1. Set up the missing parameters in the parameter section
|
14 |
| -# 2. Create a service group and two devices |
| 14 | +# 2. Create a service group and two corresponding devices |
15 | 15 | # 3. Provision the service group and the devices
|
16 | 16 | # 4. Create an MQTT client using the filip.client.mqtt package and register
|
17 | 17 | # your service group and your devices
|
|
46 | 46 | IOTA_URL = "http://localhost:4041"
|
47 | 47 | # ToDo: Enter your mqtt broker url, e.g mqtt://test.mosquitto.org:1883
|
48 | 48 | MQTT_BROKER_URL = "mqtt://localhost:1883"
|
49 |
| -# ToDo: If required enter your username and password |
| 49 | +# ToDo: If required, enter your username and password |
50 | 50 | MQTT_USER = ""
|
51 |
| -MQTT_PW = "" |
| 51 | +MQTT_PW = "" |
52 | 52 | # FIWARE-Service
|
53 | 53 | SERVICE = 'filip_tutorial'
|
54 |
| -# FIWARE-Servicepath |
| 54 | +# FIWARE-Service path |
55 | 55 | # ToDo: Change the name of your service-path to something unique. If you run
|
56 |
| -# on a shared instance this very important in order to avoid user |
57 |
| -# collisions. You will use this service path through the whole tutorial. |
58 |
| -# If you forget to change it an error will be raised! |
| 56 | +# on a shared instance, this is very important in order to avoid user |
| 57 | +# collisions. You will use this service path throughout the whole tutorial. |
| 58 | +# If you forget to change it, an error will raise! |
59 | 59 | SERVICE_PATH = '/your_path'
|
60 | 60 | APIKEY = SERVICE_PATH.strip('/')
|
61 | 61 |
|
62 |
| -# Path to json-files to device configuration data for follow up exercises |
| 62 | +# Path to json-files to device configuration data for follow-up exercises |
63 | 63 | WRITE_GROUPS_FILEPATH = Path(
|
64 | 64 | "../e4_iot_thermal_zone_sensors_solution_groups.json")
|
65 | 65 | WRITE_DEVICES_FILEPATH = Path(
|
|
94 | 94 | history_weather_station = []
|
95 | 95 | history_zone_temperature_sensor = []
|
96 | 96 |
|
97 |
| - # Create a service group and add it to your |
| 97 | + # create a service group and add it to your |
98 | 98 | service_group = ServiceGroup(apikey=APIKEY,
|
99 | 99 | resource="/iot/json")
|
100 | 100 |
|
101 | 101 | # ToDo: create two IoTA-MQTT devices for the weather station and the zone
|
102 | 102 | # temperature sensor. Also add the simulation time as `active attribute`
|
103 | 103 | # to each device!
|
104 |
| - # |
105 | 104 | # create the weather station device
|
106 |
| - # create the simtime attribute and add during device creation |
107 |
| - t_sim = DeviceAttribute(name='simtime', |
| 105 | + # create the sim_time attribute and add it to the weather station's attributes |
| 106 | + t_sim = DeviceAttribute(name='sim_time', |
108 | 107 | object_id='t_sim',
|
109 | 108 | type="Number")
|
110 | 109 |
|
|
118 | 117 | commands=[])
|
119 | 118 |
|
120 | 119 | # create a temperature attribute and add it via the api of the
|
121 |
| - # `device`-model. Use the 't_amb' as `object_id`. `object_id` specifies |
| 120 | + # 'device'-model. Use the 't_amb' as 'object_id'. 'object_id' specifies |
122 | 121 | # what key will be used in the MQTT Message payload
|
123 | 122 | t_amb = DeviceAttribute(name='temperature',
|
124 | 123 | object_id='t_amb',
|
125 | 124 | type="Number")
|
126 | 125 |
|
127 | 126 | weather_station.add_attribute(t_amb)
|
128 | 127 |
|
129 |
| - # ToDo: create the zone temperature device add the t_sim attribute upon |
130 |
| - # creation |
| 128 | + # ToDo: Create the zone temperature device add the t_sim attribute upon |
| 129 | + # creation. |
131 | 130 | zone_temperature_sensor = Device(device_id='device:002',
|
132 | 131 | entity_name='urn:ngsi-ld:TemperatureSensor:001',
|
133 | 132 | entity_type='TemperatureSensor',
|
|
137 | 136 | attributes=[t_sim],
|
138 | 137 | commands=[])
|
139 | 138 | # ToDo: Create the temperature attribute. Use the 't_zone' as `object_id`.
|
140 |
| - # `object_id` specifies what key will be used in the MQTT Message payload |
| 139 | + # `object_id` specifies what key will be used in the MQTT Message payload. |
141 | 140 | t_zone = DeviceAttribute(name='temperature',
|
142 | 141 | object_id='t_zone',
|
143 | 142 | type="Number")
|
| 143 | + |
144 | 144 | zone_temperature_sensor.add_attribute(t_zone)
|
145 | 145 |
|
146 |
| - # ToDo: Create an IoTAClient |
| 146 | + # ToDo: Create an IoTAClient. |
147 | 147 | iotac = IoTAClient(url=IOTA_URL, fiware_header=fiware_header)
|
148 |
| - # ToDo: Provision service group and add it to your IoTAMQTTClient |
| 148 | + # ToDo: Provision service group and add it to your IoTAMQTTClient. |
149 | 149 | iotac.post_group(service_group=service_group, update=True)
|
150 |
| - # ToDo: Provision the devices at the IoTA-Agent |
151 |
| - # provision the WeatherStation device |
| 150 | + # ToDo: Provision the devices at the IoTA-Agent. |
| 151 | + # provision the weather station device |
152 | 152 | iotac.post_device(device=weather_station, update=True)
|
153 |
| - # ToDo: provision the zone temperature device |
| 153 | + # ToDo: Provision the zone temperature device. |
154 | 154 | iotac.post_device(device=zone_temperature_sensor, update=True)
|
155 | 155 |
|
156 |
| - # ToDo: Check in the context broker if the entities corresponding to your |
157 |
| - # devices where correctly created |
158 |
| - # ToDo: Create a context broker client |
| 156 | + # ToDo: Create a context broker client. |
| 157 | + # ToDo: Check in the context broker whether the entities corresponding to your |
| 158 | + # devices were correctly created. |
159 | 159 | cbc = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header)
|
160 |
| - # Get WeatherStation entity |
161 |
| - print(cbc.get_entity(weather_station.entity_name).json(indent=2)) |
162 |
| - # Get ZoneTemperatureSensor entity |
163 |
| - print(cbc.get_entity(zone_temperature_sensor.entity_name).json(indent=2)) |
| 160 | + # get weather station entity |
| 161 | + print(f"Weather station:\n{cbc.get_entity(weather_station.entity_name).model_dump_json(indent=2)}") |
| 162 | + # get zone temperature sensor entity |
| 163 | + print(f"Zone temperature sensor:\n{cbc.get_entity(zone_temperature_sensor.entity_name).model_dump_json(indent=2)}") |
164 | 164 |
|
165 |
| - # ToDo: create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient |
| 165 | + # ToDo: Create an MQTTv5 client using filip.clients.mqtt.IoTAMQTTClient. |
166 | 166 | mqttc = IoTAMQTTClient(protocol=mqtt.MQTTv5)
|
167 | 167 | # set user data if required
|
168 | 168 | mqttc.username_pw_set(username=MQTT_USER, password=MQTT_PW)
|
169 |
| - # ToDo: Register the service group with your MQTT-Client |
| 169 | + # ToDo: Register the service group with your MQTT-Client. |
170 | 170 | mqttc.add_service_group(service_group=service_group)
|
171 |
| - # ToDo: Register devices with your MQTT-Client |
| 171 | + # ToDo: Register devices with your MQTT-Client. |
172 | 172 | # register the weather station
|
173 | 173 | mqttc.add_device(weather_station)
|
174 |
| - # ToDo: register the zone temperature sensor |
| 174 | + # ToDo: register the zone temperature sensor. |
175 | 175 | mqttc.add_device(zone_temperature_sensor)
|
176 | 176 |
|
177 |
| - # The IoTAMQTTClient automatically creates the outgoing topics from the |
| 177 | + # The IoTAMQTTClient automatically creates outgoing topics from the |
178 | 178 | # device configuration during runtime. Hence, we need to construct them
|
179 |
| - # manually in order to subscribe to them. This is usually not required as |
180 |
| - # only the platform should listen to incoming traffic. |
181 |
| - # if you want to listen subscribe to the following topics: |
| 179 | + # manually in order to subscribe to them. This is usually not required as |
| 180 | + # only the platform should listen to the incoming traffic. |
| 181 | + # If you want to listen subscribe to the following topics: |
182 | 182 | # "/json/<APIKEY>/<weather_station.device_id>/attrs"
|
183 | 183 | # "/json/<APIKEY>/<zone_temperature_sensor.device_id>/attrs"
|
184 | 184 |
|
185 |
| - # ToDO: connect to the mqtt broker and subscribe to your topic |
| 185 | + # ToDO: Connect to the mqtt broker and subscribe to your topic. |
186 | 186 | mqtt_url = urlparse(MQTT_BROKER_URL)
|
187 | 187 | mqttc.connect(host=mqtt_url.hostname,
|
188 | 188 | port=mqtt_url.port,
|
|
197 | 197 | # create a non-blocking thread for mqtt communication
|
198 | 198 | mqttc.loop_start()
|
199 | 199 |
|
200 |
| - # ToDo: Create a loop that publishes every second a message to the broker |
201 |
| - # that holds the simulation time "simtime" and the corresponding |
202 |
| - # temperature "temperature" the loop should. You may use the `object_id` |
203 |
| - # or the attribute name as key in your payload. |
| 200 | + # ToDo: Create a loop that publishes a message every 100 milliseconds |
| 201 | + # to the broker that holds the simulation time "sim_time" and the |
| 202 | + # corresponding temperature "temperature". You may use the 'object_id' |
| 203 | + # or the attribute name as a key in your payload. |
204 | 204 | for t_sim in range(sim_model.t_start,
|
205 | 205 | sim_model.t_end + int(COM_STEP),
|
206 | 206 | int(COM_STEP)):
|
207 | 207 | # publish the simulated ambient temperature
|
208 | 208 | mqttc.publish(device_id=weather_station.device_id,
|
209 | 209 | payload={"temperature": sim_model.t_amb,
|
210 |
| - "simtime": sim_model.t_sim}) |
| 210 | + "sim_time": sim_model.t_sim}) |
211 | 211 |
|
212 |
| - # ToDo: publish the simulated zone temperature |
| 212 | + # ToDo: Publish the simulated zone temperature. |
213 | 213 | mqttc.publish(device_id=zone_temperature_sensor.device_id,
|
214 | 214 | payload={"temperature": sim_model.t_zone,
|
215 |
| - "simtime": sim_model.t_sim}) |
| 215 | + "sim_time": sim_model.t_sim}) |
216 | 216 |
|
217 |
| - # simulation step for next loop |
| 217 | + # simulation step for the next loop |
218 | 218 | sim_model.do_step(int(t_sim + COM_STEP))
|
219 | 219 | # wait for one second before publishing the next values
|
220 |
| - time.sleep(1) |
| 220 | + time.sleep(0.1) |
221 | 221 |
|
222 |
| - # Get corresponding entities and write values to history |
| 222 | + # get corresponding entities and store the data |
223 | 223 | weather_station_entity = cbc.get_entity(
|
224 | 224 | entity_id=weather_station.entity_name,
|
225 | 225 | entity_type=weather_station.entity_type
|
226 | 226 | )
|
227 | 227 | # append the data to the local history
|
228 | 228 | history_weather_station.append(
|
229 |
| - {"simtime": weather_station_entity.simtime.value, |
| 229 | + {"sim_time": weather_station_entity.sim_time.value, |
230 | 230 | "temperature": weather_station_entity.temperature.value})
|
231 | 231 |
|
232 |
| - # ToDo: Get ZoneTemperatureSensor and write values to history |
| 232 | + # ToDo: Get zone temperature sensor and store the data. |
233 | 233 | zone_temperature_sensor_entity = cbc.get_entity(
|
234 | 234 | entity_id=zone_temperature_sensor.entity_name,
|
235 | 235 | entity_type=zone_temperature_sensor.entity_type
|
236 | 236 | )
|
237 | 237 | history_zone_temperature_sensor.append(
|
238 |
| - {"simtime": zone_temperature_sensor_entity.simtime.value, |
| 238 | + {"sim_time": zone_temperature_sensor_entity.sim_time.value, |
239 | 239 | "temperature": zone_temperature_sensor_entity.temperature.value})
|
240 | 240 |
|
241 | 241 | # close the mqtt listening thread
|
242 | 242 | mqttc.loop_stop()
|
243 | 243 | # disconnect the mqtt device
|
244 | 244 | mqttc.disconnect()
|
245 | 245 |
|
246 |
| - # plot results |
| 246 | + # plot the results |
247 | 247 | fig, ax = plt.subplots()
|
248 |
| - t_simulation = [item["simtime"] for item in history_weather_station] |
| 248 | + t_simulation = [item["sim_time"]/3600 for item in history_weather_station] |
249 | 249 | temperature = [item["temperature"] for item in history_weather_station]
|
250 | 250 | ax.plot(t_simulation, temperature)
|
251 |
| - ax.set_xlabel('time in s') |
| 251 | + ax.set_xlabel('time in h') |
252 | 252 | ax.set_ylabel('ambient temperature in °C')
|
253 | 253 |
|
254 | 254 | fig2, ax2 = plt.subplots()
|
255 |
| - t_simulation = [item["simtime"] for item in history_zone_temperature_sensor] |
| 255 | + t_simulation = [item["sim_time"]/3600 for item in history_zone_temperature_sensor] |
256 | 256 | temperature = [item["temperature"] for item in
|
257 | 257 | history_zone_temperature_sensor]
|
258 | 258 | ax2.plot(t_simulation, temperature)
|
259 |
| - ax2.set_xlabel('time in s') |
| 259 | + ax2.set_xlabel('time in h') |
260 | 260 | ax2.set_ylabel('zone temperature in °C')
|
261 | 261 |
|
262 | 262 | plt.show()
|
|
266 | 266 | f"Wrong file extension! {WRITE_DEVICES_FILEPATH.suffix}"
|
267 | 267 | WRITE_DEVICES_FILEPATH.touch(exist_ok=True)
|
268 | 268 | with WRITE_DEVICES_FILEPATH.open('w', encoding='utf-8') as f:
|
269 |
| - devices = [item.dict() for item in iotac.get_device_list()] |
| 269 | + devices = [item.model_dump() for item in iotac.get_device_list()] |
270 | 270 | json.dump(devices, f, ensure_ascii=False, indent=2)
|
271 | 271 |
|
272 | 272 | assert WRITE_GROUPS_FILEPATH.suffix == '.json', \
|
273 | 273 | f"Wrong file extension! {WRITE_GROUPS_FILEPATH.suffix}"
|
274 | 274 | WRITE_GROUPS_FILEPATH.touch(exist_ok=True)
|
275 | 275 | with WRITE_GROUPS_FILEPATH.open('w', encoding='utf-8') as f:
|
276 |
| - groups = [item.dict() for item in iotac.get_group_list()] |
| 276 | + groups = [item.model_dump() for item in iotac.get_group_list()] |
277 | 277 | json.dump(groups, f, ensure_ascii=False, indent=2)
|
278 | 278 |
|
279 | 279 | clear_context_broker(url=CB_URL, fiware_header=fiware_header)
|
|
0 commit comments