somfy_sdn 2.2.2 → 2.3.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: 376921afa9659381b31f588319c02f35f3d539052e8eb6375b311420bcdac60e
4
- data.tar.gz: 8c8f5644816c615d0f31dec41e615a1d5857c1f1ee94e45d83bc759616c30e04
3
+ metadata.gz: 58db5a5328195a9cbd9458c4702f2e31490b7a5967d7e94e4ac9fc84a569ba88
4
+ data.tar.gz: cb58ee6964aeb41ce39737b3bdacd44974b2f40dd53395c0376c09b8e2add269
5
5
  SHA512:
6
- metadata.gz: 84de94b508cc42c76bab56b3a02ba61cd389277463583353c67df5798a710016e143b4607367be71db35efa052949c146476a591ef4d8a08f54a99e76833e907
7
- data.tar.gz: 2319f36c605560ebccc80e64600a26f2fec4abb806d33bb041bb0f667c41de1f6facecf8bd93b0ec557a676b34474393c1b4fa807a3cfcc84adbbae9244e0ccc
6
+ metadata.gz: 0d2f32416ae7e943f101868cdda35162f1b14dd7e047d1df2725c36095fc4437e713fc32546b03e10c89e013b00210062ff3e8dfd88f6503266cc3a65e5e262b
7
+ data.tar.gz: 3b1e3732ad5d70fbac17f5e498b418a096c890e37b570e5da70cfbeee77c2b0ae6cec25d0ae2f8c3f39bac80e836ab8a70018b43ba75ca3be3421478281f8757
@@ -11,6 +11,7 @@ module SDN
11
11
  :position_percent,
12
12
  :ip,
13
13
  :state,
14
+ :hass_state,
14
15
  :last_direction,
15
16
  :last_action_source,
16
17
  :last_action_cause,
@@ -83,6 +83,16 @@ module SDN
83
83
  follow_ups << Message::GetMotorPosition.new(message.src)
84
84
  motor.publish(:state, message.state)
85
85
  motor.publish(:last_direction, message.last_direction)
86
+ hass_state = if message.state == :running
87
+ (message.last_direction == :down) ? :closing : :opening
88
+ elsif motor.position_percent&.zero?
89
+ :open
90
+ elsif motor.position_percent == 100
91
+ :closed
92
+ else
93
+ :stopped
94
+ end
95
+ motor.publish(:hass_state, hass_state)
86
96
  motor.publish(:last_action_source, message.last_action_source)
87
97
  motor.publish(:last_action_cause, message.last_action_cause)
88
98
  motor.group_objects.each do |group|
data/lib/sdn/cli/mqtt.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mqtt"
4
+ require "mqtt-homeassistant"
4
5
  require "uri"
5
6
  require "set"
6
7
 
@@ -32,6 +33,7 @@ module SDN
32
33
  auto_discover: true,
33
34
  known_motors: [])
34
35
  @base_topic = "#{base_topic}/#{device_id}"
36
+ @device_id = device_id
35
37
  @mqtt = ::MQTT::Client.new(mqtt_uri)
36
38
  @mqtt.set_will("#{@base_topic}/$state", "lost", retain: true)
37
39
  @mqtt.connect
@@ -107,6 +109,21 @@ module SDN
107
109
  publish("FFFFFF/discover/$settable", "true")
108
110
  publish("FFFFFF/discover/$retained", "false")
109
111
 
112
+ hass_device = {
113
+ name: "Somfy SDN Bridge",
114
+ identifiers: @device_id,
115
+ sw_version: SDN::VERSION
116
+ }
117
+ @mqtt.publish_hass_button("discover",
118
+ command_topic: "#{@base_topic}/FFFFFF/discover/set",
119
+ device: hass_device,
120
+ icon: "mdi:search-add",
121
+ name: "Discover Motors",
122
+ node_id: @device_id,
123
+ object_id: "discover",
124
+ unique_id: "#{@device_id}_discover",
125
+ payload_press: "true")
126
+
110
127
  subscribe_all
