somfy_sdn 2.4.0 → 2.5.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 +4 -4
- data/exe/somfy_sdn +1 -0
- data/lib/sdn/cli/mqtt/group.rb +11 -0
- data/lib/sdn/cli/mqtt/motor.rb +25 -1
- data/lib/sdn/cli/mqtt/p_queue.rb +6 -0
- data/lib/sdn/cli/mqtt/read.rb +46 -47
- data/lib/sdn/cli/mqtt/subscriptions.rb +8 -0
- data/lib/sdn/cli/mqtt/write.rb +67 -27
- data/lib/sdn/cli/mqtt.rb +296 -211
- data/lib/sdn/cli/provisioner.rb +34 -1
- data/lib/sdn/cli/simulator.rb +78 -11
- data/lib/sdn/cli.rb +6 -0
- data/lib/sdn/client.rb +42 -2
- data/lib/sdn/message/control.rb +136 -12
- data/lib/sdn/message/get.rb +57 -0
- data/lib/sdn/message/helpers.rb +64 -0
- data/lib/sdn/message/ilt2/get.rb +24 -0
- data/lib/sdn/message/ilt2/master_control.rb +10 -0
- data/lib/sdn/message/ilt2/post.rb +79 -13
- data/lib/sdn/message/ilt2/set.rb +112 -7
- data/lib/sdn/message/post.rb +181 -20
- data/lib/sdn/message/set.rb +166 -8
- data/lib/sdn/message.rb +121 -14
- data/lib/sdn/version.rb +2 -1
- data/lib/sdn.rb +13 -5
- metadata +5 -18
data/lib/sdn/cli/mqtt.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "mqtt"
|
|
4
4
|
require "mqtt-homeassistant"
|
|
5
5
|
require "uri"
|
|
6
|
-
require "set"
|
|
7
6
|
|
|
8
7
|
require "sdn/cli/mqtt/group"
|
|
9
8
|
require "sdn/cli/mqtt/motor"
|
|
@@ -15,18 +14,40 @@ require "sdn/version"
|
|
|
15
14
|
|
|
16
15
|
module SDN
|
|
17
16
|
module CLI
|
|
17
|
+
# @!visibility private
|
|
18
18
|
class MQTT
|
|
19
|
+
# Queue entry wrapper containing an SDN message, retry budget, and scheduling priority.
|
|
19
20
|
MessageAndRetries = Struct.new(:message, :remaining_retries, :priority)
|
|
20
21
|
|
|
21
22
|
include Read
|
|
22
23
|
include Write
|
|
23
24
|
include Subscriptions
|
|
24
25
|
|
|
26
|
+
# Time to wait, in seconds, for a direct response before retrying.
|
|
25
27
|
WAIT_TIME = 0.25
|
|
28
|
+
# Time to wait, in seconds, for broadcast or group discovery responses.
|
|
26
29
|
BROADCAST_WAIT = 5.0
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
# Discovered motors keyed by normalized address.
|
|
32
|
+
#
|
|
33
|
+
# @return [{String => SDN::CLI::MQTT::Motor}]
|
|
34
|
+
attr_reader :motors
|
|
35
|
+
|
|
36
|
+
# Discovered groups keyed by normalized address.
|
|
37
|
+
#
|
|
38
|
+
# @return [{String => SDN::CLI::MQTT::Group}]
|
|
39
|
+
attr_reader :groups
|
|
40
|
+
|
|
41
|
+
# Creates the MQTT bridge, publishes discovery metadata, and starts read/write loops.
|
|
42
|
+
#
|
|
43
|
+
# @param sdn [SDN::Client]
|
|
44
|
+
# @param mqtt_uri [String]
|
|
45
|
+
# @param device_id [String] Homie bridge device identifier
|
|
46
|
+
# @param base_topic [String] Homie base MQTT topic
|
|
47
|
+
# @param auto_discover [true, false] whether to periodically discover motors
|
|
48
|
+
# @param known_motors [<String>] motor addresses to seed discovery
|
|
49
|
+
# @param homie [true, false] whether to publish Homie metadata
|
|
50
|
+
# @param home_assistant [true, false] whether to publish Home Assistant discovery data
|
|
30
51
|
def initialize(sdn,
|
|
31
52
|
mqtt_uri,
|
|
32
53
|
device_id: "somfy",
|
|
@@ -43,6 +64,12 @@ module SDN
|
|
|
43
64
|
@mqtt.set_will("#{@base_topic}/$state", "lost", retain: true)
|
|
44
65
|
@mqtt.connect
|
|
45
66
|
|
|
67
|
+
@availability = [{
|
|
68
|
+
topic: "#{@base_topic}/$state",
|
|
69
|
+
payload_available: "ready",
|
|
70
|
+
payload_not_available: "lost"
|
|
71
|
+
}.freeze].freeze
|
|
72
|
+
|
|
46
73
|
@motors = {}
|
|
47
74
|
@groups = {}
|
|
48
75
|
|
|
@@ -75,10 +102,214 @@ module SDN
|
|
|
75
102
|
@mqtt.publish("#{@base_topic}/#{topic}", value, retain: true, qos: 1)
|
|
76
103
|
end
|
|
77
104
|
|
|
105
|
+
# Refreshes a group's published motor membership when one of its members changes.
|
|
106
|
+
#
|
|
107
|
+
# @param group_addr [(Integer, Integer, Integer))] group address bytes
|
|
108
|
+
#
|
|
109
|
+
# @return [void]
|
|
110
|
+
def touch_group(group_addr)
|
|
111
|
+
group = @groups[Message.print_address(group_addr).delete(".")]
|
|
112
|
+
group&.publish(:motors, group.motors_string)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Publishes MQTT metadata for a group, creating it if necessary.
|
|
116
|
+
#
|
|
117
|
+
# @param addr [String] printable group address
|
|
118
|
+
#
|
|
119
|
+
# @return [SDN::CLI::MQTT::Group]
|
|
120
|
+
def add_group(addr)
|
|
121
|
+
addr = addr.delete(".")
|
|
122
|
+
group = @groups[addr]
|
|
123
|
+
return group if group
|
|
124
|
+
|
|
125
|
+
@mqtt.batch_publish do
|
|
126
|
+
hass_device = {
|
|
127
|
+
identifiers: addr,
|
|
128
|
+
model: "Shade Group",
|
|
129
|
+
name: addr,
|
|
130
|
+
via_device: @device_id
|
|
131
|
+
}.freeze
|
|
132
|
+
node_id = "#{@device_id}_#{addr}"
|
|
133
|
+
|
|
134
|
+
if @homie
|
|
135
|
+
publish("#{addr}/$name", addr)
|
|
136
|
+
publish("#{addr}/$type", "Shade Group")
|
|
137
|
+
publish("#{addr}/$properties",
|
|
138
|
+
"discover,control,jog-ms,jog-pulses,position-pulses,position-percent," \
|
|
139
|
+
"ip,reset,state,last-direction,motors")
|
|
140
|
+
|
|
141
|
+
publish("#{addr}/discover/$name", "Trigger Motor Discovery")
|
|
142
|
+
publish("#{addr}/discover/$datatype", "enum")
|
|
143
|
+
publish("#{addr}/discover/$format", "discover")
|
|
144
|
+
publish("#{addr}/discover/$settable", "true")
|
|
145
|
+
publish("#{addr}/discover/$retained", "false")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if @home_assistant
|
|
149
|
+
@mqtt.publish_hass_button("discover",
|
|
150
|
+
command_topic: "#{@base_topic}/#{addr}/discover/set",
|
|
151
|
+
device: hass_device,
|
|
152
|
+
icon: "mdi:search-add",
|
|
153
|
+
name: "Rediscover",
|
|
154
|
+
node_id:,
|
|
155
|
+
object_id: "discover",
|
|
156
|
+
payload_press: "discover",
|
|
157
|
+
unique_id: "#{node_id}_discover",
|
|
158
|
+
availability: @availability)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if @homie
|
|
162
|
+
publish("#{addr}/control/$name", "Control motors")
|
|
163
|
+
publish("#{addr}/control/$datatype", "enum")
|
|
164
|
+
publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
|
|
165
|
+
publish("#{addr}/control/$settable", "true")
|
|
166
|
+
publish("#{addr}/control/$retained", "false")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
if @home_assistant
|
|
170
|
+
@mqtt.publish_hass_cover("group",
|
|
171
|
+
command_topic: "#{@base_topic}/#{addr}/control/set",
|
|
172
|
+
device: hass_device,
|
|
173
|
+
icon: "mdi:roller-shade",
|
|
174
|
+
name: "Group",
|
|
175
|
+
node_id:,
|
|
176
|
+
payload_close: "down",
|
|
177
|
+
payload_open: "up",
|
|
178
|
+
payload_stop: "stop",
|
|
179
|
+
position_open: 0,
|
|
180
|
+
position_closed: 100,
|
|
181
|
+
position_topic: "#{@base_topic}/#{addr}/position-percent",
|
|
182
|
+
set_position_topic: "#{@base_topic}/#{addr}/position-percent/set",
|
|
183
|
+
state_topic: "#{@base_topic}/#{addr}/hass-state",
|
|
184
|
+
unique_id: "#{node_id}_group",
|
|
185
|
+
availability: @availability)
|
|
186
|
+
{
|
|
187
|
+
Wink: "mdi:emoticon-wink",
|
|
188
|
+
Next_IP: "mdi:skip-next",
|
|
189
|
+
Previous_IP: "mdi:skip-previous",
|
|
190
|
+
Refresh: "mdi:refresh"
|
|
191
|
+
}.each do |command, icon|
|
|
192
|
+
@mqtt.publish_hass_button(command.to_s.downcase,
|
|
193
|
+
command_topic: "#{@base_topic}/#{addr}/control/set",
|
|
194
|
+
device: hass_device,
|
|
195
|
+
icon:,
|
|
196
|
+
name: command.to_s.sub("_", " "),
|
|
197
|
+
node_id:,
|
|
198
|
+
payload_press: command.to_s.downcase,
|
|
199
|
+
unique_id: "#{node_id}_#{command.to_s.downcase}",
|
|
200
|
+
availability: @availability)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
if @homie
|
|
205
|
+
publish("#{addr}/jog-ms/$name", "Jog motors by ms")
|
|
206
|
+
publish("#{addr}/jog-ms/$datatype", "integer")
|
|
207
|
+
publish("#{addr}/jog-ms/$format", "-65535:65535")
|
|
208
|
+
publish("#{addr}/jog-ms/$unit", "ms")
|
|
209
|
+
publish("#{addr}/jog-ms/$settable", "true")
|
|
210
|
+
publish("#{addr}/jog-ms/$retained", "false")
|
|
211
|
+
|
|
212
|
+
publish("#{addr}/jog-pulses/$name", "Jog motors by pulses")
|
|
213
|
+
publish("#{addr}/jog-pulses/$datatype", "integer")
|
|
214
|
+
publish("#{addr}/jog-pulses/$format", "-65535:65535")
|
|
215
|
+
publish("#{addr}/jog-pulses/$unit", "pulses")
|
|
216
|
+
publish("#{addr}/jog-pulses/$settable", "true")
|
|
217
|
+
publish("#{addr}/jog-pulses/$retained", "false")
|
|
218
|
+
|
|
219
|
+
publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
|
|
220
|
+
publish("#{addr}/position-pulses/$datatype", "integer")
|
|
221
|
+
publish("#{addr}/position-pulses/$format", "0:65535")
|
|
222
|
+
publish("#{addr}/position-pulses/$unit", "pulses")
|
|
223
|
+
publish("#{addr}/position-pulses/$settable", "true")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if @home_assistant
|
|
227
|
+
@mqtt.publish_hass_number("position-pulses",
|
|
228
|
+
command_topic: "#{@base_topic}/#{addr}/position-pulses/set",
|
|
229
|
+
device: hass_device,
|
|
230
|
+
enabled_by_default: false,
|
|
231
|
+
max: 65_536,
|
|
232
|
+
min: 0,
|
|
233
|
+
name: "Position (Pulses)",
|
|
234
|
+
node_id:,
|
|
235
|
+
object_id: "position-pulses",
|
|
236
|
+
state_topic: "#{@base_topic}/#{addr}/position-pulses",
|
|
237
|
+
step: 10,
|
|
238
|
+
unit_of_measurement: "pulses",
|
|
239
|
+
unique_id: "#{node_id}_position-pulses",
|
|
240
|
+
availability: @availability)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
if @homie
|
|
244
|
+
publish("#{addr}/position-percent/$name", "Position (in %)")
|
|
245
|
+
publish("#{addr}/position-percent/$datatype", "integer")
|
|
246
|
+
publish("#{addr}/position-percent/$format", "0:100")
|
|
247
|
+
publish("#{addr}/position-percent/$unit", "%")
|
|
248
|
+
publish("#{addr}/position-percent/$settable", "true")
|
|
249
|
+
|
|
250
|
+
publish("#{addr}/ip/$name", "Intermediate Position")
|
|
251
|
+
publish("#{addr}/ip/$datatype", "integer")
|
|
252
|
+
publish("#{addr}/ip/$format", "1:16")
|
|
253
|
+
publish("#{addr}/ip/$settable", "true")
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if @home_assistant
|
|
257
|
+
@mqtt.publish_hass_number("ip",
|
|
258
|
+
command_topic: "#{@base_topic}/#{addr}/ip/set",
|
|
259
|
+
device: hass_device,
|
|
260
|
+
name: "Intermediate Position",
|
|
261
|
+
max: 16,
|
|
262
|
+
min: 0,
|
|
263
|
+
node_id:,
|
|
264
|
+
object_id: "ip",
|
|
265
|
+
payload_reset: "",
|
|
266
|
+
state_topic: "#{@base_topic}/#{addr}/ip",
|
|
267
|
+
unique_id: "#{node_id}_ip",
|
|
268
|
+
availability: @availability)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
if @homie
|
|
272
|
+
publish("#{addr}/state/$name", "State of the motors")
|
|
273
|
+
publish("#{addr}/state/$datatype", "enum")
|
|
274
|
+
publish("#{addr}/state/$format", "#{Message::PostMotorStatus::STATE.keys.join(",")},mixed")
|
|
275
|
+
|
|
276
|
+
publish("#{addr}/last-direction/$name", "Direction of last motion")
|
|
277
|
+
publish("#{addr}/last-direction/$datatype", "enum")
|
|
278
|
+
publish("#{addr}/last-direction/$format", "#{Message::PostMotorStatus::DIRECTION.keys.join(",")},mixed")
|
|
279
|
+
|
|
280
|
+
publish("#{addr}/motors/$name", "Comma separated motor addresses that are members of this group")
|
|
281
|
+
publish("#{addr}/motors/$datatype", "string")
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
group = @groups[addr] = Group.new(self, addr)
|
|
285
|
+
publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(",")) if @homie
|
|
286
|
+
end
|
|
287
|
+
group
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
private
|
|
291
|
+
|
|
292
|
+
def motor_hass_state(state, direction, position_percent)
|
|
293
|
+
if state == :running
|
|
294
|
+
(direction == :down) ? :closing : :opening
|
|
295
|
+
elsif position_percent&.zero?
|
|
296
|
+
:open
|
|
297
|
+
elsif position_percent == 100
|
|
298
|
+
:closed
|
|
299
|
+
else
|
|
300
|
+
:stopped
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
78
304
|
def subscribe(topic)
|
|
79
305
|
@mqtt.subscribe("#{@base_topic}/#{topic}")
|
|
80
306
|
end
|
|
81
307
|
|
|
308
|
+
# Adds a message to the prioritized write queue if it is not already present.
|
|
309
|
+
#
|
|
310
|
+
# @param message [Message]
|
|
311
|
+
#
|
|
312
|
+
# @return [void]
|
|
82
313
|
def enqueue(message)
|
|
83
314
|
@mutex.synchronize do
|
|
84
315
|
break if @queue.include?(message)
|
|
@@ -121,7 +352,7 @@ module SDN
|
|
|
121
352
|
name: "Somfy SDN Bridge",
|
|
122
353
|
identifiers: @device_id,
|
|
123
354
|
sw_version: SDN::VERSION
|
|
124
|
-
}
|
|
355
|
+
}.freeze
|
|
125
356
|
@mqtt.publish_hass_button("discover",
|
|
126
357
|
command_topic: "#{@base_topic}/FFFFFF/discover/set",
|
|
127
358
|
device: hass_device,
|
|
@@ -130,7 +361,8 @@ module SDN
|
|
|
130
361
|
node_id: @device_id,
|
|
131
362
|
object_id: "discover",
|
|
132
363
|
unique_id: "#{@device_id}_discover",
|
|
133
|
-
payload_press: "true"
|
|
364
|
+
payload_press: "true",
|
|
365
|
+
availability: @availability)
|
|
134
366
|
end
|
|
135
367
|
|
|
136
368
|
subscribe_all
|
|
@@ -168,6 +400,12 @@ module SDN
|
|
|
168
400
|
end
|
|
169
401
|
end
|
|
170
402
|
|
|
403
|
+
# Publishes MQTT metadata for a motor and schedules follow-up SDN discovery queries.
|
|
404
|
+
#
|
|
405
|
+
# @param addr [String] printable motor address without separators
|
|
406
|
+
# @param node_type [Symbol, Integer] detected motor node type
|
|
407
|
+
#
|
|
408
|
+
# @return [SDN::CLI::MQTT::Motor]
|
|
171
409
|
def publish_motor(addr, node_type)
|
|
172
410
|
motor = nil
|
|
173
411
|
|
|
@@ -177,7 +415,7 @@ module SDN
|
|
|
177
415
|
model_id: node_type,
|
|
178
416
|
name: addr,
|
|
179
417
|
via_device: @device_id
|
|
180
|
-
}
|
|
418
|
+
}.freeze
|
|
181
419
|
node_id = "#{@device_id}_#{addr}"
|
|
182
420
|
|
|
183
421
|
if @homie
|
|
@@ -226,10 +464,11 @@ module SDN
|
|
|
226
464
|
device: hass_device,
|
|
227
465
|
icon: "mdi:search-add",
|
|
228
466
|
name: "Rediscover",
|
|
229
|
-
node_id
|
|
467
|
+
node_id:,
|
|
230
468
|
object_id: "discover",
|
|
231
469
|
payload_press: "true",
|
|
232
|
-
unique_id: "#{node_id}_discover"
|
|
470
|
+
unique_id: "#{node_id}_discover",
|
|
471
|
+
availability: @availability)
|
|
233
472
|
end
|
|
234
473
|
|
|
235
474
|
if @homie
|
|
@@ -246,8 +485,9 @@ module SDN
|
|
|
246
485
|
icon: "mdi:rename",
|
|
247
486
|
max: 16,
|
|
248
487
|
name: "Label",
|
|
249
|
-
node_id
|
|
250
|
-
unique_id: "#{node_id}_label"
|
|
488
|
+
node_id:,
|
|
489
|
+
unique_id: "#{node_id}_label",
|
|
490
|
+
availability: @availability)
|
|
251
491
|
end
|
|
252
492
|
|
|
253
493
|
if @homie
|
|
@@ -288,7 +528,7 @@ module SDN
|
|
|
288
528
|
device: hass_device,
|
|
289
529
|
icon: "mdi:roller-shade",
|
|
290
530
|
name: "Motor",
|
|
291
|
-
node_id
|
|
531
|
+
node_id:,
|
|
292
532
|
payload_close: "down",
|
|
293
533
|
payload_open: "up",
|
|
294
534
|
payload_stop: "stop",
|
|
@@ -297,7 +537,8 @@ module SDN
|
|
|
297
537
|
position_topic: "#{@base_topic}/#{addr}/position-percent",
|
|
298
538
|
set_position_topic: "#{@base_topic}/#{addr}/position-percent/set",
|
|
299
539
|
state_topic: "#{@base_topic}/#{addr}/hass-state",
|
|
300
|
-
unique_id: "#{node_id}_motor"
|
|
540
|
+
unique_id: "#{node_id}_motor",
|
|
541
|
+
availability: @availability)
|
|
301
542
|
{
|
|
302
543
|
Wink: "mdi:emoticon-wink",
|
|
303
544
|
Next_IP: "mdi:skip-next",
|
|
@@ -307,11 +548,12 @@ module SDN
|
|
|
307
548
|
@mqtt.publish_hass_button(command.to_s.downcase,
|
|
308
549
|
command_topic: "#{@base_topic}/#{addr}/control/set",
|
|
309
550
|
device: hass_device,
|
|
310
|
-
icon
|
|
551
|
+
icon:,
|
|
311
552
|
name: command.to_s.sub("_", " "),
|
|
312
|
-
node_id
|
|
553
|
+
node_id:,
|
|
313
554
|
payload_press: command.to_s.downcase,
|
|
314
|
-
unique_id: "#{node_id}_#{command.to_s.downcase}"
|
|
555
|
+
unique_id: "#{node_id}_#{command.to_s.downcase}",
|
|
556
|
+
availability: @availability)
|
|
315
557
|
end
|
|
316
558
|
end
|
|
317
559
|
|
|
@@ -331,12 +573,13 @@ module SDN
|
|
|
331
573
|
max: 65_536,
|
|
332
574
|
min: 0,
|
|
333
575
|
name: "Position (Pulses)",
|
|
334
|
-
node_id
|
|
576
|
+
node_id:,
|
|
335
577
|
object_id: "position-pulses",
|
|
336
578
|
state_topic: "#{@base_topic}/#{addr}/position-pulses",
|
|
337
579
|
step: 10,
|
|
338
580
|
unit_of_measurement: "pulses",
|
|
339
|
-
unique_id: "#{node_id}_position-pulses"
|
|
581
|
+
unique_id: "#{node_id}_position-pulses",
|
|
582
|
+
availability: @availability)
|
|
340
583
|
end
|
|
341
584
|
|
|
342
585
|
if @homie
|
|
@@ -354,11 +597,12 @@ module SDN
|
|
|
354
597
|
name: "Intermediate Position",
|
|
355
598
|
max: 16,
|
|
356
599
|
min: 0,
|
|
357
|
-
node_id
|
|
600
|
+
node_id:,
|
|
358
601
|
object_id: "ip",
|
|
359
602
|
payload_reset: "",
|
|
360
603
|
state_topic: "#{@base_topic}/#{addr}/ip",
|
|
361
|
-
unique_id: "#{node_id}_ip"
|
|
604
|
+
unique_id: "#{node_id}_ip",
|
|
605
|
+
availability: @availability)
|
|
362
606
|
end
|
|
363
607
|
|
|
364
608
|
if @homie
|
|
@@ -377,12 +621,13 @@ module SDN
|
|
|
377
621
|
icon: "mdi:roller-shade-closed",
|
|
378
622
|
max: 65_536,
|
|
379
623
|
min: 0,
|
|
380
|
-
node_id
|
|
624
|
+
node_id:,
|
|
381
625
|
payload_reset: "",
|
|
382
626
|
state_topic: "#{@base_topic}/#{addr}/down-limit",
|
|
383
627
|
step: 10,
|
|
384
628
|
unit_of_measurement: "pulses",
|
|
385
|
-
unique_id: "#{node_id}_down-limit"
|
|
629
|
+
unique_id: "#{node_id}_down-limit",
|
|
630
|
+
availability: @availability)
|
|
386
631
|
end
|
|
387
632
|
|
|
388
633
|
if @homie
|
|
@@ -408,16 +653,18 @@ module SDN
|
|
|
408
653
|
enabled_by_default: false,
|
|
409
654
|
entity_category: :config,
|
|
410
655
|
name: "Reset #{key.to_s.sub("_", " ")}",
|
|
411
|
-
node_id
|
|
656
|
+
node_id:,
|
|
412
657
|
payload_press: key,
|
|
413
|
-
unique_id: "#{node_id}_#{key}"
|
|
658
|
+
unique_id: "#{node_id}_#{key}",
|
|
659
|
+
availability: @availability)
|
|
414
660
|
end
|
|
415
661
|
end
|
|
416
662
|
|
|
417
663
|
if @homie
|
|
418
664
|
publish("#{addr}/last-action-source/$name", "Source of last action")
|
|
419
665
|
publish("#{addr}/last-action-source/$datatype", "enum")
|
|
420
|
-
publish("#{addr}/last-action-source/$format",
|
|
666
|
+
publish("#{addr}/last-action-source/$format",
|
|
667
|
+
(Message::PostMotorStatus::SOURCE.keys + [:bridge]).join(","))
|
|
421
668
|
end
|
|
422
669
|
|
|
423
670
|
if @home_assistant
|
|
@@ -426,17 +673,19 @@ module SDN
|
|
|
426
673
|
device_class: :enum,
|
|
427
674
|
entity_category: :diagnostic,
|
|
428
675
|
name: "Source of last action",
|
|
429
|
-
node_id
|
|
676
|
+
node_id:,
|
|
430
677
|
object_id: "last-action-source",
|
|
431
678
|
options: Message::PostMotorStatus::SOURCE.keys,
|
|
432
679
|
state_topic: "#{@base_topic}/#{addr}/last-action-source",
|
|
433
|
-
unique_id: "#{node_id}_last-action-source"
|
|
680
|
+
unique_id: "#{node_id}_last-action-source",
|
|
681
|
+
availability: @availability)
|
|
434
682
|
end
|
|
435
683
|
|
|
436
684
|
if @homie
|
|
437
685
|
publish("#{addr}/last-action-cause/$name", "Cause of last action")
|
|
438
686
|
publish("#{addr}/last-action-cause/$datatype", "enum")
|
|
439
|
-
publish("#{addr}/last-action-cause/$format",
|
|
687
|
+
publish("#{addr}/last-action-cause/$format",
|
|
688
|
+
(Message::PostMotorStatus::CAUSE.keys + [:timeout]).join(","))
|
|
440
689
|
end
|
|
441
690
|
|
|
442
691
|
if @home_assistant
|
|
@@ -445,11 +694,12 @@ module SDN
|
|
|
445
694
|
device_class: :enum,
|
|
446
695
|
entity_category: :diagnostic,
|
|
447
696
|
name: "Cause of last action",
|
|
448
|
-
node_id
|
|
697
|
+
node_id:,
|
|
449
698
|
object_id: "last-action-cause",
|
|
450
699
|
options: Message::PostMotorStatus::CAUSE.keys,
|
|
451
700
|
state_topic: "#{@base_topic}/#{addr}/last-action-cause",
|
|
452
|
-
unique_id: "#{node_id}_last-action-cause"
|
|
701
|
+
unique_id: "#{node_id}_last-action-cause",
|
|
702
|
+
availability: @availability)
|
|
453
703
|
end
|
|
454
704
|
|
|
455
705
|
if @homie
|
|
@@ -460,7 +710,7 @@ module SDN
|
|
|
460
710
|
publish("#{addr}/up-limit/$settable", "true")
|
|
461
711
|
end
|
|
462
712
|
|
|
463
|
-
if @
|
|
713
|
+
if @home_assistant
|
|
464
714
|
@mqtt.publish_hass_number("up-limit",
|
|
465
715
|
command_topic: "#{@base_topic}/#{addr}/up-limit/set",
|
|
466
716
|
device: hass_device,
|
|
@@ -469,12 +719,13 @@ module SDN
|
|
|
469
719
|
max: 65_536,
|
|
470
720
|
min: 0,
|
|
471
721
|
name: "Up Limit",
|
|
472
|
-
node_id
|
|
722
|
+
node_id:,
|
|
473
723
|
payload_reset: "",
|
|
474
724
|
state_topic: "#{@base_topic}/#{addr}/up-limit",
|
|
475
725
|
step: 10,
|
|
476
726
|
unit_of_measurement: "pulses",
|
|
477
|
-
unique_id: "#{node_id}_up-limit"
|
|
727
|
+
unique_id: "#{node_id}_up-limit",
|
|
728
|
+
availability: @availability)
|
|
478
729
|
end
|
|
479
730
|
|
|
480
731
|
if @homie
|
|
@@ -484,18 +735,19 @@ module SDN
|
|
|
484
735
|
publish("#{addr}/direction/$settable", "true")
|
|
485
736
|
end
|
|
486
737
|
|
|
487
|
-
if @
|
|
738
|
+
if @home_assistant
|
|
488
739
|
@mqtt.publish_hass_select("direction",
|
|
489
740
|
command_topic: "#{@base_topic}/#{addr}/direction/set",
|
|
490
741
|
device: hass_device,
|
|
491
742
|
entity_category: :config,
|
|
492
743
|
icon: "mdi:circle-arrows",
|
|
493
744
|
name: "Motor rotation direction",
|
|
494
|
-
node_id
|
|
745
|
+
node_id:,
|
|
495
746
|
object_id: "direction",
|
|
496
747
|
options: %w[standard reversed],
|
|
497
748
|
state_topic: "#{@base_topic}/#{addr}/direction",
|
|
498
|
-
unique_id: "#{node_id}_direction"
|
|
749
|
+
unique_id: "#{node_id}_direction",
|
|
750
|
+
availability: @availability)
|
|
499
751
|
end
|
|
500
752
|
|
|
501
753
|
if @homie
|
|
@@ -528,10 +780,11 @@ module SDN
|
|
|
528
780
|
max: 28,
|
|
529
781
|
min: 6,
|
|
530
782
|
name: "#{speed_type} speed",
|
|
531
|
-
node_id
|
|
783
|
+
node_id:,
|
|
532
784
|
state_topic: "#{@base_topic}/#{addr}/#{speed_type.downcase}-speed",
|
|
533
785
|
unit_of_measurement: "RPM",
|
|
534
|
-
unique_id: "#{node_id}_#{speed_type.downcase}-speed"
|
|
786
|
+
unique_id: "#{node_id}_#{speed_type.downcase}-speed",
|
|
787
|
+
availability: @availability)
|
|
535
788
|
end
|
|
536
789
|
end
|
|
537
790
|
end
|
|
@@ -560,13 +813,14 @@ module SDN
|
|
|
560
813
|
max: 65_536,
|
|
561
814
|
min: 0,
|
|
562
815
|
name: "Intermediation Position #{ip} (Pulses)",
|
|
563
|
-
node_id
|
|
816
|
+
node_id:,
|
|
564
817
|
object_id: "ip#{ip}-pulses",
|
|
565
818
|
payload_reset: "",
|
|
566
819
|
state_topic: "#{@base_topic}/#{addr}/ip#{ip}-pulses",
|
|
567
820
|
step: 10,
|
|
568
821
|
unit_of_measurement: "pulses",
|
|
569
|
-
unique_id: "#{node_id}_ip#{ip}-pulses"
|
|
822
|
+
unique_id: "#{node_id}_ip#{ip}-pulses",
|
|
823
|
+
availability: @availability)
|
|
570
824
|
end
|
|
571
825
|
|
|
572
826
|
if @homie
|
|
@@ -586,12 +840,13 @@ module SDN
|
|
|
586
840
|
max: 100,
|
|
587
841
|
min: 0,
|
|
588
842
|
name: "Intermediation Position #{ip} (Percent)",
|
|
589
|
-
node_id
|
|
843
|
+
node_id:,
|
|
590
844
|
object_id: "ip#{ip}-percent",
|
|
591
845
|
payload_reset: "",
|
|
592
846
|
state_topic: "#{@base_topic}/#{addr}/ip#{ip}-percent",
|
|
593
847
|
unit_of_measurement: "%",
|
|
594
|
-
unique_id: "#{node_id}_ip#{ip}-percent"
|
|
848
|
+
unique_id: "#{node_id}_ip#{ip}-percent",
|
|
849
|
+
availability: @availability)
|
|
595
850
|
end
|
|
596
851
|
|
|
597
852
|
motor = Motor.new(self, addr, node_type)
|
|
@@ -643,176 +898,6 @@ module SDN
|
|
|
643
898
|
|
|
644
899
|
motor
|
|
645
900
|
end
|
|
646
|
-
|
|
647
|
-
def touch_group(group_addr)
|
|
648
|
-
group = @groups[Message.print_address(group_addr).delete(".")]
|
|
649
|
-
group&.publish(:motors, group.motors_string)
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
def add_group(addr)
|
|
653
|
-
addr = addr.delete(".")
|
|
654
|
-
group = @groups[addr]
|
|
655
|
-
return group if group
|
|
656
|
-
|
|
657
|
-
@mqtt.batch_publish do
|
|
658
|
-
hass_device = {
|
|
659
|
-
identifiers: addr,
|
|
660
|
-
model: "Shade Group",
|
|
661
|
-
name: addr,
|
|
662
|
-
via_device: @device_id
|
|
663
|
-
}
|
|
664
|
-
node_id = "#{@device_id}_#{addr}"
|
|
665
|
-
|
|
666
|
-
if @homie
|
|
667
|
-
publish("#{addr}/$name", addr)
|
|
668
|
-
publish("#{addr}/$type", "Shade Group")
|
|
669
|
-
publish("#{addr}/$properties",
|
|
670
|
-
"discover,control,jog-ms,jog-pulses,position-pulses,position-percent," \
|
|
671
|
-
"ip,reset,state,last-direction,motors")
|
|
672
|
-
|
|
673
|
-
publish("#{addr}/discover/$name", "Trigger Motor Discovery")
|
|
674
|
-
publish("#{addr}/discover/$datatype", "enum")
|
|
675
|
-
publish("#{addr}/discover/$format", "discover")
|
|
676
|
-
publish("#{addr}/discover/$settable", "true")
|
|
677
|
-
publish("#{addr}/discover/$retained", "false")
|
|
678
|
-
end
|
|
679
|
-
|
|
680
|
-
if @home_assistant
|
|
681
|
-
@mqtt.publish_hass_button("discover",
|
|
682
|
-
command_topic: "#{@base_topic}/#{addr}/discover/set",
|
|
683
|
-
device: hass_device,
|
|
684
|
-
icon: "mdi:search-add",
|
|
685
|
-
name: "Rediscover",
|
|
686
|
-
node_id: node_id,
|
|
687
|
-
object_id: "discover",
|
|
688
|
-
payload_press: "discover",
|
|
689
|
-
unique_id: "#{node_id}_discover")
|
|
690
|
-
end
|
|
691
|
-
|
|
692
|
-
if @homie
|
|
693
|
-
publish("#{addr}/control/$name", "Control motors")
|
|
694
|
-
publish("#{addr}/control/$datatype", "enum")
|
|
695
|
-
publish("#{addr}/control/$format", "up,down,stop,wink,next_ip,previous_ip,refresh")
|
|
696
|
-
publish("#{addr}/control/$settable", "true")
|
|
697
|
-
publish("#{addr}/control/$retained", "false")
|
|
698
|
-
end
|
|
699
|
-
|
|
700
|
-
if @home_assistant
|
|
701
|
-
@mqtt.publish_hass_cover("group",
|
|
702
|
-
command_topic: "#{@base_topic}/#{addr}/control/set",
|
|
703
|
-
device: hass_device,
|
|
704
|
-
icon: "mdi:roller-shade",
|
|
705
|
-
name: "Group",
|
|
706
|
-
node_id: node_id,
|
|
707
|
-
payload_close: "down",
|
|
708
|
-
payload_open: "up",
|
|
709
|
-
payload_stop: "stop",
|
|
710
|
-
position_open: 0,
|
|
711
|
-
position_closed: 100,
|
|
712
|
-
position_topic: "#{@base_topic}/#{addr}/position-percent",
|
|
713
|
-
set_position_topic: "#{@base_topic}/#{addr}/position-percent/set",
|
|
714
|
-
state_topic: "#{@base_topic}/#{addr}/hass-state",
|
|
715
|
-
unique_id: "#{node_id}_group")
|
|
716
|
-
{
|
|
717
|
-
Wink: "mdi:emoticon-wink",
|
|
718
|
-
Next_IP: "mdi:skip-next",
|
|
719
|
-
Previous_IP: "mdi:skip-previous",
|
|
720
|
-
Refresh: "mdi:refresh"
|
|
721
|
-
}.each do |command, icon|
|
|
722
|
-
@mqtt.publish_hass_button(command.to_s.downcase,
|
|
723
|
-
command_topic: "#{@base_topic}/#{addr}/control/set",
|
|
724
|
-
device: hass_device,
|
|
725
|
-
icon: icon,
|
|
726
|
-
name: command.to_s.sub("_", " "),
|
|
727
|
-
node_id: node_id,
|
|
728
|
-
payload_press: command.to_s.downcase,
|
|
729
|
-
unique_id: "#{node_id}_#{command.to_s.downcase}")
|
|
730
|
-
end
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
if @homie
|
|
734
|
-
publish("#{addr}/jog-ms/$name", "Jog motors by ms")
|
|
735
|
-
publish("#{addr}/jog-ms/$datatype", "integer")
|
|
736
|
-
publish("#{addr}/jog-ms/$format", "-65535:65535")
|
|
737
|
-
publish("#{addr}/jog-ms/$unit", "ms")
|
|
738
|
-
publish("#{addr}/jog-ms/$settable", "true")
|
|
739
|
-
publish("#{addr}/jog-ms/$retained", "false")
|
|
740
|
-
|
|
741
|
-
publish("#{addr}/jog-pulses/$name", "Jog motors by pulses")
|
|
742
|
-
publish("#{addr}/jog-pulses/$datatype", "integer")
|
|
743
|
-
publish("#{addr}/jog-pulses/$format", "-65535:65535")
|
|
744
|
-
publish("#{addr}/jog-pulses/$unit", "pulses")
|
|
745
|
-
publish("#{addr}/jog-pulses/$settable", "true")
|
|
746
|
-
publish("#{addr}/jog-pulses/$retained", "false")
|
|
747
|
-
|
|
748
|
-
publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
|
|
749
|
-
publish("#{addr}/position-pulses/$datatype", "integer")
|
|
750
|
-
publish("#{addr}/position-pulses/$format", "0:65535")
|
|
751
|
-
publish("#{addr}/position-pulses/$unit", "pulses")
|
|
752
|
-
publish("#{addr}/position-pulses/$settable", "true")
|
|
753
|
-
end
|
|
754
|
-
|
|
755
|
-
if @home_assistant
|
|
756
|
-
@mqtt.publish_hass_number("position-pulses",
|
|
757
|
-
command_topic: "#{@base_topic}/#{addr}/position-pulses/set",
|
|
758
|
-
device: hass_device,
|
|
759
|
-
enabled_by_default: false,
|
|
760
|
-
max: 65_536,
|
|
761
|
-
min: 0,
|
|
762
|
-
name: "Position (Pulses)",
|
|
763
|
-
node_id: node_id,
|
|
764
|
-
object_id: "position-pulses",
|
|
765
|
-
state_topic: "#{@base_topic}/#{addr}/position-pulses",
|
|
766
|
-
step: 10,
|
|
767
|
-
unit_of_measurement: "pulses",
|
|
768
|
-
unique_id: "#{node_id}_position-pulses")
|
|
769
|
-
end
|
|
770
|
-
|
|
771
|
-
if @homie
|
|
772
|
-
publish("#{addr}/position-percent/$name", "Position (in %)")
|
|
773
|
-
publish("#{addr}/position-percent/$datatype", "integer")
|
|
774
|
-
publish("#{addr}/position-percent/$format", "0:100")
|
|
775
|
-
publish("#{addr}/position-percent/$unit", "%")
|
|
776
|
-
publish("#{addr}/position-percent/$settable", "true")
|
|
777
|
-
|
|
778
|
-
publish("#{addr}/ip/$name", "Intermediate Position")
|
|
779
|
-
publish("#{addr}/ip/$datatype", "integer")
|
|
780
|
-
publish("#{addr}/ip/$format", "1:16")
|
|
781
|
-
publish("#{addr}/ip/$settable", "true")
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
if @home_assistant
|
|
785
|
-
@mqtt.publish_hass_number("ip",
|
|
786
|
-
command_topic: "#{@base_topic}/#{addr}/ip/set",
|
|
787
|
-
device: hass_device,
|
|
788
|
-
name: "Intermediate Position",
|
|
789
|
-
max: 16,
|
|
790
|
-
min: 0,
|
|
791
|
-
node_id: node_id,
|
|
792
|
-
object_id: "ip",
|
|
793
|
-
payload_reset: "",
|
|
794
|
-
state_topic: "#{@base_topic}/#{addr}/ip",
|
|
795
|
-
unique_id: "#{node_id}_ip")
|
|
796
|
-
end
|
|
797
|
-
|
|
798
|
-
if @homie
|
|
799
|
-
publish("#{addr}/state/$name", "State of the motors")
|
|
800
|
-
publish("#{addr}/state/$datatype", "enum")
|
|
801
|
-
publish("#{addr}/state/$format", "#{Message::PostMotorStatus::STATE.keys.join(",")},mixed")
|
|
802
|
-
|
|
803
|
-
publish("#{addr}/last-direction/$name", "Direction of last motion")
|
|
804
|
-
publish("#{addr}/last-direction/$datatype", "enum")
|
|
805
|
-
publish("#{addr}/last-direction/$format", "#{Message::PostMotorStatus::DIRECTION.keys.join(",")},mixed")
|
|
806
|
-
|
|
807
|
-
publish("#{addr}/motors/$name", "Comma separated motor addresses that are members of this group")
|
|
808
|
-
publish("#{addr}/motors/$datatype", "string")
|
|
809
|
-
end
|
|
810
|
-
|
|
811
|
-
group = @groups[addr] = Group.new(self, addr)
|
|
812
|
-
publish("$nodes", (["FFFFFF"] + @motors.keys.sort + @groups.keys.sort).join(",")) if @homie
|
|
813
|
-
end
|
|
814
|
-
group
|
|
815
|
-
end
|
|
816
901
|
end
|
|
817
902
|
end
|
|
818
903
|
end
|