somfy_sdn 2.0.0 → 2.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b14639678ed3c5a54d4f8d70fd217898238aed5ccd1c81c0f3b78d04592e48b
4
- data.tar.gz: 26d14eb134eee69523d2bca19f8ec32c63a3fc110ff7da704681569de2b8422b
3
+ metadata.gz: df44cc648ee7fa0079f6067deee6b8fa488c1cbdc31acc252d9803eb1a32cdf3
4
+ data.tar.gz: 17cda1e8629bc58d88cb202286a7d9b5bb4d88de28d3a9c133c669796b979c60
5
5
  SHA512:
6
- metadata.gz: 3da036c72689def6022a487078bc1024ff9aa10c1b9ec7d04ce6e19a0b5b1e3956e18dcdd44272e6e7e72175afe335dc7d9f4c29785004b88ad5fda344b7a84d
7
- data.tar.gz: c12a57173231c7be1b4b7557cb71c4c58fcc312deace5157893850d5301696ca77b1e743d6ed4e2b005c3cc532c267875f234bbe5bf92a564a17d5fa921b3ff9
6
+ metadata.gz: 7915001ae78047ac6e0669d2f362bcb165db06fe6ca29c8a155150c04093044abd9f158b89ee81c836dcece92114a60a39ca139f30317791f08bb99a132b5ad1
7
+ data.tar.gz: 7f97633393e8daeefdf9bafb8e12f4534e1c724939b161357543df808a630815ff34dc42af5fc172dc45d09101bf9bf4bccd24fb53cb181eebb37447287bd32e
data/lib/sdn/cli/mqtt.rb CHANGED
@@ -40,6 +40,7 @@ module SDN
40
40
  @auto_discover = auto_discover
41
41
  @motors_found = true
42
42
 
43
+ clear_tree(@base_topic)
43
44
  publish_basic_attributes
44
45
 
45
46
  @sdn = Client.new(port)
@@ -50,7 +51,7 @@ module SDN
50
51
  end
51
52
 
52
53
  def publish(topic, value)
53
- @mqtt.publish("#{@base_topic}/#{topic}", value, true, 0)
54
+ @mqtt.publish("#{@base_topic}/#{topic}", value, retain: true, qos: 1)
54
55
  end
55
56
 
56
57
  def subscribe(topic)
@@ -67,207 +68,222 @@ module SDN
67
68
  end
68
69
  end
69
70
 
70
- def publish_basic_attributes
71
- publish("$homie", "v4.0.0")
72
- publish("$name", "Somfy SDN Network")
73
- publish("$state", "init")
74
- publish("$nodes", "FFFFFF")
75
-
76
- publish("FFFFFF/$name", "Broadcast")
77
- publish("FFFFFF/$type", "sdn")
78
- publish("FFFFFF/$properties", "discover")
79
-
80
- publish("FFFFFF/discover/$name", "Trigger Motor Discovery")
81
- publish("FFFFFF/discover/$datatype", "enum")
82
- publish("FFFFFF/discover/$format", "discover")
83
- publish("FFFFFF/discover/$settable", "true")
84
- publish("FFFFFF/discover/$retained", "false")
85
-
86
- subscribe("+/discover/set")
87
- subscribe("+/label/set")
88
- subscribe("+/control/set")
89
- subscribe("+/jog-ms/set")
90
- subscribe("+/jog-pulses/set")
91
- subscribe("+/position-pulses/set")
92
- subscribe("+/position-percent/set")
93
- subscribe("+/ip/set")
94
- subscribe("+/reset/set")
95
- subscribe("+/direction/set")
96
- subscribe("+/up-speed/set")
97
- subscribe("+/down-speed/set")
98
- subscribe("+/slow-speed/set")
99
- subscribe("+/up-limit/set")
100
- subscribe("+/down-limit/set")
101
- subscribe("+/groups/set")
102
- (1..16).each do |ip|
103
- subscribe("+/ip#{ip}-pulses/set")
104
- subscribe("+/ip#{ip}-percent/set")
71
+ def clear_tree(topic)
72
+ @mqtt.subscribe("#{topic}/#")
73
+ @mqtt.unsubscribe("#{topic}/#", wait_for_ack: true)
74
+ while !@mqtt.queue_empty?
75
+ topic, value = @mqtt.get
76
+ @mqtt.publish(topic, nil, retain: true)
105
77
  end
78
+ end
106
79
 
107
- publish("$state", "ready")
80
+ def publish_basic_attributes
81
+ @mqtt.batch_publish do
82
+ publish("$homie", "v4.0.0")
83
+ publish("$name", "Somfy SDN Network")
84
+ publish("$state", "init")
85
+ publish("$nodes", "FFFFFF")
86
+
87
+ publish("FFFFFF/$name", "Broadcast")
88
+ publish("FFFFFF/$type", "sdn")
89
+ publish("FFFFFF/$properties", "discover")
90
+
91
+ publish("FFFFFF/discover/$name", "Trigger Motor Discovery")
92
+ publish("FFFFFF/discover/$datatype", "enum")
93
+ publish("FFFFFF/discover/$format", "discover")
94
+ publish("FFFFFF/discover/$settable", "true")
95
+ publish("FFFFFF/discover/$retained", "false")
96
+
97
+ subscribe("+/discover/set")
98
+ subscribe("+/label/set")
99
+ subscribe("+/control/set")
100
+ subscribe("+/jog-ms/set")
101
+ subscribe("+/jog-pulses/set")
102
+ subscribe("+/position-pulses/set")
103
+ subscribe("+/position-percent/set")
104
+ subscribe("+/ip/set")
105
+ subscribe("+/reset/set")
106
+ subscribe("+/direction/set")
107
+ subscribe("+/up-speed/set")
108
+ subscribe("+/down-speed/set")
109
+ subscribe("+/slow-speed/set")
110
+ subscribe("+/up-limit/set")
111
+ subscribe("+/down-limit/set")
112
+ subscribe("+/groups/set")
113
+ (1..16).each do |ip|
114
+ subscribe("+/ip#{ip}-pulses/set")
115
+ subscribe("+/ip#{ip}-percent/set")
116
+ end
117
+
118
+ publish("$state", "ready")
119
+ end
108
120
  end