111
128
 
112
129
  publish("$state", "ready")
@@ -146,6 +163,14 @@ module SDN
146
163
  motor = nil
147
164
 
148
165
  @mqtt.batch_publish do
166
+ hass_device = {
167
+ identifiers: addr,
168
+ model_id: node_type,
169
+ name: addr,
170
+ via_device: @device_id
171
+ }
172
+ node_id = "#{@device_id}_#{addr}"
173
+
149
174
  publish("#{addr}/$name", addr)
150
175
  publish("#{addr}/$type", node_type.to_s)
151
176
  properties = %w[
@@ -181,10 +206,28 @@ module SDN
181
206
  publish("#{addr}/discover/$format", "discover")
182
207
  publish("#{addr}/discover/$settable", "true")
183
208
  publish("#{addr}/discover/$retained", "false")
209
+ @mqtt.publish_hass_button("discover",
210
+ command_topic: "#{@base_topic}/#{addr}/discover/set",
211
+ device: hass_device,
212
+ icon: "mdi:search-add",
213
+ name: "Rediscover",
214
+ node_id: node_id,
215
+ object_id: "discover",
216
+ payload_press: "true",
217
+ unique_id: "#{node_id}_discover")
184
218
 
185
219
  publish("#{addr}/label/$name", "Node label")
186
220
  publish("#{addr}/label/$datatype", "string")
187
221
  publish("#{addr}/label/$settable", "true")
222
+ @mqtt.publish_hass_text("label",
223
+ command_topic: "#{@base_topic}/#{addr}/label/set",
224
+ device: hass_device,
225
+ entity_category: :config,
226
+ icon: "mdi:rename",
227
+ max: 16,
228
+ name: "Label",
229
+ node_id: node_id,
230
+ unique_id: "#{node_id}_label")
188
231
 
189
232
  publish("#{addr}/state/$name", "Current state of the motor")
190
233
  publish("#{addr}/state/$datatype", "enum")
@@ -216,24 +259,95 @@ module SDN
216
259
  publish("#{addr}/position-percent/$unit", "%")
217
260
  publish("#{addr}/position-percent/$settable", "true")
218
261
 
262
+ @mqtt.publish_hass_cover("motor",
263
+ command_topic: "#{@base_topic}/#{addr}/control/set",
264
+ device: hass_device,
265
+ icon: "mdi:roller-shade",
266
+ name: "Motor",
267
+ node_id: node_id,
268
+ payload_close: "down",
269
+ payload_open: "up",
270
+ payload_stop: "stop",
271
+ position_open: 0,
272
+ position_closed: 100,
273
+ position_topic: "#{@base_topic}/#{addr}/position-percent",
274
+ set_position_topic: "#{@base_topic}/#{addr}/position-percent/set",
275
+ state_topic: "#{@base_topic}/#{addr}/hass-state",
276
+ unique_id: "#{node_id}_motor")
277
+ {
278
+ Wink: "mdi:emoticon-wink",
279
+ Next_IP: "mdi:skip-next",
280
+ Previous_IP: "mdi:skip-previous",
281
+ Refresh: "mdi:refresh"
282
+ }.each do |command, icon|
283
+ @mqtt.publish_hass_button(command.to_s.downcase,
284
+ command_topic: "#{@base_topic}/#{addr}/control/set",
285
+ device: hass_device,
286
+ icon: icon,
287
+ name: command.to_s.sub("_", " "),
288
+ node_id: node_id,
289
+ payload_press: command.to_s.downcase,
290
+ unique_id: "#{node_id}_#{command.to_s.downcase}")
291
+ end
292
+
219
293
  publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
220
294
  publish("#{addr}/position-pulses/$datatype", "integer")
221
295
  publish("#{addr}/position-pulses/$format", "0:65535")
222
296
  publish("#{addr}/position-pulses/$unit", "pulses")
223
297
  publish("#{addr}/position-pulses/$settable", "true")
224
298
 
299
+ @mqtt.publish_hass_number("position-pulses",
300
+ command_topic: "#{@base_topic}/#{addr}/position-pulses/set",
301
+ device: hass_device,
302
+ enabled_by_default: false,
303
+ max: 65_536,
304
+ min: 0,
305
+ name: "Position (Pulses)",
306
+ node_id: node_id,
307
+ object_id: "position-pulses",
308
+ state_topic: "#{@base_topic}/#{addr}/position-pulses",
309
+ step: 10,
310
+ unit_of_measurement: "pulses",
311
+ unique_id: "#{node_id}_position-pulses")
312
+
225
313
  publish("#{addr}/ip/$name", "Intermediate Position")
226
314
  publish("#{addr}/ip/$datatype", "integer")
227
315
  publish("#{addr}/ip/$format", "1:16")
228
316
  publish("#{addr}/ip/$settable", "true")
229
317
  publish("#{addr}/ip/$retained", "false") if node_type == :st50ilt2
230
318
 
319
+ @mqtt.publish_hass_number("ip",
320
+ command_topic: "#{@base_topic}/#{addr}/ip/set",
321
+ device: hass_device,
322
+ name: "Intermediate Position",
323
+ max: 16,
324
+ min: 0,
325
+ node_id: node_id,
326
+ object_id: "ip",
327
+ payload_reset: "",
328
+ state_topic: "#{@base_topic}/#{addr}/ip",
329
+ unique_id: "#{node_id}_ip")
330
+
231
331
  publish("#{addr}/down-limit/$name", "Down limit")
232
332
  publish("#{addr}/down-limit/$datatype", "integer")
233
333
  publish("#{addr}/down-limit/$format", "0:65535")
234
334
  publish("#{addr}/down-limit/$unit", "pulses")
235
335
  publish("#{addr}/down-limit/$settable", "true")
236
336
 
337
+ @mqtt.publish_hass_number("down-limit",
338
+ command_topic: "#{@base_topic}/#{addr}/down-limit/set",
339
+ device: hass_device,
340
+ entity_category: :config,
341
+ icon: "mdi:roller-shade-closed",
342
+ max: 65_536,
343
+ min: 0,
344
+ node_id: node_id,
345
+ payload_reset: "",
346
+ state_topic: "#{@base_topic}/#{addr}/down-limit",
347
+ step: 10,
348
+ unit_of_measurement: "pulses",
349
+ unique_id: "#{node_id}_down-limit")
350
+
237
351
  publish("#{addr}/last-direction/$name", "Direction of last motion")
238
352
  publish("#{addr}/last-direction/$datatype", "enum")
239
353
  publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(","))
