mqtt-sub_handler 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mqtt/persistence.rb +175 -0
- data/lib/mqtt/persistence_extensions.rb +13 -0
- data/lib/mqtt/sub_testing.rb +4 -2
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eea6058ba5750daeebbf41696765e1702b60495a
|
4
|
+
data.tar.gz: 3dbea34eea6371096173df3cccf2468d682e0786
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cda8aadc97990675ca7d3b443fa69f82dfb253a4c67eddad0f0bc7a769b24012d30947cd8e7d72df417eea919981590a17e3166e7aa31f9a7deb6bde58d9478
|
7
|
+
data.tar.gz: 1cf6cd63dde250ad6da0d09dbea8b0610cbd375db61305df229d31951961902d26d789fe476b77b9d9be932f9ffcf779474d70372b8a2c30bbbee84d8582534b
|
@@ -0,0 +1,175 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
require_relative 'persistence_extensions.rb'
|
4
|
+
|
5
|
+
module MQTT
|
6
|
+
class Persistence
|
7
|
+
# Provide persistence for tasks that regularly restart, using MQTT retained messages.
|
8
|
+
# As a fun side effect, this also allows for live monitoring and modifying!
|
9
|
+
#
|
10
|
+
# @author Xasin
|
11
|
+
|
12
|
+
|
13
|
+
# Initialize the Persistence instance.
|
14
|
+
# This will immediately connect and subscribe to the wildcard of `dir + "/#"`
|
15
|
+
# @param mqtt [MQTT::SubHandler] The MQTT instance to use for storing/communication
|
16
|
+
# @param dir [String] A valid MQTT topic string under which to nest the persistence.
|
17
|
+
def initialize(mqtt, dir = "Persistence")
|
18
|
+
raise ArgumentError, "Not a mqtt class!" unless mqtt.is_a? MQTT::SubHandler
|
19
|
+
|
20
|
+
@mqtt = mqtt;
|
21
|
+
@dir = dir;
|
22
|
+
|
23
|
+
@rawData = Hash.new();
|
24
|
+
@paramList = Hash.new();
|
25
|
+
|
26
|
+
@mqtt.subscribe_to "#{@dir}/+" do |data, key|
|
27
|
+
_process_data(data, key[0].to_sym);
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Prepare a piece of data to be sent to the MQTT topic
|
33
|
+
# It must either respond to `to_mqtt_string` or to `to_json`
|
34
|
+
def _prepare_send(data, key)
|
35
|
+
if(data.respond_to? :to_mqtt_string)
|
36
|
+
return data.to_mqtt_string;
|
37
|
+
else
|
38
|
+
return data.to_json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
private :_prepare_send
|
42
|
+
|
43
|
+
# Parse a raw String received from MQTT.
|
44
|
+
# If the given class type responds to `from_mqtt_string`, that
|
45
|
+
# function is used for processing.
|
46
|
+
# `JSON.parse` will be used otherwise.
|
47
|
+
# @return [key-type, nil] Returns the parsed data, nil if that fails.
|
48
|
+
def _parse_received(data, key)
|
49
|
+
begin
|
50
|
+
dType = @paramList[key][:type];
|
51
|
+
if(dType.respond_to? :from_mqtt_string)
|
52
|
+
return dType.from_mqtt_string(data);
|
53
|
+
else
|
54
|
+
return JSON.parse(data, symbolize_names: true);
|
55
|
+
end
|
56
|
+
rescue JSON::ParserError
|
57
|
+
return nil;
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :_parse_received
|
61
|
+
|
62
|
+
# This processes new data received from MQTT.
|
63
|
+
# It returns if the raw string hasn't changed, and will trigger the callbacks.
|
64
|
+
def _process_data(data, key)
|
65
|
+
return if @rawData[key] == data;
|
66
|
+
@rawData[key] = data;
|
67
|
+
|
68
|
+
return unless param = @paramList[key];
|
69
|
+
|
70
|
+
oldData = param[:current];
|
71
|
+
param[:current] = _parse_received(data, key);
|
72
|
+
param[:cbList].each do |cb|
|
73
|
+
cb.call(param[:current], oldData);
|
74
|
+
end
|
75
|
+
|
76
|
+
if(param[:first])
|
77
|
+
param.delete :first
|
78
|
+
param[:cbList] = [param[:cbList], param[:change_cb]].flatten;
|
79
|
+
end
|
80
|
+
end
|
81
|
+
private :_process_data
|
82
|
+
|
83
|
+
# Force setting up a key, overwriting what was already configured.
|
84
|
+
# You usually don't need this!
|
85
|
+
# @param key [Symbol] The key that should be set up.
|
86
|
+
# @param type_class [#from_mqtt_string] The class to use to parse the data. A JSON-conversion
|
87
|
+
# is used if the class doesn't respond to `from_mqtt_string`
|
88
|
+
def setup!(key, type_class = nil)
|
89
|
+
raise ArgumentError, "Key needs to be a symbol!" unless key.is_a? Symbol
|
90
|
+
|
91
|
+
@paramList[key] = {
|
92
|
+
type: type_class,
|
93
|
+
current: nil,
|
94
|
+
cbList: Array.new(),
|
95
|
+
|
96
|
+
first: true,
|
97
|
+
change_cb: Array.new(),
|
98
|
+
}
|
99
|
+
|
100
|
+
if(data = @rawData[key])
|
101
|
+
@rawData[key] = nil;
|
102
|
+
_process_data(data, key);
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# Set up a key, unless it has already been configured.
|
106
|
+
# @param (See #setup!)
|
107
|
+
def setup(key, type_class = nil)
|
108
|
+
return if @paramList.key? key;
|
109
|
+
setup!(key, type_class);
|
110
|
+
end
|
111
|
+
|
112
|
+
# Read out a given, set-up key, returning `nil` if nothing has been read yet (or the value is nil right now),
|
113
|
+
# returning the data otherwise.
|
114
|
+
# If a block is given (using the on_set alias), this block will be added as a callback.
|
115
|
+
# @param key [Symbol] The key to fetch. Must have been set up first!
|
116
|
+
# @yieldparam newData The newly parsed data (depending on the key's type.)
|
117
|
+
# @yieldparam oldData The former data that was present.
|
118
|
+
def [](key, &callback)
|
119
|
+
raise ArgumentError, "Key needs to be a symbol!" unless key.is_a? Symbol
|
120
|
+
raise ArgumentError, "Key has not been set up!" unless @paramList.key? key;
|
121
|
+
|
122
|
+
if(callback)
|
123
|
+
@paramList[key][:cbList] << callback
|
124
|
+
yield(@paramList[key][:current], nil) if @paramList[key][:current]
|
125
|
+
end
|
126
|
+
|
127
|
+
return @paramList[key][:current];
|
128
|
+
end
|
129
|
+
alias on_set []
|
130
|
+
|
131
|
+
# Set a given key to a new value, provided the key has been set up.
|
132
|
+
# Will run the `on_change` and `on_set` callbacks immediately, then publish to MQTT.
|
133
|
+
# @param key [Symbol] The key to write. Must have been set up first!
|
134
|
+
# @param data [#to_json, #to_mqtt_string] The data to write.
|
135
|
+
def []=(key, data)
|
136
|
+
raise ArgumentError, "Key needs to be a symbol!" unless key.is_a? Symbol
|
137
|
+
raise ArgumentError, "Key has not been set up!" unless param = @paramList[key];
|
138
|
+
|
139
|
+
newString = _prepare_send(data, key);
|
140
|
+
return if @rawData[key] == newString;
|
141
|
+
@rawData[key] = newString;
|
142
|
+
|
143
|
+
if(param[:first])
|
144
|
+
param.delete :first
|
145
|
+
param[:cbList] = [param[:cbList], param[:change_cb]].flatten;
|
146
|
+
end
|
147
|
+
|
148
|
+
oldData = param[:current];
|
149
|
+
param[:current] = data;
|
150
|
+
param[:cbList].each do |cb|
|
151
|
+
cb.call(param[:current], oldData);
|
152
|
+
end
|
153
|
+
|
154
|
+
@mqtt.publish_to "#{@dir}/#{key}", newString, retain: true;
|
155
|
+
end
|
156
|
+
|
157
|
+
# Add a callback to be triggered whenever the key's value changes.
|
158
|
+
# The slight difference to `on_set` is that this callback is not executed on the very first
|
159
|
+
# value received from MQTT, making it easier to not trigger some code on restart, only on actual changes.
|
160
|
+
# @param (See #[])
|
161
|
+
# @yieldparam (See #[])
|
162
|
+
def on_change(key, &callback)
|
163
|
+
raise ArgumentError, "Key needs to be a symbol!" unless key.is_a? Symbol
|
164
|
+
raise ArgumentError, "Key has not been set up!" unless @paramList.key? key;
|
165
|
+
|
166
|
+
raise ArgumentError, "A callback needs to be given!" unless callback;
|
167
|
+
|
168
|
+
if(@paramList[key][:first])
|
169
|
+
@paramList[key][:change_cb] << callback;
|
170
|
+
else
|
171
|
+
@paramList[key][:cbList] << callback;
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
data/lib/mqtt/sub_testing.rb
CHANGED
@@ -26,8 +26,10 @@ module MQTT
|
|
26
26
|
private :call_interested
|
27
27
|
|
28
28
|
def raw_subscribe_to(topic, qos: nil)
|
29
|
-
|
30
|
-
|
29
|
+
@retained_topics.each do |retTopic, retData|
|
30
|
+
if SubHandler.getTopicMatch(retTopic, SubHandler.get_topic_split(topic))
|
31
|
+
publish_to(retTopic, retData)
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
private :raw_subscribe_to
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mqtt-sub_handler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xasin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mqtt
|
@@ -89,10 +89,12 @@ extra_rdoc_files: []
|
|
89
89
|
files:
|
90
90
|
- README.md
|
91
91
|
- lib/mqtt/Waitpoint.rb
|
92
|
+
- lib/mqtt/persistence.rb
|
93
|
+
- lib/mqtt/persistence_extensions.rb
|
92
94
|
- lib/mqtt/sub_handler.rb
|
93
95
|
- lib/mqtt/sub_testing.rb
|
94
96
|
- lib/mqtt/subscription_classes.rb
|
95
|
-
homepage: https://github.com/XasWorks/XasCode/tree/
|
97
|
+
homepage: https://github.com/XasWorks/XasCode/tree/master/Ruby/MQTT
|
96
98
|
licenses:
|
97
99
|
- GPL-3.0
|
98
100
|
metadata: {}
|
@@ -112,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
114
|
version: '0'
|
113
115
|
requirements: []
|
114
116
|
rubyforge_project:
|
115
|
-
rubygems_version: 2.
|
117
|
+
rubygems_version: 2.5.2.1
|
116
118
|
signing_key:
|
117
119
|
specification_version: 4
|
118
120
|
summary: Asynchronous, topic-based MQTT gem
|