109
121
 
110
122
  def publish_motor(addr, node_type)
111
- publish("#{addr}/$name", addr)
112
- publish("#{addr}/$type", node_type.to_s)
113
- properties = %w{
114
- discover
115
- label
116
- state
117
- control
118
- jog-ms
119
- jog-pulses
120
- position-pulses
121
- position-percent
122
- ip
123
- down-limit
124
- groups
125
- last-direction
126
- } + (1..16).map { |ip| ["ip#{ip}-pulses", "ip#{ip}-percent"] }.flatten
127
-
128
- unless node_type == :st50ilt2
129
- properties.concat %w{
130
- reset
131
- last-action-source
132
- last-action-cause
133
- up-limit
134
- direction
135
- up-speed
136
- down-speed
137
- slow-speed
138
- }
139
- end
123
+ motor = nil
124
+
125
+ @mqtt.batch_publish do
126
+ publish("#{addr}/$name", addr)
127
+ publish("#{addr}/$type", node_type.to_s)
128
+ properties = %w{
129
+ discover
130
+ label
131
+ state
132
+ control
133
+ jog-ms
134
+ jog-pulses
135
+ position-pulses
136
+ position-percent
137
+ ip
138
+ down-limit
139
+ groups
140
+ last-direction
141
+ } + (1..16).map { |ip| ["ip#{ip}-pulses", "ip#{ip}-percent"] }.flatten
142
+
143
+ unless node_type == :st50ilt2
144
+ properties.concat %w{
145
+ reset
146
+ last-action-source
147
+ last-action-cause
148
+ up-limit
149
+ direction
150
+ up-speed
151
+ down-speed
152
+ slow-speed
153
+ }
154
+ end
140
155
 
