somfy_sdn 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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