Skip to content

Commit f68bb2c

Browse files
authored
Initial shadow integration (#12)
* Initial shadow integration * Version bump
1 parent f0aff34 commit f68bb2c

File tree

6 files changed

+288
-41
lines changed

6 files changed

+288
-41
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "AWSCRT"
22
uuid = "df31ea59-17a4-4ebd-9d69-4f45266dc2c7"
3-
version = "0.1.0"
3+
version = "0.1.1"
44

55
[deps]
66
AWSCRT_jll = "01db5350-6ea1-5d9a-9a47-8a31a394cb9c"

src/AWSCRT.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,7 @@ export unsubscribe
116116
export resubscribe_existing_topics
117117
export publish
118118

119+
include("IOTShadow.jl")
120+
export ShadowClient
121+
119122
end

src/AWSMQTT.jl

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
const subscribe_callback_docs = "Callback invoked when message received. See [`OnMessage`](@ref) for the required signature."
2+
const subscribe_qos_docs = "Maximum requested QoS that server may use when sending messages to the client. The server may grant a lower QoS in the SUBACK (see returned task)."
3+
const subscribe_return_docs = """Returns a task and the ID of the SUBSCRIBE packet.
4+
The task completes when a SUBACK is received from the server.
5+
6+
If successful, the task will contain a dict with the following members:
7+
- `:packet_id (Int)`: ID of the SUBSCRIBE packet being acknowledged.
8+
- `:topic (String)`: Topic filter of the SUBSCRIBE packet being acknowledged.
9+
- `:qos (aws_mqtt_qos)`: Maximum QoS that was granted by the server. This may be lower than the requested QoS.
10+
11+
If unsuccessful, the task contains an exception."""
12+
13+
114
"""
215
MQTTClient(
316
tls_ctx::Union{ClientTLSContext,Nothing},
@@ -76,7 +89,7 @@ Arguments:
7689
- `topic (String)`: Topic receiving message.
7790
- `payload (String)`: Payload of message.
7891
- `dup (Bool)`: DUP flag. If True, this might be re-delivery of an earlier attempt to send the message.
79-
- `qos (aws_mqtt_qos)`: Quality of Service used to deliver the message.
92+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
8093
- `retain (Bool)`: Retain flag. If `true`, the message was sent as a result of a new subscription being made by the client.
8194
8295
Returns `nothing`.
@@ -561,20 +574,12 @@ Once subscribed, `callback` is invoked each time a message matching the `topic`
561574
possible for such messages to arrive before the SUBACK is received.
562575
563576
Arguments:
564-
- `connection`: Connection to use.
565-
- `topic`: Subscribe to this topic filter, which may include wildcards.
566-
- `qos`: Maximum requested QoS that server may use when sending messages to the client. The server may grant a lower QoS in the SUBACK (see returned task).
567-
- `callback`: Optional callback invoked when message received. See [`OnMessage`](@ref) for the required signature.
568-
569-
Returns a task and the ID of the SUBSCRIBE packet.
570-
The task completes when a SUBACK is received from the server.
571-
572-
If successful, the task will contain a dict with the following members:
573-
- `:packet_id (Int)`: ID of the SUBSCRIBE packet being acknowledged.
574-
- `:topic (String)`: Topic filter of the SUBSCRIBE packet being acknowledged.
575-
- `:qos (aws_mqtt_qos)`: Maximum QoS that was granted by the server. This may be lower than the requested QoS.
577+
- `connection (MQTTConnection)`: Connection to use.
578+
- `topic (String)`: Subscribe to this topic filter, which may include wildcards.
579+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
580+
- `callback (OnMessage)`: $subscribe_callback_docs
576581
577-
If unsuccessful, the task contains an exception.
582+
$subscribe_return_docs
578583
"""
579584
function subscribe(connection::MQTTConnection, topic::String, qos::aws_mqtt_qos, callback::OnMessage)
580585
on_message_fcb = ForeignCallbacks.ForeignCallback{OnMessageMsg}() do msg
@@ -653,8 +658,8 @@ end
653658
Set callback to be invoked when ANY message is received.
654659
655660
Arguments:
656-
- `connection`: Connection to use.
657-
- `callback`: Optional callback invoked when message received. See [`OnMessage`](@ref) for the required signature. Set to `nothing` to clear this callback.
661+
- `connection (MQTTConnection)`: Connection to use.
662+
- `callback (Union{OnMessage,Nothing})`: Optional callback invoked when message received. See [`OnMessage`](@ref) for the required signature. Set to `nothing` to clear this callback.
658663
659664
Returns nothing.
660665
"""
@@ -728,23 +733,25 @@ function on_unsubscribe_complete(
728733
return nothing
729734
end
730735

736+
const unsubscribe_return_docs = """Returns a task and the ID of the UNSUBSCRIBE packet.
737+
The task completes when an UNSUBACK is received from the server.
738+
739+
If successful, the task will contain a dict with the following members:
740+
- `:packet_id (Int)`: ID of the UNSUBSCRIBE packet being acknowledged.
741+
742+
If unsuccessful, the task will throw an exception."""
743+
731744
"""
732745
unsubscribe(connection::MQTTConnection, topic::String)
733746
734747
Unsubscribe from a topic filter (async).
735748
The client sends an UNSUBSCRIBE packet, and the server responds with an UNSUBACK.
736749
737750
Arguments:
738-
- `connection`: Connection to use.
739-
- `topic`: Unsubscribe from this topic filter.
740-
741-
Returns a task and the ID of the UNSUBSCRIBE packet.
742-
The task completes when an UNSUBACK is received from the server.
743-
744-
If successful, the task will contain a dict with the following members:
745-
- `:packet_id (Int)`: ID of the UNSUBSCRIBE packet being acknowledged.
751+
- `connection (MQTTConnection)`: Connection to use.
752+
- `topic (String)`: Unsubscribe from this topic filter.
746753
747-
If unsuccessful, the task will throw an exception.
754+
$unsubscribe_return_docs
748755
"""
749756
function unsubscribe(connection::MQTTConnection, topic::String)
750757
ch = Channel(1)
@@ -978,19 +985,7 @@ function on_publish_complete(
978985
return nothing
979986
end
980987

981-
"""
982-
publish(connection::MQTTConnection, topic::String, payload::String, qos::aws_mqtt_qos, retain::Bool = false)
983-
984-
Publish message (async).
985-
If the device is offline, the PUBLISH packet will be sent once the connection resumes.
986-
987-
Arguments:
988-
- `connection`: Connection to use.
989-
- `topic`: Topic name.
990-
- `payload`: Contents of message.
991-
- `qos`: Quality of Service for delivering this message.
992-
- `retain`: If `true`, the server will store the message and its QoS so that it can be delivered to future subscribers whose subscriptions match its topic name.
993-
988+
const publish_return_docs = """
994989
Returns a task and the ID of the PUBLISH packet.
995990
The QoS determines when the task completes:
996991
- For QoS 0, completes as soon as the packet is sent.
@@ -1000,7 +995,22 @@ The QoS determines when the task completes:
1000995
If successful, the task will contain a dict with the following members:
1001996
- `:packet_id (Int)`: ID of the PUBLISH packet that is complete.
1002997
1003-
If unsuccessful, the task will throw an exception.
998+
If unsuccessful, the task will throw an exception."""
999+
1000+
"""
1001+
publish(connection::MQTTConnection, topic::String, payload::String, qos::aws_mqtt_qos, retain::Bool = false)
1002+
1003+
Publish message (async).
1004+
If the device is offline, the PUBLISH packet will be sent once the connection resumes.
1005+
1006+
Arguments:
1007+
- `connection (MQTTConnection)`: Connection to use.
1008+
- `topic (String)`: Topic name.
1009+
- `payload (String)`: Contents of message.
1010+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
1011+
- `retain (Bool)`: If `true`, the server will store the message and its QoS so that it can be delivered to future subscribers whose subscriptions match its topic name.
1012+
1013+
$publish_return_docs
10041014
"""
10051015
function publish(connection::MQTTConnection, topic::String, payload::String, qos::aws_mqtt_qos, retain::Bool = false)
10061016
ch = Channel(1)

src/IOTShadow.jl

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"""
2+
ShadowClient(
3+
connection::MQTTConnection,
4+
)
5+
6+
Device Shadow service client.
7+
[AWS Documentation](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html).
8+
9+
Arguments:
10+
- `connection (MQTTConnection)`: MQTT connection to publish and subscribe on.
11+
"""
12+
mutable struct ShadowClient
13+
connection::MQTTConnection
14+
shadow_topic_prefix::Union{String,Nothing}
15+
16+
function ShadowClient(connection::MQTTConnection)
17+
new(connection, nothing)
18+
end
19+
end
20+
21+
"""
22+
on_shadow_message(
23+
shadow_client::ShadowClient,
24+
topic::String,
25+
payload::String,
26+
dup::Bool,
27+
qos::aws_mqtt_qos,
28+
retain::Bool,
29+
)
30+
31+
A callback invoked when a shadow document message is received.
32+
33+
Arguments:
34+
- `shadow_client (ShadowClient)`: Shadow client that received the message.
35+
- `topic (String)`: Topic receiving message.
36+
- `payload (String)`: Payload of message.
37+
- `dup (Bool)`: DUP flag. If True, this might be re-delivery of an earlier attempt to send the message.
38+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
39+
- `retain (Bool)`: Retain flag. If `true`, the message was sent as a result of a new subscription being made by the client.
40+
41+
Returns `nothing`.
42+
"""
43+
const OnShadowMessage = Function
44+
45+
"""
46+
subscribe(
47+
client::ShadowClient,
48+
thing_name::String,
49+
shadow_name::Union{String,Nothing},
50+
qos::aws_mqtt_qos,
51+
callback::OnShadowMessage,
52+
)
53+
54+
Subscribes to all topics under the given shadow document using a wildcard, including but not limited to:
55+
- `/get/accepted`
56+
- `/get/rejected`
57+
- `/update/delta`
58+
- `/update/accepted`
59+
- `/update/documents`
60+
- `/update/rejected`
61+
- `/delete/accepted`
62+
- `/delete/rejected`
63+
64+
Arguments:
65+
- `client (ShadowClient)`: Shadow client to use.
66+
- `thing_name (String)`: Name of the Thing in AWS IoT.
67+
- `shadow_name (Union{String,Nothing})`: Shadow name for a named shadow document or `nothing` for an unnamed shadow document.
68+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
69+
- `callback (OnShadowMessage)`: Callback invoked when message received. See [`OnShadowMessage`](@ref) for the required signature.
70+
71+
Returns the tasks from each subscribe call (`/get/#`, `/update/#`, and `/delete/#`).
72+
"""
73+
function subscribe(
74+
client::ShadowClient,
75+
thing_name::String,
76+
shadow_name::Union{String,Nothing},
77+
qos::aws_mqtt_qos,
78+
callback::OnShadowMessage,
79+
)
80+
client.shadow_topic_prefix =
81+
shadow_name === nothing ? "\$aws/things/$thing_name/shadow" :
82+
"\$aws/things/$thing_name/shadow/name/$shadow_name"
83+
mqtt_callback =
84+
(topic::String, payload::String, dup::Bool, qos::aws_mqtt_qos, retain::Bool) ->
85+
callback(client, topic, payload, dup, qos, retain)
86+
getf = subscribe(client.connection, "$(client.shadow_topic_prefix)/get/#", qos, mqtt_callback)
87+
updatef = subscribe(client.connection, "$(client.shadow_topic_prefix)/update/#", qos, mqtt_callback)
88+
deletef = subscribe(client.connection, "$(client.shadow_topic_prefix)/delete/#", qos, mqtt_callback)
89+
return getf, updatef, deletef
90+
end
91+
92+
"""
93+
unsubscribe(client::ShadowClient)
94+
95+
Unsubscribes from the shadow document topics.
96+
97+
Arguments:
98+
- `client (ShadowClient)`: Shadow client to use.
99+
100+
$unsubscribe_return_docs
101+
"""
102+
function unsubscribe(client::ShadowClient)
103+
topic = client.shadow_topic_prefix
104+
client.shadow_topic_prefix = nothing
105+
return unsubscribe(client.connection, "$topic/#")
106+
end
107+
108+
"""
109+
publish(client::ShadowClient, topic::String, payload::String, qos::aws_mqtt_qos)
110+
111+
Publishes the payload to the topic under the configured shadow topic.
112+
113+
Arguments:
114+
- `client (ShadowClient)`: Shadow client to use.
115+
- `topic (String)`: Topic name, not including the shadow topic prefix. E.g. `/get`.
116+
- `payload (String)`: Message contents.
117+
- `qos (aws_mqtt_qos)`: $subscribe_qos_docs
118+
119+
$publish_return_docs
120+
"""
121+
function publish(client::ShadowClient, topic::String, payload::String, qos::aws_mqtt_qos)
122+
return publish(client.connection, "$(client.shadow_topic_prefix)$topic", payload, qos)
123+
end

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[deps]
22
AWSCRT_jll = "01db5350-6ea1-5d9a-9a47-8a31a394cb9c"
3+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
34
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
45
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

0 commit comments

Comments
 (0)