somfy_sdn 1.0.6 → 1.0.7

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: e0d95ac8d2a5acf1ceb0e03051645d8b243f0feffd89da389757df490a85fd6b
4
- data.tar.gz: 86cd04871922fbad94f4b1c7fc5a99da211a1f4cffd398ee69c1cda8ffdcc681
3
+ metadata.gz: 4edace9439651dd89a2c7618a03ae5b6ac9d4ccdf8101ac597eca8f5e66bacf9
4
+ data.tar.gz: 4ecefc5e15483f4b97169f3a0e8939297c1bdd4cdbe909a2388806feaa0bbac7
5
5
  SHA512:
6
- metadata.gz: 430f8b9cab0a245abe7b150203556376de5afbe409a7f5272634880157b831aed32434baf9758e55bb00ba2afbd732f9e3e8b19605e9e2bb51619a2dc5080405
7
- data.tar.gz: b51a53a9cabd2d2efadcff644b7c7bdfce958744228f6092c318d7bfec4447302570e20de8d36a78e09496e3c5a5a3ec1cf6e32bbbeedb29abcc12bad0a27d64
6
+ metadata.gz: e515961e3b633fd7d02879b4d907d76a7bacde2e91f20ddb90893d676e88657f89f912dd645a7e32f9cc166641ae86b816f2dbe96ea1eb702922c51a3bc1b697
7
+ data.tar.gz: 5825179cad7e2dd32af3a9bbca82cf706dbd0e38b10fe0a756241f8e28e954b414a37fe1a80fa0a84e0586f515b379d27a6b0ab013e9a77a71519924ae68e4a0
@@ -1,10 +1,30 @@
1
1
  require 'mqtt'
2
- require 'serialport'
3
- require 'socket'
4
2
  require 'uri'
5
3
  require 'set'
6
4
 
7
5
  module SDN