141
- publish("#{addr}/$properties", properties.join(","))
142
-
143
- publish("#{addr}/discover/$name", "Trigger Motor Discovery")
144
- publish("#{addr}/discover/$datatype", "enum")
145
- publish("#{addr}/discover/$format", "discover")
146
- publish("#{addr}/discover/$settable", "true")
147
- publish("#{addr}/discover/$retained", "false")
148
-
149
- publish("#{addr}/label/$name", "Node label")
150
- publish("#{addr}/label/$datatype", "string")
151
- publish("#{addr}/label/$settable", "true")
152
-
153
- publish("#{addr}/state/$name", "Current state of the motor")
154
- publish("#{addr}/state/$datatype", "enum")
155
- publish("#{addr}/state/$format", Message::PostMotorStatus::STATE.keys.join(','))
156
-
157
- publish("#{addr}/control/$name", "Control motor")
158
- publish("#{addr}/control/$datatype", "enum")
159
- publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
160
- publish("#{addr}/control/$settable", "true")
161
- publish("#{addr}/control/$retained", "false")
162
-
163
- publish("#{addr}/jog-ms/$name", "Jog motor by ms")
164
- publish("#{addr}/jog-ms/$datatype", "integer")
165
- publish("#{addr}/jog-ms/$format", "-65535:65535")
166
- publish("#{addr}/jog-ms/$unit", "ms")
167
- publish("#{addr}/jog-ms/$settable", "true")
168
- publish("#{addr}/jog-ms/$retained", "false")
169
-
170
- publish("#{addr}/jog-pulses/$name", "Jog motor by pulses")
171
- publish("#{addr}/jog-pulses/$datatype", "integer")
172
- publish("#{addr}/jog-pulses/$format", "-65535:65535")
173
- publish("#{addr}/jog-pulses/$unit", "pulses")
174
- publish("#{addr}/jog-pulses/$settable", "true")
175
- publish("#{addr}/jog-pulses/$retained", "false")
176
-
177
- publish("#{addr}/position-percent/$name", "Position (in %)")
178
- publish("#{addr}/position-percent/$datatype", "integer")
179
- publish("#{addr}/position-percent/$format", "0:100")
180
- publish("#{addr}/position-percent/$unit", "%")
181
- publish("#{addr}/position-percent/$settable", "true")
182
-
183
- publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
184
- publish("#{addr}/position-pulses/$datatype", "integer")
185
- publish("#{addr}/position-pulses/$format", "0:65535")
186
- publish("#{addr}/position-pulses/$unit", "pulses")
187
- publish("#{addr}/position-pulses/$settable", "true")
188
-
189
- publish("#{addr}/ip/$name", "Intermediate Position")
190
- publish("#{addr}/ip/$datatype", "integer")
191
- publish("#{addr}/ip/$format", "1:16")
192
- publish("#{addr}/ip/$settable", "true")
193
- publish("#{addr}/ip/$retained", "false") if node_type == :st50ilt2
194
-
195
- publish("#{addr}/down-limit/$name", "Down limit")
196
- publish("#{addr}/down-limit/$datatype", "integer")
197
- publish("#{addr}/down-limit/$format", "0:65535")
198
- publish("#{addr}/down-limit/$unit", "pulses")
199
- publish("#{addr}/down-limit/$settable", "true")
200
-
201
- publish("#{addr}/last-direction/$name", "Direction of last motion")
202
- publish("#{addr}/last-direction/$datatype", "enum")
203
- publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(','))
204
-
205
- unless node_type == :st50ilt2
206
- publish("#{addr}/reset/$name", "Recall factory settings")
207
- publish("#{addr}/reset/$datatype", "enum")
208
- publish("#{addr}/reset/$format", Message::SetFactoryDefault::RESET.keys.join(','))
209
- publish("#{addr}/reset/$settable", "true")
210
- publish("#{addr}/reset/$retained", "false")
211
-
212
- publish("#{addr}/last-action-source/$name", "Source of last action")
213
- publish("#{addr}/last-action-source/$datatype", "enum")
214
- publish("#{addr}/last-action-source/$format", Message::PostMotorStatus::SOURCE.keys.join(','))
215
-
216
- publish("#{addr}/last-action-cause/$name", "Cause of last action")
217
- publish("#{addr}/last-action-cause/$datatype", "enum")
218
- publish("#{addr}/last-action-cause/$format", Message::PostMotorStatus::CAUSE.keys.join(','))
219
-
220
- publish("#{addr}/up-limit/$name", "Up limit (always = 0)")
221
- publish("#{addr}/up-limit/$datatype", "integer")
222
- publish("#{addr}/up-limit/$format", "0:65535")
223
- publish("#{addr}/up-limit/$unit", "pulses")
224
- publish("#{addr}/up-limit/$settable", "true")
225
-
226
- publish("#{addr}/direction/$name", "Motor rotation direction")
227
- publish("#{addr}/direction/$datatype", "enum")
228
- publish("#{addr}/direction/$format", "standard,reversed")
229
- publish("#{addr}/direction/$settable", "true")
230
-
231
- publish("#{addr}/up-speed/$name", "Up speed")
232
- publish("#{addr}/up-speed/$datatype", "integer")
233
- publish("#{addr}/up-speed/$format", "6:28")
234
- publish("#{addr}/up-speed/$unit", "RPM")
235
- publish("#{addr}/up-speed/$settable", "true")
236
-
237
- publish("#{addr}/down-speed/$name", "Down speed, always = Up speed")
238
- publish("#{addr}/down-speed/$datatype", "integer")
239
- publish("#{addr}/down-speed/$format", "6:28")
240
- publish("#{addr}/down-speed/$unit", "RPM")
241
- publish("#{addr}/down-speed/$settable", "true")
242
-
243
- publish("#{addr}/slow-speed/$name", "Slow speed")
244
- publish("#{addr}/slow-speed/$datatype", "integer")
245
- publish("#{addr}/slow-speed/$format", "6:28")
246
- publish("#{addr}/slow-speed/$unit", "RPM")
247
- publish("#{addr}/slow-speed/$settable", "true")
248
- end
156
+ publish("#{addr}/$properties", properties.join(","))
157
+
158
+ publish("#{addr}/discover/$name", "Trigger Motor Discovery")
159
+ publish("#{addr}/discover/$datatype", "enum")
160
+ publish("#{addr}/discover/$format", "discover")
161
+ publish("#{addr}/discover/$settable", "true")
162
+ publish("#{addr}/discover/$retained", "false")
163
+
164
+ publish("#{addr}/label/$name", "Node label")
165
+ publish("#{addr}/label/$datatype", "string")
166
+ publish("#{addr}/label/$settable", "true")
167
+
168
+ publish("#{addr}/state/$name", "Current state of the motor")
169
+ publish("#{addr}/state/$datatype", "enum")
170
+ publish("#{addr}/state/$format", Message::PostMotorStatus::STATE.keys.join(','))
171
+
172
+ publish("#{addr}/control/$name", "Control motor")
173
+ publish("#{addr}/control/$datatype", "enum")
174
+ publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
175
+ publish("#{addr}/control/$settable", "true")
176
+ publish("#{addr}/control/$retained", "false")
177
+
178
+ publish("#{addr}/jog-ms/$name", "Jog motor by ms")
179
+ publish("#{addr}/jog-ms/$datatype", "integer")
180
+ publish("#{addr}/jog-ms/$format", "-65535:65535")
181
+ publish("#{addr}/jog-ms/$unit", "ms")
182
+ publish("#{addr}/jog-ms/$settable", "true")
183
+ publish("#{addr}/jog-ms/$retained", "false")
184
+
185
+ publish("#{addr}/jog-pulses/$name", "Jog motor by pulses")
186
+ publish("#{addr}/jog-pulses/$datatype", "integer")
187
+ publish("#{addr}/jog-pulses/$format", "-65535:65535")
188
+ publish("#{addr}/jog-pulses/$unit", "pulses")
189
+ publish("#{addr}/jog-pulses/$settable", "true")
190
+ publish("#{addr}/jog-pulses/$retained", "false")
191
+
192
+ publish("#{addr}/position-percent/$name", "Position (in %)")
193
+ publish("#{addr}/position-percent/$datatype", "integer")
194
+ publish("#{addr}/position-percent/$format", "0:100")
195
+ publish("#{addr}/position-percent/$unit", "%")
196
+ publish("#{addr}/position-percent/$settable", "true")
197
+
198
+ publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
199
+ publish("#{addr}/position-pulses/$datatype", "integer")
200
+ publish("#{addr}/position-pulses/$format", "0:65535")
201
+ publish("#{addr}/position-pulses/$unit", "pulses")
202
+ publish("#{addr}/position-pulses/$settable", "true")
203
+
204
+ publish("#{addr}/ip/$name", "Intermediate Position")
205
+ publish("#{addr}/ip/$datatype", "integer")
206
+ publish("#{addr}/ip/$format", "1:16")
207
+ publish("#{addr}/ip/$settable", "true")
208
+ publish("#{addr}/ip/$retained", "false") if node_type == :st50ilt2
209
+
210
+ publish("#{addr}/down-limit/$name", "Down limit")
211
+ publish("#{addr}/down-limit/$datatype", "integer")
212
+ publish("#{addr}/down-limit/$format", "0:65535")
213
+ publish("#{addr}/down-limit/$unit", "pulses")
214
+ publish("#{addr}/down-limit/$settable", "true")
215
+
216
+ publish("#{addr}/last-direction/$name", "Direction of last motion")
217
+ publish("#{addr}/last-direction/$datatype", "enum")
218
+ publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(','))
219
+
220
+ unless node_type == :st50ilt2
221
+ publish("#{addr}/reset/$name", "Recall factory settings")
222
+ publish("#{addr}/reset/$datatype", "enum")
223
+ publish("#{addr}/reset/$format", Message::SetFactoryDefault::RESET.keys.join(','))
224
+ publish("#{addr}/reset/$settable", "true")
225
+ publish("#{addr}/reset/$retained", "false")
226
+
227
+ publish("#{addr}/last-action-source/$name", "Source of last action")
228
+ publish("#{addr}/last-action-source/$datatype", "enum")
229
+ publish("#{addr}/last-action-source/$format", Message::PostMotorStatus::SOURCE.keys.join(','))
230
+
231
+ publish("#{addr}/last-action-cause/$name", "Cause of last action")
232
+ publish("#{addr}/last-action-cause/$datatype", "enum")
233
+ publish("#{addr}/last-action-cause/$format", Message::PostMotorStatus::CAUSE.keys.join(','))
234
+
235
+ publish("#{addr}/up-limit/$name", "Up limit (always = 0)")
236
+ publish("#{addr}/up-limit/$datatype", "integer")
237
+ publish("#{addr}/up-limit/$format", "0:65535")
238
+ publish("#{addr}/up-limit/$unit", "pulses")
239
+ publish("#{addr}/up-limit/$settable", "true")
240
+
241
+ publish("#{addr}/direction/$name", "Motor rotation direction")
242
+ publish("#{addr}/direction/$datatype", "enum")
243
+ publish("#{addr}/direction/$format", "standard,reversed")
244
+ publish("#{addr}/direction/$settable", "true")
245
+
246
+ publish("#{addr}/up-speed/$name", "Up speed")
247
+ publish("#{addr}/up-speed/$datatype", "integer")
248
+ publish("#{addr}/up-speed/$format", "6:28")
249
+ publish("#{addr}/up-speed/$unit", "RPM")
250
+ publish("#{addr}/up-speed/$settable", "true")
251
+
252
+ publish("#{addr}/down-speed/$name", "Down speed, always = Up speed")
253
+ publish("#{addr}/down-speed/$datatype", "integer")
254
+ publish("#{addr}/down-speed/$format", "6:28")
255
+ publish("#{addr}/down-speed/$unit", "RPM")
256
+ publish("#{addr}/down-speed/$settable", "true")
257
+
258
+ publish("#{addr}/slow-speed/$name", "Slow speed")
259
+ publish("#{addr}/slow-speed/$datatype", "integer")
260
+ publish("#{addr}/slow-speed/$format", "6:28")
261
+ publish("#{addr}/slow-speed/$unit", "RPM")
262
+ publish("#{addr}/slow-speed/$settable", "true")
263
+ end
249
264
 
