somfy_sdn 2.2.2 → 2.3.1

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: 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: []