@@ -245,25 +359,86 @@ module SDN
245
359
  publish("#{addr}/reset/$settable", "true")
246
360
  publish("#{addr}/reset/$retained", "false")
247
361
 
362
+ Message::SetFactoryDefault::RESET.each_key do |key|
363
+ @mqtt.publish_hass_button("reset_#{key}",
364
+ command_topic: "#{@base_topic}/#{addr}/reset/set",
365
+ device: hass_device,
366
+ enabled_by_default: false,
367
+ entity_category: :config,
368
+ name: "Reset #{key.to_s.sub("_", " ")}",
369
+ node_id: node_id,
370
+ payload_press: key,
371
+ unique_id: "#{node_id}_#{key}")
372
+ end
373
+
248
374
  publish("#{addr}/last-action-source/$name", "Source of last action")
249
375
  publish("#{addr}/last-action-source/$datatype", "enum")
250
376
  publish("#{addr}/last-action-source/$format", Message::PostMotorStatus::SOURCE.keys.join(","))
251
377
 
378
+ @mqtt.publish_hass_sensor("last-action-source",
379
+ device: hass_device,
380
+ device_class: :enum,
381
+ entity_category: :diagnostic,
382
+ name: "Source of last action",
383
+ node_id: node_id,
384
+ object_id: "last-action-source",
385
+ options: Message::PostMotorStatus::SOURCE.keys,
386
+ state_topic: "#{@base_topic}/#{addr}/last-action-source",
387
+ unique_id: "#{node_id}_last-action-source")
388
+
252
389
  publish("#{addr}/last-action-cause/$name", "Cause of last action")