250
- publish("#{addr}/groups/$name", "Group Memberships (comma separated, address must start 0101xx)")
251
- publish("#{addr}/groups/$datatype", "string")
252
- publish("#{addr}/groups/$settable", "true")
253
-
254
- (1..16).each do |ip|
255
- publish("#{addr}/ip#{ip}-pulses/$name", "Intermediate Position #{ip}")
256
- publish("#{addr}/ip#{ip}-pulses/$datatype", "integer")
257
- publish("#{addr}/ip#{ip}-pulses/$format", "0:65535")
258
- publish("#{addr}/ip#{ip}-pulses/$unit", "pulses")
259
- publish("#{addr}/ip#{ip}-pulses/$settable", "true")
260
-
261
- publish("#{addr}/ip#{ip}-percent/$name", "Intermediate Position #{ip}")
262
- publish("#{addr}/ip#{ip}-percent/$datatype", "integer")
263
- publish("#{addr}/ip#{ip}-percent/$format", "0:100")
264
- publish("#{addr}/ip#{ip}-percent/$unit", "%")
265
- publish("#{addr}/ip#{ip}-percent/$settable", "true")
266
- end
265
+ publish("#{addr}/groups/$name", "Group Memberships (comma separated, address must start 0101xx)")
266
+ publish("#{addr}/groups/$datatype", "string")
267
+ publish("#{addr}/groups/$settable", "true")
268
+
269
+ (1..16).each do |ip|
270
+ publish("#{addr}/ip#{ip}-pulses/$name", "Intermediate Position #{ip}")
271
+ publish("#{addr}/ip#{ip}-pulses/$datatype", "integer")
272
+ publish("#{addr}/ip#{ip}-pulses/$format", "0:65535")
273
+ publish("#{addr}/ip#{ip}-pulses/$unit", "pulses")
274
+ publish("#{addr}/ip#{ip}-pulses/$settable", "true")
275
+
276
+ publish("#{addr}/ip#{ip}-percent/$name", "Intermediate Position #{ip}")
277
+ publish("#{addr}/ip#{ip}-percent/$datatype", "integer")
278
+ publish("#{addr}/ip#{ip}-percent/$format", "0:100")
279
+ publish("#{addr}/ip#{ip}-percent/$unit", "%")
280
+ publish("#{addr}/ip#{ip}-percent/$settable", "true")
281
+ end
267
282
 
