somfy_sdn 2.2.2 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 376921afa9659381b31f588319c02f35f3d539052e8eb6375b311420bcdac60e
4
- data.tar.gz: 8c8f5644816c615d0f31dec41e615a1d5857c1f1ee94e45d83bc759616c30e04
3
+ metadata.gz: 541526d413e48d3a6d60db5758a5f2ac92cfa1a397420c29221488f3993e0ae2
4
+ data.tar.gz: d758f5f0cab2a0fbb6defecadb752f2342c031f209771f26ddf81003a361d649
5
5
  SHA512:
6
- metadata.gz: 84de94b508cc42c76bab56b3a02ba61cd389277463583353c67df5798a710016e143b4607367be71db35efa052949c146476a591ef4d8a08f54a99e76833e907
7
- data.tar.gz: 2319f36c605560ebccc80e64600a26f2fec4abb806d33bb041bb0f667c41de1f6facecf8bd93b0ec557a676b34474393c1b4fa807a3cfcc84adbbae9244e0ccc
6
+ metadata.gz: ea35c9e9d4700af4da7971bbee10dafa9a3303ae17e554d3d905daebd59c07796ec8b260750a6337deb1c00edf03607ac885316fbcc9535a0a820a94fa991550
7
+ data.tar.gz: be63bab6a4ba6b727d3b2e242c1fbd6453130367272978e15ac439ce92bdc3bfcede6cbd5c63235355753c2fde986ea7e1287bc4ce6321f357b5953eee97d789
@@ -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
 
@@ -10,6 +11,7 @@ require "sdn/cli/mqtt/p_queue"
10
11
  require "sdn/cli/mqtt/read"
11
12
  require "sdn/cli/mqtt/write"
12
13
  require "sdn/cli/mqtt/subscriptions"
14
+ require "sdn/version"
13
15
 
14
16
  module SDN
15
17
  module CLI
@@ -32,6 +34,7 @@ module SDN
32
34
  auto_discover: true,
33
35
  known_motors: [])
34
36
  @base_topic = "#{base_topic}/#{device_id}"
37
+ @device_id = device_id
35
38
  @mqtt = ::MQTT::Client.new(mqtt_uri)
36
39
  @mqtt.set_will("#{@base_topic}/$state", "lost", retain: true)
37
40
  @mqtt.connect
@@ -107,6 +110,21 @@ module SDN
107
110
  publish("FFFFFF/discover/$settable", "true")
108
111
  publish("FFFFFF/discover/$retained", "false")
109
112
 
113
+ hass_device = {
114
+ name: "Somfy SDN Bridge",
115
+ identifiers: @device_id,
116
+ sw_version: SDN::VERSION
117
+ }
118
+ @mqtt.publish_hass_button("discover",
119
+ command_topic: "#{@base_topic}/FFFFFF/discover/set",
120
+ device: hass_device,
121
+ icon: "mdi:search-add",
122
+ name: "Discover Motors",
123
+ node_id: @device_id,
124
+ object_id: "discover",
125
+ unique_id: "#{@device_id}_discover",
126
+ payload_press: "true")
127
+
110
128
  subscribe_all
111
129
 
112
130
  publish("$state", "ready")
@@ -146,6 +164,14 @@ module SDN
146
164
  motor = nil
147
165
 
148
166
  @mqtt.batch_publish do
167
+ hass_device = {
168
+ identifiers: addr,
169
+ model_id: node_type,
170
+ name: addr,
171
+ via_device: @device_id
172
+ }
173
+ node_id = "#{@device_id}_#{addr}"
174
+
149
175
  publish("#{addr}/$name", addr)
150
176
  publish("#{addr}/$type", node_type.to_s)