253
390
  publish("#{addr}/last-action-cause/$datatype", "enum")
254
391
  publish("#{addr}/last-action-cause/$format", Message::PostMotorStatus::CAUSE.keys.join(","))
255
392
 
393
+ @mqtt.publish_hass_sensor("last-action-cause",
394
+ device: hass_device,
395
+ device_class: :enum,
396
+ entity_category: :diagnostic,
397
+ name: "Cause of last action",
398
+ node_id: node_id,
399
+ object_id: "last-action-cause",
400
+ options: Message::PostMotorStatus::CAUSE.keys,
401
+ state_topic: "#{@base_topic}/#{addr}/last-action-cause",
402
+ unique_id: "#{node_id}_last-action-cause")
403
+
256
404
  publish("#{addr}/up-limit/$name", "Up limit (always = 0)")
257
405
  publish("#{addr}/up-limit/$datatype", "integer")
258
406
  publish("#{addr}/up-limit/$format", "0:65535")
259
407
  publish("#{addr}/up-limit/$unit", "pulses")
260
408
  publish("#{addr}/up-limit/$settable", "true")
261
409
 
410
+ @mqtt.publish_hass_number("up-limit",
411
+ command_topic: "#{@base_topic}/#{addr}/up-limit/set",
412
+ device: hass_device,
413
+ entity_category: :config,
414
+ icon: "mdi:roller-shade-open",
415
+ max: 65_536,
416
+ min: 0,
417
+ name: "Up Limit",
418
+ node_id: node_id,
419
+ payload_reset: "",
420
+ state_topic: "#{@base_topic}/#{addr}/up-limit",
421
+ step: 10,
422
+ unit_of_measurement: "pulses",
423
+ unique_id: "#{node_id}_up-limit")
424
+
262
425
  publish("#{addr}/direction/$name", "Motor rotation direction")
263
426
  publish("#{addr}/direction/$datatype", "enum")
264
427
  publish("#{addr}/direction/$format", "standard,reversed")
265
428
  publish("#{addr}/direction/$settable", "true")
266
429
 
430
+ @mqtt.publish_hass_select("direction",
431
+ command_topic: "#{@base_topic}/#{addr}/direction/set",
432
+ device: hass_device,
433
+ entity_category: :config,
434
+ icon: "mdi:circle-arrows",
435
+ name: "Motor rotation direction",
436
+ node_id: node_id,
437
+ object_id: "direction",
438
+ options: %w[standard reversed],
439
+ state_topic: "#{@base_topic}/#{addr}/direction",
440
+ unique_id: "#{node_id}_direction")
441
+
267
442
  publish("#{addr}/up-speed/$name", "Up speed")
268
443
  publish("#{addr}/up-speed/$datatype", "integer")
269
444
  publish("#{addr}/up-speed/$format", "6:28")
@@ -281,6 +456,21 @@ module SDN
281
456
  publish("#{addr}/slow-speed/$format", "6:28")
282
457
  publish("#{addr}/slow-speed/$unit", "RPM")
283
458
  publish("#{addr}/slow-speed/$settable", "true")
459
+
460
+ %w[Up Slow].each do |speed_type|
461
+ @mqtt.publish_hass_number("#{speed_type.downcase}-speed",
462
+ command_topic: "#{@base_topic}/#{addr}/#{speed_type.downcase}-speed/set",
463
+ device: hass_device,
464
+ entity_category: :config,
465
+ icon: "mdi:car-speed-limiter",
466
+ max: 28,
467
+ min: 6,
468
+ name: "#{speed_type} speed",
469
+ node_id: node_id,
470
+ state_topic: "#{@base_topic}/#{addr}/#{speed_type.downcase}-speed",
471
+ unit_of_measurement: "RPM",
472
+ unique_id: "#{node_id}_#{speed_type.downcase}-speed")
473
+ end
284
474
  end