268
- motor = Motor.new(self, addr, node_type)
269
- @motors[addr] = motor
270
- publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(","))
283
+ motor = Motor.new(self, addr, node_type)
284
+ @motors[addr] = motor
285
+ publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(","))
286
+ end
271
287
 
272
288
  sdn_addr = Message.parse_address(addr)
273
289
  @mutex.synchronize do
@@ -302,66 +318,68 @@ module SDN
302
318
  group = @groups[addr]
303
319
  return group if group
304
320
 
305
- publish("#{addr}/$name", addr)
306
- publish("#{addr}/$type", "Shade Group")
307
- publish("#{addr}/$properties", "discover,control,jog-ms,jog-pulses,position-pulses,position-percent,ip,reset,state,last-direction,motors")
308
-
309
- publish("#{addr}/discover/$name", "Trigger Motor Discovery")
310
- publish("#{addr}/discover/$datatype", "enum")
311
- publish("#{addr}/discover/$format", "discover")
312
- publish("#{addr}/discover/$settable", "true")
313
- publish("#{addr}/discover/$retained", "false")
314
-
315
- publish("#{addr}/control/$name", "Control motors")
316
- publish("#{addr}/control/$datatype", "enum")
317
- publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
318
- publish("#{addr}/control/$settable", "true")
319
- publish("#{addr}/control/$retained", "false")
320
-
321
- publish("#{addr}/jog-ms/$name", "Jog motors by ms")
322
- publish("#{addr}/jog-ms/$datatype", "integer")
323
- publish("#{addr}/jog-ms/$format", "-65535:65535")
324
- publish("#{addr}/jog-ms/$unit", "ms")
325
- publish("#{addr}/jog-ms/$settable", "true")
326
- publish("#{addr}/jog-ms/$retained", "false")
327
-
328
- publish("#{addr}/jog-pulses/$name", "Jog motors by pulses")
329
- publish("#{addr}/jog-pulses/$datatype", "integer")
330
- publish("#{addr}/jog-pulses/$format", "-65535:65535")
331
- publish("#{addr}/jog-pulses/$unit", "pulses")
332
- publish("#{addr}/jog-pulses/$settable", "true")
333
- publish("#{addr}/jog-pulses/$retained", "false")
334
-
335
- publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
336
- publish("#{addr}/position-pulses/$datatype", "integer")
337
- publish("#{addr}/position-pulses/$format", "0:65535")
338
- publish("#{addr}/position-pulses/$unit", "pulses")
339
- publish("#{addr}/position-pulses/$settable", "true")
340
-
341
- publish("#{addr}/position-percent/$name", "Position (in %)")
342
- publish("#{addr}/position-percent/$datatype", "integer")
343
- publish("#{addr}/position-percent/$format", "0:100")
344
- publish("#{addr}/position-percent/$unit", "%")
345
- publish("#{addr}/position-percent/$settable", "true")
346
-
347
- publish("#{addr}/ip/$name", "Intermediate Position")
348
- publish("#{addr}/ip/$datatype", "integer")
349
- publish("#{addr}/ip/$format", "1:16")
350
- publish("#{addr}/ip/$settable", "true")
351
-
352
- publish("#{addr}/state/$name", "State of the motors")
353
- publish("#{addr}/state/$datatype", "enum")
354
- publish("#{addr}/state/$format", Message::PostMotorStatus::STATE.keys.join(',') + ",mixed")
355
-
356
- publish("#{addr}/last-direction/$name", "Direction of last motion")
357
- publish("#{addr}/last-direction/$datatype", "enum")
358
- publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(',') + ",mixed")
359
-
360
- publish("#{addr}/motors/$name", "Comma separated motor addresses that are members of this group")
361
- publish("#{addr}/motors/$datatype", "string")
362
-
363
- group = @groups[addr] = Group.new(self, addr)
364
- publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(","))
321
+ @mqtt.batch_publish do
322
+ publish("#{addr}/$name", addr)
323
+ publish("#{addr}/$type", "Shade Group")
324
+ publish("#{addr}/$properties", "discover,control,jog-ms,jog-pulses,position-pulses,position-percent,ip,reset,state,last-direction,motors")
325
+
326
+ publish("#{addr}/discover/$name", "Trigger Motor Discovery")
327
+ publish("#{addr}/discover/$datatype", "enum")
328
+ publish("#{addr}/discover/$format", "discover")
329
+ publish("#{addr}/discover/$settable", "true")
330
+ publish("#{addr}/discover/$retained", "false")
331
+
332
+ publish("#{addr}/control/$name", "Control motors")
333
+ publish("#{addr}/control/$datatype", "enum")
334
+ publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
335
+ publish("#{addr}/control/$settable", "true")
336
+ publish("#{addr}/control/$retained", "false")
337
+
338
+ publish("#{addr}/jog-ms/$name", "Jog motors by ms")
339
+ publish("#{addr}/jog-ms/$datatype", "integer")
340
+ publish("#{addr}/jog-ms/$format", "-65535:65535")
341
+ publish("#{addr}/jog-ms/$unit", "ms")
342
+ publish("#{addr}/jog-ms/$settable", "true")
343
+ publish("#{addr}/jog-ms/$retained", "false")
344
+
345
+ publish("#{addr}/jog-pulses/$name", "Jog motors by pulses")
346
+ publish("#{addr}/jog-pulses/$datatype", "integer")
347
+ publish("#{addr}/jog-pulses/$format", "-65535:65535")
348
+ publish("#{addr}/jog-pulses/$unit", "pulses")
349
+ publish("#{addr}/jog-pulses/$settable", "true")
350
+ publish("#{addr}/jog-pulses/$retained", "false")
351
+
352
+ publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
353
+ publish("#{addr}/position-pulses/$datatype", "integer")
354
+ publish("#{addr}/position-pulses/$format", "0:65535")
355
+ publish("#{addr}/position-pulses/$unit", "pulses")
356
+ publish("#{addr}/position-pulses/$settable", "true")
357
+
358
+ publish("#{addr}/position-percent/$name", "Position (in %)")
359
+ publish("#{addr}/position-percent/$datatype", "integer")
360
+ publish("#{addr}/position-percent/$format", "0:100")
361
+ publish("#{addr}/position-percent/$unit", "%")
362
+ publish("#{addr}/position-percent/$settable", "true")
363
+
364
+ publish("#{addr}/ip/$name", "Intermediate Position")
365
+ publish("#{addr}/ip/$datatype", "integer")
366
+ publish("#{addr}/ip/$format", "1:16")
367
+ publish("#{addr}/ip/$settable", "true")
368
+
369
+ publish("#{addr}/state/$name", "State of the motors")
370
+ publish("#{addr}/state/$datatype", "enum")
371
+ publish("#{addr}/state/$format", Message::PostMotorStatus::STATE.keys.join(',') + ",mixed")
372
+
373
+ publish("#{addr}/last-direction/$name", "Direction of last motion")
374
+ publish("#{addr}/last-direction/$datatype", "enum")
375
+ publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(',') + ",mixed")
376
+
377
+ publish("#{addr}/motors/$name", "Comma separated motor addresses that are members of this group")
378
+ publish("#{addr}/motors/$datatype", "string")
379
+
380
+ group = @groups[addr] = Group.new(self, addr)
381
+ publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(","))
382
+ end
365
383
  group