151
177
  properties = %w[
@@ -181,10 +207,28 @@ module SDN
181
207
  publish("#{addr}/discover/$format", "discover")
182
208
  publish("#{addr}/discover/$settable", "true")
183
209
  publish("#{addr}/discover/$retained", "false")
210
+ @mqtt.publish_hass_button("discover",
211
+ command_topic: "#{@base_topic}/#{addr}/discover/set",
212
+ device: hass_device,
213
+ icon: "mdi:search-add",
214
+ name: "Rediscover",
215
+ node_id: node_id,
216
+ object_id: "discover",
217
+ payload_press: "true",
218
+ unique_id: "#{node_id}_discover")
184
219
 
185
220
  publish("#{addr}/label/$name", "Node label")
186
221
  publish("#{addr}/label/$datatype", "string")
187
222
  publish("#{addr}/label/$settable", "true")
223
+ @mqtt.publish_hass_text("label",
224
+ command_topic: "#{@base_topic}/#{addr}/label/set",
225
+ device: hass_device,
226
+ entity_category: :config,
227
+ icon: "mdi:rename",
228
+ max: 16,
229
+ name: "Label",
230
+ node_id: node_id,
231
+ unique_id: "#{node_id}_label")
188
232
 
189
233
  publish("#{addr}/state/$name", "Current state of the motor")
190
234
  publish("#{addr}/state/$datatype", "enum")
@@ -216,24 +260,95 @@ module SDN
216
260
  publish("#{addr}/position-percent/$unit", "%")
217
261
  publish("#{addr}/position-percent/$settable", "true")
218
262
 
263
+ @mqtt.publish_hass_cover("motor",
264
+ command_topic: "#{@base_topic}/#{addr}/control/set",
265
+ device: hass_device,
266
+ icon: "mdi:roller-shade",
267
+ name: "Motor",
268
+ node_id: node_id,
269
+ payload_close: "down",
270
+ payload_open: "up",
271
+ payload_stop: "stop",
272
+ position_open: 0,
273
+ position_closed: 100,
274
+ position_topic: "#{@base_topic}/#{addr}/position-percent",
275
+ set_position_topic: "#{@base_topic}/#{addr}/position-percent/set",
276
+ state_topic: "#{@base_topic}/#{addr}/hass-state",
277
+ unique_id: "#{node_id}_motor")
278
+ {
279
+ Wink: "mdi:emoticon-wink",
280
+ Next_IP: "mdi:skip-next",
281
+ Previous_IP: "mdi:skip-previous",
282
+ Refresh: "mdi:refresh"
283
+ }.each do |command, icon|
284
+ @mqtt.publish_hass_button(command.to_s.downcase,
285
+ command_topic: "#{@base_topic}/#{addr}/control/set",
286
+ device: hass_device,
287
+ icon: icon,
288
+ name: command.to_s.sub("_", " "),
289
+ node_id: node_id,
290
+ payload_press: command.to_s.downcase,
291
+ unique_id: "#{node_id}_#{command.to_s.downcase}")
292
+ end
293
+
219
294
  publish("#{addr}/position-pulses/$name", "Position from up limit (in pulses)")
220
295
  publish("#{addr}/position-pulses/$datatype", "integer")
221
296
  publish("#{addr}/position-pulses/$format", "0:65535")
222
297
  publish("#{addr}/position-pulses/$unit", "pulses")
223
298
  publish("#{addr}/position-pulses/$settable", "true")
224
299
 
300
+ @mqtt.publish_hass_number("position-pulses",
301
+ command_topic: "#{@base_topic}/#{addr}/position-pulses/set",
302
+ device: hass_device,
303
+ enabled_by_default: false,
304
+ max: 65_536,
305
+ min: 0,
306
+ name: "Position (Pulses)",
307
+ node_id: node_id,
308
+ object_id: "position-pulses",
309
+ state_topic: "#{@base_topic}/#{addr}/position-pulses",
310
+ step: 10,
311
+ unit_of_measurement: "pulses",
312
+ unique_id: "#{node_id}_position-pulses")
313
+
225
314
  publish("#{addr}/ip/$name", "Intermediate Position")
226
315
  publish("#{addr}/ip/$datatype", "integer")
227
316
  publish("#{addr}/ip/$format", "1:16")
228
317
  publish("#{addr}/ip/$settable", "true")
229
318
  publish("#{addr}/ip/$retained", "false") if node_type == :st50ilt2
230
319
 
320
+ @mqtt.publish_hass_number("ip",
321
+ command_topic: "#{@base_topic}/#{addr}/ip/set",
322
+ device: hass_device,
323
+ name: "Intermediate Position",
324
+ max: 16,
325
+ min: 0,
326
+ node_id: node_id,
327
+ object_id: "ip",
328
+ payload_reset: "",
329
+ state_topic: "#{@base_topic}/#{addr}/ip",
330
+ unique_id: "#{node_id}_ip")
331
+
231
332
  publish("#{addr}/down-limit/$name", "Down limit")
232
333
  publish("#{addr}/down-limit/$datatype", "integer")
233
334
  publish("#{addr}/down-limit/$format", "0:65535")
234
335
  publish("#{addr}/down-limit/$unit", "pulses")
235
336
  publish("#{addr}/down-limit/$settable", "true")
236
337
 
338
+ @mqtt.publish_hass_number("down-limit",
339
+ command_topic: "#{@base_topic}/#{addr}/down-limit/set",
340
+ device: hass_device,
341
+ entity_category: :config,
342
+ icon: "mdi:roller-shade-closed",
343
+ max: 65_536,
344
+ min: 0,
345
+ node_id: node_id,
346
+ payload_reset: "",
347
+ state_topic: "#{@base_topic}/#{addr}/down-limit",
348
+ step: 10,
349
+ unit_of_measurement: "pulses",
350
+ unique_id: "#{node_id}_down-limit")
351
+
237
352
  publish("#{addr}/last-direction/$name", "Direction of last motion")
238
353
  publish("#{addr}/last-direction/$datatype", "enum")
239
354
  publish("#{addr}/last-direction/$format", Message::PostMotorStatus::DIRECTION.keys.join(","))
@@ -245,25 +360,86 @@ module SDN
245
360
  publish("#{addr}/reset/$settable", "true")
246
361
  publish("#{addr}/reset/$retained", "false")
247
362
 
363
+ Message::SetFactoryDefault::RESET.each_key do |key|
364
+ @mqtt.publish_hass_button("reset_#{key}",
365
+ command_topic: "#{@base_topic}/#{addr}/reset/set",
366
+ device: hass_device,
367
+ enabled_by_default: false,
368
+ entity_category: :config,
369
+ name: "Reset #{key.to_s.sub("_", " ")}",
370
+ node_id: node_id,
371
+ payload_press: key,
372
+ unique_id: "#{node_id}_#{key}")
373
+ end
374
+
248
375
  publish("#{addr}/last-action-source/$name", "Source of last action")
249
376
  publish("#{addr}/last-action-source/$datatype", "enum")
250
377
  publish("#{addr}/last-action-source/$format", Message::PostMotorStatus::SOURCE.keys.join(","))
251
378
 
379
+ @mqtt.publish_hass_sensor("last-action-source",
380
+ device: hass_device,
381
+ device_class: :enum,
382
+ entity_category: :diagnostic,
383
+ name: "Source of last action",
384
+ node_id: node_id,
385
+ object_id: "last-action-source",
386
+ options: Message::PostMotorStatus::SOURCE.keys,
387
+ state_topic: "#{@base_topic}/#{addr}/last-action-source",
388
+ unique_id: "#{node_id}_last-action-source")
389
+
252
390
  publish("#{addr}/last-action-cause/$name", "Cause of last action")
253
391
  publish("#{addr}/last-action-cause/$datatype", "enum")
254
392
  publish("#{addr}/last-action-cause/$format", Message::PostMotorStatus::CAUSE.keys.join(","))
255
393
 
394
+ @mqtt.publish_hass_sensor("last-action-cause",
395
+ device: hass_device,
396
+ device_class: :enum,
397
+ entity_category: :diagnostic,
398
+ name: "Cause of last action",
399
+ node_id: node_id,
400
+ object_id: "last-action-cause",
401
+ options: Message::PostMotorStatus::CAUSE.keys,
402
+ state_topic: "#{@base_topic}/#{addr}/last-action-cause",
403
+ unique_id: "#{node_id}_last-action-cause")
404
+
256
405
  publish("#{addr}/up-limit/$name", "Up limit (always = 0)")
257
406
  publish("#{addr}/up-limit/$datatype", "integer")
258
407
  publish("#{addr}/up-limit/$format", "0:65535")
259
408
  publish("#{addr}/up-limit/$unit", "pulses")
260
409
  publish("#{addr}/up-limit/$settable", "true")
261
410
 
411
+ @mqtt.publish_hass_number("up-limit",
412
+ command_topic: "#{@base_topic}/#{addr}/up-limit/set",
413
+ device: hass_device,
414
+ entity_category: :config,
415
+ icon: "mdi:roller-shade-open",
416
+ max: 65_536,
417
+ min: 0,
418
+ name: "Up Limit",
419
+ node_id: node_id,
420
+ payload_reset: "",
421
+ state_topic: "#{@base_topic}/#{addr}/up-limit",
422
+ step: 10,
423
+ unit_of_measurement: "pulses",
424
+ unique_id: "#{node_id}_up-limit")
425
+
262
426
  publish("#{addr}/direction/$name", "Motor rotation direction")
263
427
  publish("#{addr}/direction/$datatype", "enum")
264
428
  publish("#{addr}/direction/$format", "standard,reversed")
265
429
  publish("#{addr}/direction/$settable", "true")
266
430
 
431
+ @mqtt.publish_hass_select("direction",
432
+ command_topic: "#{@base_topic}/#{addr}/direction/set",
433
+ device: hass_device,
434
+ entity_category: :config,
435
+ icon: "mdi:circle-arrows",
436
+ name: "Motor rotation direction",
437
+ node_id: node_id,
438
+ object_id: "direction",
439
+ options: %w[standard reversed],
440
+ state_topic: "#{@base_topic}/#{addr}/direction",
441
+ unique_id: "#{node_id}_direction")
442
+
267
443
  publish("#{addr}/up-speed/$name", "Up speed")
268
444
  publish("#{addr}/up-speed/$datatype", "integer")
269
445
  publish("#{addr}/up-speed/$format", "6:28")
@@ -281,6 +457,21 @@ module SDN
281
457
  publish("#{addr}/slow-speed/$format", "6:28")
282
458
  publish("#{addr}/slow-speed/$unit", "RPM")
283
459
  publish("#{addr}/slow-speed/$settable", "true")
460
+
461
+ %w[Up Slow].each do |speed_type|
462
+ @mqtt.publish_hass_number("#{speed_type.downcase}-speed",
463
+ command_topic: "#{@base_topic}/#{addr}/#{speed_type.downcase}-speed/set",
464
+ device: hass_device,
465
+ entity_category: :config,
466
+ icon: "mdi:car-speed-limiter",
467
+ max: 28,
468
+ min: 6,
469
+ name: "#{speed_type} speed",
470
+ node_id: node_id,
471
+ state_topic: "#{@base_topic}/#{addr}/#{speed_type.downcase}-speed",
472
+ unit_of_measurement: "RPM",
473
+ unique_id: "#{node_id}_#{speed_type.downcase}-speed")
474
+ end
284
475
  end
285
476
 
286
477
  publish("#{addr}/groups/$name", "Group Memberships (comma separated, address must start 0101xx)")
@@ -294,11 +485,41 @@ module SDN
294
485
  publish("#{addr}/ip#{ip}-pulses/$unit", "pulses")
295
486
  publish("#{addr}/ip#{ip}-pulses/$settable", "true")
296
487
 
488
+ @mqtt.publish_hass_number("ip#{ip}-pulses",
489
+ command_topic: "#{@base_topic}/#{addr}/ip#{ip}-pulses/set",
490
+ device: hass_device,
491
+ enabled_by_default: false,
492
+ entity_category: :config,
493
+ max: 65_536,
494
+ min: 0,
495
+ name: "Intermediation Position #{ip} (Pulses)",
496
+ node_id: node_id,
497
+ object_id: "ip#{ip}-pulses",
498
+ payload_reset: "",
499
+ state_topic: "#{@base_topic}/#{addr}/ip#{ip}-pulses",
500
+ step: 10,
501
+ unit_of_measurement: "pulses",
502
+ unique_id: "#{node_id}_ip#{ip}-pulses")
503
+
297
504
  publish("#{addr}/ip#{ip}-percent/$name", "Intermediate Position #{ip}")
298
505
  publish("#{addr}/ip#{ip}-percent/$datatype", "integer")
299
506
  publish("#{addr}/ip#{ip}-percent/$format", "0:100")
300
507
  publish("#{addr}/ip#{ip}-percent/$unit", "%")
301
508
  publish("#{addr}/ip#{ip}-percent/$settable", "true")
509
+
510
+ @mqtt.publish_hass_number("ip#{ip}-percent",
511
+ command_topic: "#{@base_topic}/#{addr}/ip#{ip}-percent/set",
512
+ device: hass_device,
513
+ entity_category: :config,
514
+ max: 100,
515
+ min: 0,
516
+ name: "Intermediation Position #{ip} (Percent)",
517
+ node_id: node_id,
518
+ object_id: "ip#{ip}-percent",
519
+ payload_reset: "",
520
+ state_topic: "#{@base_topic}/#{addr}/ip#{ip}-percent",
521
+ unit_of_measurement: "%",
522
+ unique_id: "#{node_id}_ip#{ip}-percent")
302
523
  end
303
524
 
304
525
  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.1"
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.1
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-03 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: []