285
475
 
286
476
  publish("#{addr}/groups/$name", "Group Memberships (comma separated, address must start 0101xx)")
@@ -294,11 +484,41 @@ module SDN
294
484
  publish("#{addr}/ip#{ip}-pulses/$unit", "pulses")
295
485
  publish("#{addr}/ip#{ip}-pulses/$settable", "true")
296
486
 
487
+ @mqtt.publish_hass_number("ip#{ip}-pulses",
488
+ command_topic: "#{@base_topic}/#{addr}/ip#{ip}-pulses/set",
489
+ device: hass_device,
490
+ enabled_by_default: false,
491
+ entity_category: :config,
492
+ max: 65_536,
493
+ min: 0,
494
+ name: "Intermediation Position #{ip} (Pulses)",
495
+ node_id: node_id,
496
+ object_id: "ip#{ip}-pulses",
497
+ payload_reset: "",
498
+ state_topic: "#{@base_topic}/#{addr}/ip#{ip}-pulses",
499
+ step: 10,
500
+ unit_of_measurement: "pulses",
501
+ unique_id: "#{node_id}_ip#{ip}-pulses")
502
+
297
503
  publish("#{addr}/ip#{ip}-percent/$name", "Intermediate Position #{ip}")
298
504
  publish("#{addr}/ip#{ip}-percent/$datatype", "integer")
299
505
  publish("#{addr}/ip#{ip}-percent/$format", "0:100")
300
506
  publish("#{addr}/ip#{ip}-percent/$unit", "%")
301
507
  publish("#{addr}/ip#{ip}-percent/$settable", "true")
508
+
509
+ @mqtt.publish_hass_number("ip#{ip}-percent",
510
+ command_topic: "#{@base_topic}/#{addr}/ip#{ip}-percent/set",
511
+ device: hass_device,
512
+ entity_category: :config,
513
+ max: 100,
514
+ min: 0,
515
+ name: "Intermediation Position #{ip} (Percent)",
516
+ node_id: node_id,
517
+ object_id: "ip#{ip}-percent",
518
+ payload_reset: "",
519
+ state_topic: "#{@base_topic}/#{addr}/ip#{ip}-percent",
520
+ unit_of_measurement: "%",
521
+ unique_id: "#{node_id}_ip#{ip}-percent")
302
522
  end
303
523
 
304
524
  motor = Motor.new(self, addr, node_type)
data/lib/sdn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SDN
4
- VERSION = "2.2.2"
4
+ VERSION = "2.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: somfy_sdn
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-02 00:00:00.000000000 Z
11
+ date: 2025-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ccutrer-serialport
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
- name: mqtt-ccutrer
42
+ name: mqtt-homeassistant
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.1'
83
- - !ruby/object:Gem::Dependency
84
- name: debug
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '1.9'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '1.9'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rake
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,35 +94,7 @@ dependencies:
108
94
  - - "~>"
109
95
  - !ruby/object:Gem::Version
110
96
  version: '13.0'
111
- - !ruby/object:Gem::Dependency
112
- name: rubocop-inst
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '1.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '1.0'
125
- - !ruby/object:Gem::Dependency
126
- name: rubocop-rake
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '0.6'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '0.6'
139
- description:
97
+ description:
140
98
  email: cody@cutrer.com'
141
99
  executables:
142
100
  - somfy_sdn
@@ -172,7 +130,7 @@ licenses:
172
130
  - MIT
173
131
  metadata:
174
132
  rubygems_mfa_required: 'true'
175
- post_install_message:
133
+ post_install_message:
176
134
  rdoc_options: []
177
135
  require_paths:
178
136
  - lib
@@ -188,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
146
  version: '0'
189
147
  requirements: []
190
148
  rubygems_version: 3.5.11
191
- signing_key:
149
+ signing_key:
192
150
  specification_version: 4
193
151
  summary: Library for communication with Somfy SDN RS-485 motorized shades
194
152
  test_files: []