366
384
  end
367
385
  end
@@ -6,144 +6,146 @@ module SDN
6
6
  loop do
7
7
  begin
8
8
  @sdn.receive do |message|
9
- SDN.logger.info "read #{message.inspect}"
9
+ @mqtt.batch_publish do
10
+ SDN.logger.info "read #{message.inspect}"
10
11
 
11
- src = Message.print_address(message.src)
12
- # ignore the UAI Plus and ourselves
13
- if src != '7F.7F.7F' && !Message.is_group_address?(message.src) && !(motor = @motors[src.gsub('.', '')])
14
- SDN.logger.info "found new motor #{src}"
15
- @motors_found = true
16
- motor = publish_motor(src.gsub('.', ''), message.node_type)
17
- end
18
-
19
- follow_ups = []
20
- case message
21
- when Message::PostNodeLabel
22
- if (motor.publish(:label, message.label))
23
- publish("#{motor.addr}/$name", message.label)
12
+ src = Message.print_address(message.src)
13
+ # ignore the UAI Plus and ourselves
14
+ if src != '7F.7F.7F' && !Message.is_group_address?(message.src) && !(motor = @motors[src.gsub('.', '')])
15
+ SDN.logger.info "found new motor #{src}"
16
+ @motors_found = true
17
+ motor = publish_motor(src.gsub('.', ''), message.node_type)
24
18
  end
25
- when Message::PostMotorPosition,
26
- Message::ILT2::PostMotorPosition
27
- if message.is_a?(Message::ILT2::PostMotorPosition)
28
- # keep polling while it's still moving; check prior two positions
29
- if motor.position_pulses == message.position_pulses &&
30
- motor.last_position_pulses == message.position_pulses
31
- motor.publish(:state, :stopped)
32
- else
33
- motor.publish(:state, :running)
34
- if motor.position_pulses && motor.position_pulses != message.position_pulses
35
- motor.publish(:last_direction, motor.position_pulses < message.position_pulses ? :down : :up)
36
- end
37
- follow_ups << Message::ILT2::GetMotorPosition.new(message.src)
19
+
20
+ follow_ups = []
21
+ case message
22
+ when Message::PostNodeLabel
23
+ if (motor.publish(:label, message.label))
24
+ publish("#{motor.addr}/$name", message.label)
38
25
  end
39
- motor.last_position_pulses = motor.position_pulses
40
- ip = (1..16).find do |i|
41
- # divide by 5 for some leniency
42
- motor["ip#{i}_pulses"].to_i / 5 == message.position_pulses / 5
26
+ when Message::PostMotorPosition,
27
+ Message::ILT2::PostMotorPosition
28
+ if message.is_a?(Message::ILT2::PostMotorPosition)
29
+ # keep polling while it's still moving; check prior two positions
30
+ if motor.position_pulses == message.position_pulses &&
31
+ motor.last_position_pulses == message.position_pulses
32
+ motor.publish(:state, :stopped)
33
+ else
34
+ motor.publish(:state, :running)
35
+ if motor.position_pulses && motor.position_pulses != message.position_pulses
36
+ motor.publish(:last_direction, motor.position_pulses < message.position_pulses ? :down : :up)
37
+ end
38
+ follow_ups << Message::ILT2::GetMotorPosition.new(message.src)
39
+ end
40
+ motor.last_position_pulses = motor.position_pulses
41
+ ip = (1..16).find do |i|
42
+ # divide by 5 for some leniency
43
+ motor["ip#{i}_pulses"].to_i / 5 == message.position_pulses / 5
44
+ end
45
+ motor.publish(:ip, ip)
43
46
  end