6
+ Group = Struct.new(:bridge, :addr, :positionpercent, :state) do
7
+ def initialize(*)
8
+ members.each { |k| self[k] = :nil }
9
+ super
10
+ end
11
+
12
+ def publish(attribute, value)
13
+ if self[attribute] != value
14
+ bridge.publish("#{addr}/#{attribute}", value.to_s)
15
+ self[attribute] = value
16
+ end
17
+ end
18
+
19
+ def printed_addr
20
+ Message.print_address(Message.parse_address(addr))
21
+ end
22
+
23
+ def motors
24
+ bridge.motors.select { |addr, motor| motor.groups_string.include?(printed_addr) }.values
25
+ end
26
+ end
27
+
8
28
  Motor = Struct.new(:bridge,
9
29
  :addr,
10
30
  :label,
@@ -91,12 +111,18 @@ module SDN
91
111
  def groups_string
92
112
  @groups.compact.map { |g| SDN::Message.print_address(g) }.sort.uniq.join(',')
93
113
  end
94
- end
114
+
115
+ def group_objects
116
+ groups_string.split(',').map { |addr| bridge.groups[addr.gsub('.', '')] }
117
+ end
118
+ end
95
119
 
96
120
  class MQTTBridge
97
121
  WAIT_TIME = 0.25
98
122
  BROADCAST_WAIT = 5.0
99
123
 
124
+ attr_reader :motors, :groups
125
+
100
126
  def initialize(mqtt_uri, port, device_id: "somfy", base_topic: "homie")
101
127
  @base_topic = "#{base_topic}/#{device_id}"
102
128
  @mqtt = MQTT::Client.new(mqtt_uri)
@@ -104,7 +130,7 @@ module SDN
104
130
  @mqtt.connect
105
131
 
106
132
  @motors = {}
107
- @groups = Set.new
133
+ @groups = {}
108
134
 
109
135
  @mutex = Mutex.new
110
136
  @cond = ConditionVariable.new
@@ -117,8 +143,16 @@ module SDN
117
143
 
118
144
  uri = URI.parse(port)
119
145
  if uri.scheme == "tcp"
146
+ require 'socket'
120
147
  @sdn = TCPSocket.new(uri.host, uri.port)
148
+ elsif uri.scheme == "telnet" || uri.scheme == "rfc2217"
149
+ require 'net/telnet/rfc2217'
150
+ @sdn = Net::Telnet::RFC2217.new('Host' => uri.host,
151
+ 'Port' => uri.port || 23,
152
+ 'baud' => 4800,
153
+ 'parity' => Net::Telnet::RFC2217::ODD)
121
154
  else
155
+ require 'serialport'
122
156
  @sdn = SerialPort.open(port, "baud" => 4800, "parity" => SerialPort::ODD)
123
157
  end
124
158
 
@@ -129,11 +163,11 @@ module SDN
129
163
  message, bytes_read = SDN::Message.parse(buffer.bytes)
130
164
  unless message
131
165
  begin
132
- buffer.concat(@sdn.read_nonblock(1))
166
+ buffer.concat(@sdn.read_nonblock(64 * 1024))
133
167
  next
134
168
  rescue IO::WaitReadable
135
169
  wait = buffer.empty? ? nil : WAIT_TIME
136
- if IO.select([@sdn], nil, nil, wait).nil?
170
+ if @sdn.wait_readable(wait).nil?
137
171
  # timed out; just discard everything
138
172
  puts "timed out reading; discarding buffer: #{buffer.unpack('H*').first}"
139
173
  buffer = ""
@@ -162,6 +196,17 @@ module SDN
162
196
  motor.publish(:positionpercent, message.position_percent)
163
197
  motor.publish(:positionpulses, message.position_pulses)
164
198
  motor.publish(:ip, message.ip)
199
+ motor.group_objects.each do |group|
200
+ positions = group.motors.map(&:positionpercent)
201
+ position = nil
202
+ # calculate an average, but only if we know a position for
203
+ # every shade
204
+ if !positions.include?(:nil) && !positions.include?(nil)
205
+ position = positions.inject(&:+) / positions.length
206
+ end
207
+
208
+ group.publish(:positionpercent, position)
209
+ end
165
210
  when SDN::Message::PostMotorStatus
166
211
  if message.state == :running || motor.state == :running
167
212
  follow_ups << SDN::Message::GetMotorStatus.new(message.src)
@@ -172,6 +217,11 @@ module SDN
172
217
  motor.publish(:last_direction, message.last_direction)
173
218
  motor.publish(:last_action_source, message.last_action_source)
174
219
  motor.publish(:last_action_cause, message.last_action_cause)
220
+ motor.group_objects.each do |group|
221
+ states = group.motors.map(&:state).uniq
222
+ state = states.length == 1 ? states.first : 'mixed'
223
+ group.publish(:state, state)
224
+ end
175
225
  when SDN::Message::PostMotorLimits
176
226
  motor.publish(:uplimit, message.up_limit)
177
227
  motor.publish(:downlimit, message.down_limit)
@@ -194,6 +244,9 @@ module SDN
194
244
  @request_queue.concat(follow_ups)
195
245
  @cond.signal if signal
196
246
  end
247
+ rescue EOFError
248
+ puts "EOF reading"
249
+ exit 2
197
250
  rescue SDN::MalformedMessage => e
198
251
  puts "ignoring malformed message: #{e}" unless e.to_s =~ /issing data/
199
252
  rescue => e
@@ -527,7 +580,7 @@ module SDN
527
580
 
528
581
  motor = Motor.new(self, addr)
529
582
  @motors[addr] = motor
530
- publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.to_a).join(","))
583
+ publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.keys.sort.to_a).join(","))
531
584
 
532
585
  sdn_addr = SDN::Message.parse_address(addr)
533
586
  @mutex.synchronize do
@@ -547,7 +600,7 @@ module SDN
547
600
 
548
601
  def add_group(addr)
549
602
  addr = addr.gsub('.', '')
550
- return if @groups.include?(addr)
603
+ return if @groups.key?(addr)
551
604
 
552
605
  publish("#{addr}/$name", addr)
553
606
  publish("#{addr}/$type", "Shade Group")
@@ -590,7 +643,11 @@ module SDN
590
643
  publish("#{addr}/wink/$settable", "true")
591
644
  publish("#{addr}/wink/$retained", "false")
592
645
 
593
- @groups << addr
646
+ publish("#{addr}/state/$name", "State of the motors; only set if all motors are in the same state")
647
+ publish("#{addr}/state/$datatype", "enum")
648
+ publish("#{addr}/state/$format", SDN::Message::PostMotorStatus::STATE.keys.join(','))
649
+
650
+ @groups[addr] = Group.new(self, addr)
594
651
  publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.to_a).join(","))
595
652
  end
596
653
  end
data/lib/sdn/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module SDN
2
- VERSION = '1.0.6'
2
+ VERSION = '1.0.7'
3
3
  end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: somfy_sdn
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-13 00:00:00.000000000 Z
11
+ date: 2020-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: serialport
14
+ name: mqtt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.3.1
19
+ version: 0.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.3.1
26
+ version: 0.5.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: mqtt
28
+ name: net-telnet-rfc2217
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.5.0
33
+ version: 0.0.2
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: 0.5.0
40
+ version: 0.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: serialport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: byebug
43
57
  requirement: !ruby/object:Gem::Requirement