somfy_sdn 1.0.6 → 1.0.7

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