44
- motor.publish(:ip, ip)
45
- end
46
- motor.publish(:position_percent, message.position_percent)
47
- motor.publish(:position_pulses, message.position_pulses)
48
- motor.publish(:ip, message.ip) if message.respond_to?(:ip)
49
- motor.group_objects.each do |group|
50
- positions_percent = group.motor_objects.map(&:position_percent)
51
- positions_pulses = group.motor_objects.map(&:position_pulses)
52
- ips = group.motor_objects.map(&:ip)
47
+ motor.publish(:position_percent, message.position_percent)
48
+ motor.publish(:position_pulses, message.position_pulses)
49
+ motor.publish(:ip, message.ip) if message.respond_to?(:ip)
50
+ motor.group_objects.each do |group|
51
+ positions_percent = group.motor_objects.map(&:position_percent)
52
+ positions_pulses = group.motor_objects.map(&:position_pulses)
53
+ ips = group.motor_objects.map(&:ip)
53
54
 
54
- position_percent = nil
55
- # calculate an average, but only if we know a position for
56
- # every shade
57
- if !positions_percent.include?(:nil) && !positions_percent.include?(nil)
58
- position_percent = positions_percent.inject(&:+) / positions_percent.length
59
- end
55
+ position_percent = nil
56
+ # calculate an average, but only if we know a position for
57
+ # every shade
58
+ if !positions_percent.include?(:nil) && !positions_percent.include?(nil)
59
+ position_percent = positions_percent.inject(&:+) / positions_percent.length
60
+ end
60
61
 
61
- position_pulses = nil
62
- if !positions_pulses.include?(:nil) && !positions_pulses.include?(nil)
63
- position_pulses = positions_pulses.inject(&:+) / positions_pulses.length
64
- end
62
+ position_pulses = nil
63
+ if !positions_pulses.include?(:nil) && !positions_pulses.include?(nil)
64
+ position_pulses = positions_pulses.inject(&:+) / positions_pulses.length
65
+ end
65
66
 
66
- ip = nil
67
- ip = ips.first if ips.uniq.length == 1
68
- ip = nil if ip == :nil
67
+ ip = nil
68
+ ip = ips.first if ips.uniq.length == 1
69
+ ip = nil if ip == :nil
69
70
 
70
- group.publish(:position_percent, position_percent)
71
- group.publish(:position_pulses, position_pulses)
72
- group.publish(:ip, ip)
73
- end
74
- when Message::PostMotorStatus
75
- if message.state == :running || motor.state == :running ||
76
- # if it's explicitly stopped, but we didn't ask it to, it's probably
77
- # changing directions so keep querying
78
- (message.state == :stopped &&
79
- message.last_action_cause == :explicit_command &&
80
- !(motor.last_action == Message::Stop || motor.last_action.nil?))
81
- follow_ups << Message::GetMotorStatus.new(message.src)
82
- end
83
- # this will do one more position request after it stopped
84
- follow_ups << Message::GetMotorPosition.new(message.src)
85
- motor.publish(:state, message.state)
86
- motor.publish(:last_direction, message.last_direction)
87
- motor.publish(:last_action_source, message.last_action_source)
88
- motor.publish(:last_action_cause, message.last_action_cause)
89
- motor.group_objects.each do |group|
90
- states = group.motor_objects.map(&:state).uniq
91
- state = states.length == 1 ? states.first : 'mixed'
92
- group.publish(:state, state)
71
+ group.publish(:position_percent, position_percent)
72
+ group.publish(:position_pulses, position_pulses)
73
+ group.publish(:ip, ip)
74
+ end
75
+ when Message::PostMotorStatus
76
+ if message.state == :running || motor.state == :running ||
77
+ # if it's explicitly stopped, but we didn't ask it to, it's probably
78
+ # changing directions so keep querying
79
+ (message.state == :stopped &&
80
+ message.last_action_cause == :explicit_command &&
81
+ !(motor.last_action == Message::Stop || motor.last_action.nil?))
82
+ follow_ups << Message::GetMotorStatus.new(message.src)
83
+ end
84
+ # this will do one more position request after it stopped
85
+ follow_ups << Message::GetMotorPosition.new(message.src)
86
+ motor.publish(:state, message.state)
87
+ motor.publish(:last_direction, message.last_direction)
88
+ motor.publish(:last_action_source, message.last_action_source)
89
+ motor.publish(:last_action_cause, message.last_action_cause)
90
+ motor.group_objects.each do |group|
91
+ states = group.motor_objects.map(&:state).uniq
92
+ state = states.length == 1 ? states.first : 'mixed'
93
+ group.publish(:state, state)
93
94
 
94
- directions = group.motor_objects.map(&:last_direction).uniq
95
- direction = directions.length == 1 ? directions.first : 'mixed'
96
- group.publish(:last_direction, direction)
97
- end
98
- when Message::PostMotorLimits
99
- motor.publish(:up_limit, message.up_limit)
100
- motor.publish(:down_limit, message.down_limit)
101
- when Message::ILT2::PostMotorSettings
102
- motor.publish(:down_limit, message.limit)
103
- when Message::PostMotorDirection
104
- motor.publish(:direction, message.direction)
105
- when Message::PostMotorRollingSpeed
106
- motor.publish(:up_speed, message.up_speed)
107
- motor.publish(:down_speed, message.down_speed)
108
- motor.publish(:slow_speed, message.slow_speed)
109
- when Message::PostMotorIP,
110
- Message::ILT2::PostMotorIP
111
- motor.publish(:"ip#{message.ip}_pulses", message.position_pulses)
112
- if message.respond_to?(:position_percent)
113
- motor.publish(:"ip#{message.ip}_percent", message.position_percent)
114
- elsif motor.down_limit
115
- motor.publish(:"ip#{message.ip}_percent", message.position_pulses.to_f / motor.down_limit * 100)
95
+ directions = group.motor_objects.map(&:last_direction).uniq
96
+ direction = directions.length == 1 ? directions.first : 'mixed'
97
+ group.publish(:last_direction, direction)
98
+ end
99
+ when Message::PostMotorLimits
100
+ motor.publish(:up_limit, message.up_limit)
101
+ motor.publish(:down_limit, message.down_limit)
102
+ when Message::ILT2::PostMotorSettings
103
+ motor.publish(:down_limit, message.limit)
104
+ when Message::PostMotorDirection
105
+ motor.publish(:direction, message.direction)
106
+ when Message::PostMotorRollingSpeed
107
+ motor.publish(:up_speed, message.up_speed)
108
+ motor.publish(:down_speed, message.down_speed)
109
+ motor.publish(:slow_speed, message.slow_speed)
110
+ when Message::PostMotorIP,
111
+ Message::ILT2::PostMotorIP
112
+ motor.publish(:"ip#{message.ip}_pulses", message.position_pulses)
113
+ if message.respond_to?(:position_percent)
114
+ motor.publish(:"ip#{message.ip}_percent", message.position_percent)
115
+ elsif motor.down_limit
116
+ motor.publish(:"ip#{message.ip}_percent", message.position_pulses.to_f / motor.down_limit * 100)
117
+ end
118
+ when Message::PostGroupAddr
119
+ motor.add_group(message.group_index, message.group_address)
116
120
  end
