Coverage for tutorials/ngsi_v2/e8_multientity_and_expression_language/e8_multientity_and_expression_language_solution.py: 0%
16 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 8: MultiEntity and Expression Language
4# The MultiEntity plugin allows the devices provisioned in the IoTAgent to map their
5attributes to more than one entity, declaring the target entity through the
6Configuration or Device provisioning APIs.
8# The IoTAgent Library provides an expression language for measurement transformation,
9that can be used to adapt the # information coming from the South Bound APIs to the
10information reported to the Context Broker. This is really useful when you need to
11adapt measure.
13# There are available two different expression languages jexl and legacy. The
14recommended language to use is jexl, which is newer and most powerful.
16# The input sections are marked with 'TODO'
18# #### Steps to complete:
19# 1. Setting up the expression language jexl
20# 2. Applying the expression language to device attributes
21# 3. Testing the expression language via MQTT messages
22# 4. Applying the expression language to device attributes in a multi-entity scenario
23"""
25# Import packages
26import time
27import datetime
29from filip.clients.ngsi_v2 import IoTAClient, ContextBrokerClient
30from filip.models.base import FiwareHeader
31from filip.models.ngsi_v2.context import ContextEntity, NamedContextAttribute
32from filip.models.ngsi_v2.iot import (
33 Device,
34 ServiceGroup,
35 TransportProtocol,
36 PayloadProtocol,
37 DeviceAttribute,
38 ExpressionLanguage,
39)
40from filip.utils.cleanup import clear_all
41from paho.mqtt import client as mqtt_client
42from paho.mqtt.client import CallbackAPIVersion
44# Host address of Context Broker
45CB_URL = "http://localhost:1026"
47# Host address of IoT-Agent
48IOTA_URL = "http://localhost:4041"
50# MQTT Broker
51MQTT_BROKER_HOST = "localhost"
52MQTT_BROKER_PORT = 1883
54# FIWARE Service
55SERVICE = "filip_tutorial"
56SERVICE_PATH = "/"
58# ToDo: Change the APIKEY to something unique. This represent the "token"
59# for IoT devices to connect (send/receive data ) with the platform. In the
60# context of MQTT, APIKEY is linked with the topic used for communication.
61APIKEY = "your_apikey"
63if __name__ == "__main__":
64 # FIWARE Header
65 fiware_header = FiwareHeader(service=SERVICE, service_path=SERVICE_PATH)
67 # Cleanup at the beginning
68 clear_all(fiware_header=fiware_header, cb_url=CB_URL, iota_url=IOTA_URL)
70 # IoT Agent and OCB Client
71 iota_client = IoTAClient(url=IOTA_URL, fiware_header=fiware_header)
72 cb_client = ContextBrokerClient(url=CB_URL, fiware_header=fiware_header)
74 # TODO: Setting expression language to JEXL at Service Group level
75 service_group1 = ServiceGroup(
76 entity_type="Thing",
77 resource="/iot/json",
78 apikey=APIKEY,
79 expressionLanguage=ExpressionLanguage.JEXL,
80 )
81 iota_client.post_group(service_group=service_group1)
83 # TODO: Create a device with two attributes 'location' and 'fillingLevel' that use
84 # expressions. These attributes are based on the attributes 'longitude',
85 # 'latitude' and 'level', while:
86 # 1. 'location' is an array with 'longitude' and 'latitude'.
87 # 2. 'fillingLevel' is 'level' divided by 100
88 device1 = Device(
89 device_id="waste_container_001",
90 entity_name="urn:ngsi-ld:WasteContainer:001",
91 entity_type="WasteContainer",
92 transport=TransportProtocol.MQTT,
93 protocol=PayloadProtocol.IOTA_JSON,
94 attributes=[
95 DeviceAttribute(name="latitude", type="Number"),
96 DeviceAttribute(name="longitude", type="Number"),
97 DeviceAttribute(name="level", type="Number"),
98 DeviceAttribute(
99 name="location", type="Array", expression="[longitude, latitude]"
100 ),
101 DeviceAttribute(
102 name="fillingLevel", type="Number", expression="level / 100"
103 ),
104 ],
105 )
106 iota_client.post_device(device=device1)
108 # TODO: Setting expression language to JEXL at Device level with five attributes, while
109 # 1. The attribute 'value' (Number) is itself multiplied by 5. The attribute
110 # 2. 'consumption' (Text) is the trimmed version of the attribute 'spaces' (Text).
111 # 3. The attribute 'iso_time' (Text) is the current 'timestamp' (Number) transformed into the ISO format.
112 device2 = Device(
113 device_id="waste_container_002",
114 entity_name="urn:ngsi-ld:WasteContainer:002",
115 entity_type="WasteContainer",
116 transport=TransportProtocol.MQTT,
117 protocol=PayloadProtocol.IOTA_JSON,
118 expressionLanguage=ExpressionLanguage.JEXL,
119 attributes=[
120 DeviceAttribute(name="value", type="Number", expression="5 * value"),
121 DeviceAttribute(name="spaces", type="Text"),
122 DeviceAttribute(name="consumption", type="Text", expression="spaces|trim"),
123 DeviceAttribute(name="timestamp", type="Number"),
124 DeviceAttribute(
125 name="iso_time", type="Text", expression="timestamp|toisodate"
126 ),
127 ],
128 )
129 iota_client.post_device(device=device2)
131 client = mqtt_client.Client(callback_api_version=CallbackAPIVersion.VERSION2)
132 client.username_pw_set(username="", password="")
133 client.connect(MQTT_BROKER_HOST, MQTT_BROKER_PORT)
134 client.loop_start()
136 # TODO: Publish attributes 'level', 'longitude' and 'latitude' of device1
137 client.publish(
138 topic=f"/json/{APIKEY}/{device1.device_id}/attrs",
139 payload='{"level": 99, "longitude": 12.0, "latitude": 23.0}',
140 )
142 # TODO: Publish attributes 'value', 'spaces' and 'timestamp' (in ms) of device2
143 client.publish(
144 topic=f"/json/{APIKEY}/{device2.device_id}/attrs",
145 payload=f'{{ "value": 10, "spaces": " foobar ",'
146 f' "timestamp": {datetime.datetime.now().timestamp() * 1000} }}',
147 )
149 client.disconnect()
151 time.sleep(2)
153 # Printing context entities of OCB
154 for context_entity in cb_client.get_entity_list(entity_types=["WasteContainer"]):
155 print(context_entity.model_dump_json(indent=4))
157 # Creating two SubWeatherStation entities
158 entity1 = ContextEntity(
159 id="urn:ngsi-ld:SubWeatherStation:001", type="SubWeatherStation"
160 )
161 entity1.add_attributes(attrs=[NamedContextAttribute(name="vol", type="Number")])
162 cb_client.post_entity(entity1)
164 entity2 = ContextEntity(
165 id="urn:ngsi-ld:SubWeatherStation:002", type="SubWeatherStation"
166 )
167 entity2.add_attributes(attrs=[NamedContextAttribute(name="vol", type="Number")])
168 cb_client.post_entity(entity2)
170 # TODO: Create a weather station device with multi entity attributes (Number).
171 # 'v' is multiplied by 100 and is a standard attribute.
172 # 'v1' and 'v2' are multiplied by 100 and should be linked with entities of
173 # the SubWeatherStation.
174 # The name of each attribute is 'vol'.
175 device3 = Device(
176 device_id="weather_station_001",
177 entity_name="urn:ngsi-ld:WeatherStation:001",
178 entity_type="WeatherStation",
179 transport=TransportProtocol.MQTT,
180 protocol=PayloadProtocol.IOTA_JSON,
181 expressionLanguage=ExpressionLanguage.JEXL,
182 attributes=[
183 DeviceAttribute(
184 object_id="v1",
185 name="vol",
186 type="Number",
187 expression="100 * v1",
188 entity_name="urn:ngsi-ld:SubWeatherStation:001",
189 entity_type="SubWeatherStation",
190 ),
191 DeviceAttribute(
192 object_id="v2",
193 name="vol",
194 type="Number",
195 expression="100 * v2",
196 entity_name="urn:ngsi-ld:SubWeatherStation:002",
197 entity_type="SubWeatherStation",
198 ),
199 DeviceAttribute(
200 object_id="v", name="vol", type="Number", expression="100 * v"
201 ),
202 ],
203 )
204 iota_client.post_device(device=device3)
206 client = mqtt_client.Client(callback_api_version=CallbackAPIVersion.VERSION2)
207 client.username_pw_set(username="", password="")
208 client.connect(MQTT_BROKER_HOST, MQTT_BROKER_PORT)
209 client.loop_start()
211 # TODO: Publish values to all attributes of device3
212 client.publish(
213 topic=f"/json/{APIKEY}/{device3.device_id}/attrs",
214 payload='{"v1": 10, "v2": 20, "v": 30}',
215 )
217 client.disconnect()
219 time.sleep(2)
221 # Printing context entities of OCB
222 for context_entity in cb_client.get_entity_list(
223 entity_types=["WeatherStation", "SubWeatherStation"]
224 ):
225 print(context_entity.model_dump_json(indent=4))