117
- when Message::PostGroupAddr
118
- motor.add_group(message.group_index, message.group_address)
119
- end
120
121
 
121
- @mutex.synchronize do
122
- prior_message_to_group = Message.is_group_address?(@prior_message&.message&.src) if @prior_message
122
+ @mutex.synchronize do
123
+ prior_message_to_group = Message.is_group_address?(@prior_message&.message&.src) if @prior_message
123
124
 
124
- correct_response = @response_pending && @prior_message&.message&.class&.expected_response?(message)
125
- correct_response = false if !prior_message_to_group && message.src != @prior_message&.message&.dest
126
- correct_response = false if prior_message_to_group && message.dest != @prior_message&.message&.src
125
+ correct_response = @response_pending && @prior_message&.message&.class&.expected_response?(message)
126
+ correct_response = false if !prior_message_to_group && message.src != @prior_message&.message&.dest
127
+ correct_response = false if prior_message_to_group && message.dest != @prior_message&.message&.src
127
128
 
128
- if prior_message_to_group && correct_response
129
- @pending_group_motors.delete(Message.print_address(message.src).gsub('.', ''))
130
- correct_response = false unless @pending_group_motors.empty?
131
- end
129
+ if prior_message_to_group && correct_response
130
+ @pending_group_motors.delete(Message.print_address(message.src).gsub('.', ''))
131
+ correct_response = false unless @pending_group_motors.empty?
132
+ end
132
133
 
133
- signal = correct_response || !follow_ups.empty?
134
- @response_pending = @broadcast_pending if correct_response
135
- follow_ups.each do |follow_up|
136
- @queues[1].push(MessageAndRetries.new(follow_up, 5, 1)) unless @queues[1].any? { |mr| mr.message == follow_up }
134
+ signal = correct_response || !follow_ups.empty?
135
+ @response_pending = @broadcast_pending if correct_response
136
+ follow_ups.each do |follow_up|
137
+ @queues[1].push(MessageAndRetries.new(follow_up, 5, 1)) unless @queues[1].any? { |mr| mr.message == follow_up }
138
+ end
139
+ @cond.signal if signal
137
140
  end
138
- @cond.signal if signal
141
+ rescue EOFError
142
+ SDN.logger.fatal "EOF reading"
143
+ exit 2
144
+ rescue MalformedMessage => e
145
+ SDN.logger.warn "ignoring malformed message: #{e}" unless e.to_s =~ /issing data/
146
+ rescue => e
147
+ SDN.logger.error "got garbage: #{e}; #{e.backtrace}"
139
148
  end
140
- rescue EOFError
141
- SDN.logger.fatal "EOF reading"
142
- exit 2
143
- rescue MalformedMessage => e
144
- SDN.logger.warn "ignoring malformed message: #{e}" unless e.to_s =~ /issing data/
145
- rescue => e
146
- SDN.logger.error "got garbage: #{e}; #{e.backtrace}"
147
149
  end
148
150
  end
149
151
  end
data/lib/sdn/client.rb CHANGED
@@ -9,10 +9,12 @@ module SDN
9
9
  TCPSocket.new(uri.host, uri.port)
10
10
  elsif uri.scheme == "telnet" || uri.scheme == "rfc2217"
11
11
  require 'net/telnet/rfc2217'
12
- Net::Telnet::RFC2217.new('Host' => uri.host,
13
- 'Port' => uri.port || 23,
14
- 'baud' => 4800,
15
- 'parity' => Net::Telnet::RFC2217::ODD)
12
+ Net::Telnet::RFC2217.new(host: uri.host,
13
+ port: uri.port || 23,
14
+ baud: 4800,
15
+ data_bits: 8,
16
+ parity: :odd,
17
+ stop_bits: 1)
16
18
  elsif port == "/dev/ptmx"
17
19
  require 'pty'
18
20
  io, slave = PTY.open
data/lib/sdn/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module SDN
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: somfy_sdn
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-13 00:00:00.000000000 Z
11
+ date: 2021-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ccutrer-serialport
14
+ name: ccutrer-mqtt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
@@ -25,47 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: curses
28
+ name: ccutrer-serialport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.4'
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.4'
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: mqtt
42
+ name: curses
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.5.0
47
+ version: '1.4'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.5.0
54
+ version: '1.4'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: net-telnet-rfc2217
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.0.3
61
+ version: '1.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.0.3
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: thor
71
71
  requirement: !ruby/object:Gem::Requirement