Class: Ansible::ZWave::ZWave_Transceiver

Inherits:
Transceiver show all
Includes:
AnsibleCallback
Defined in:
lib/ansible/zwave/zwave_transceiver.rb

Overview

the ZWave transceiver is responsible for communication with the ZWave network uses ozwd, a Thrift wrapper around the OpenZWave library

Constant Summary collapse

ThriftURL_RE =

Thrift URL Regexp

/thrift:\/\/([^:]*)(?::(.*))*/

Instance Attribute Summary collapse

Attributes inherited from Transceiver

#thread

Instance Method Summary collapse

Methods included from AnsibleCallback

#add_callback, #fire_callback, #remove_callback

Methods inherited from Transceiver

#stop

Constructor Details

#initialize(stompURL, thriftURL) ⇒ ZWave_Transceiver

Returns a new instance of ZWave_Transceiver.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 41

def initialize(stompURL, thriftURL)
    raise "Already initialized!" unless Ansible::ZWave::ValueID::transceiver.nil?
    puts "#{self}: initializing" if $DEBUG
    @stompURL, @thriftURL = stompURL, thriftURL
    #@stompMutex = Mutex.new
    @thriftMutex = Mutex.new
    @stomp_ok, @thrift_ok = false, false
    @alive = true
    super()
    # store reference to ourselves to the classes that use us
    Ansible::ZWave::ValueID.transceiver = self
    
    @ValueMonitors = {}
    @ValueMonitorMutex = Mutex.new
end

Instance Attribute Details

#stompURLObject (readonly)

Returns the value of attribute stompURL.



39
40
41
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 39

def stompURL
  @stompURL
end

#thriftURLObject (readonly)

Returns the value of attribute thriftURL.



39
40
41
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 39

def thriftURL
  @thriftURL
end

Instance Method Details

#controller_state(idx) ⇒ Object

handle controller state notifications



385
386
387
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 385

def controller_state(idx)
    puts OpenZWave::ControllerStates[idx].join(': ')
end

#init_stompObject

initialize connection to STOMP server



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 58

def init_stomp
    unless @stomp_ok
        begin
            #puts "init_stomp\n-------------\n\t" +  caller.join("\n\t") + "\n"
            @stompserver = OnStomp::Client.new(@stompURL)
            @stompserver.on_connection_died { |client, con|
                @stomp_ok = false
                puts "STOMP connection died!! sleeping for 3 seconds and then retrying..."
                puts "stack trace: \n\t"<< caller.join("\t\n")
                sleep(3)
                @stompserver.connect
            }
            #
            @stompserver.on_connection_closed { |client, con|
                @stomp_ok = false
                puts "STOMP connection closed!! sleeping for 10 seconds and then retrying..."
                puts "stack trace: \n\t"<< caller.join("\t\n")
                sleep(10)
                @stompserver.connect
            }
            #
            @stompserver.on_connection_established { |client, con|
                puts "STOMP: Connected to broker using protocol version #{con.version}"
                @stomp_ok = true
            }
            @stompserver.connect
        rescue Errno::ECONNREFUSED => e
            @stomp_ok = false
            puts "#{e}"
        end
    end
    return @stompserver
end

#init_thriftObject

initialize connection to THRIFT server



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 102

def init_thrift()
    unless @thrift_ok
        # connect to Thrift server for OpenZWave
        begin
            if md = ThriftURL_RE.match(@thriftURL) then
                host = md[1]
                port = md[2].nil?? 9090: md[2].to_i
                #puts "THRIFT host, port = #{host}:#{port}"
                @thrift_transport = Thrift::BufferedTransport.new(Thrift::Socket.new(host, port))
                @thrift_protocol = Thrift::BinaryProtocol.new(@thrift_transport)
                @thrift_transport.open()
                @manager = ::OpenZWave::RemoteManager::Client.new(@thrift_protocol)
                # fetch all known ValueID's from the server
                @manager.SendAllValues
                @thrift_ok = true
                @thrift_heartbeat = Thread.new{
                    puts "Thrift: New heartbeat thread, #{Thread.current}"
                    # aargh, ugly heartbeat
                    while (@thrift_ok) do
                        sleep(1)
                        #puts 'ping...'
                        manager_send(:GetControllerNodeId, HomeID)
                    end
                    puts "Thrift: heartbeat thread exiting, #{Thread.current}"
                }
            else
                raise "Thrift URL invalid"
            end
        #rescue Thrift::TransportException => e
        rescue Exception => e
            @thrift_ok = false
            puts "#{e}"
        end
    end
    return @manager
end

#managerObject

get handle to OpenZWave::RemoteManager



140
141
142
143
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 140

def manager
    # TODO: add caller watch here, (check for unsynchronized access)
    @thrift_ok ? @manager : init_thrift() 
end

#manager_send(meth, *args) ⇒ Object

the preferred method to access OpenZWave::Manager methods is via this generic call function, which takes care of all the nitty-gritty details such as connection monitoring and thread synchronization



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 148

def manager_send(meth, *args)
    result = nil
    @thriftMutex.synchronize {
        begin
            result = manager.method(meth).call(*args)
        rescue Thrift::TransportException => e
            @thrift_ok = false
            puts "Thrift transport exception, in method #{meth.inspect}"
            puts "--------------------------, callers=\n\t\t" + caller[0..2].join("\n\t\t")
            sleep(1)
            retry
         rescue Exception => e
            @thrift_ok = false
            puts "OpenZWave exception: #{e}, in method #{meth.inspect}"
            puts "--------------------, callers=\n\t\t" + caller[0..2].join("\n\t\t")
        end
    }
    return(result)
end

#notification_AllNodesQueried(nodeId, byte, value) ⇒ Object

All nodes have been queried, so client application can expect complete data.



376
377
378
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 376

def notification_AllNodesQueried(nodeId, byte, value) #
    puts 'TODO'
end

#notification_AwakeNodesQueried(nodeId, byte, value) ⇒ Object

All awake nodes have been queried, so client application can expected complete data for these nodes.



370
371
372
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 370

def notification_AwakeNodesQueried(nodeId, byte, value) 
    puts 'TODO'
end

#notification_DriverFailed(nodeId, byte, value) ⇒ Object

Driver failed to load */



329
330
331
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 329

def notification_DriverFailed(nodeId, byte, value) 
    puts 'TODO'
end

#notification_DriverReady(nodeId, byte, value) ⇒ Object

A driver for a PC Z-Wave controller has been added and is ready to use. The notification will contain the controller’s Home ID, which is needed to call most of the Manager methods.



324
325
326
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 324

def notification_DriverReady(nodeId, byte, value) 
    puts 'TODO'
end

#notification_DriverReset(nodeId, byte, value) ⇒ Object

All nodes and values for this driver have been removed.

This is sent instead of potentially hundreds of individual node and value notifications.



336
337
338
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 336

def notification_DriverReset(nodeId, byte, value) 
    puts 'TODO'
end

#notification_EssentialNodeQueriesComplete(nodeId, byte, value) ⇒ Object

The queries on a node that are essential to its operation have been completed. The node can now handle incoming messages.



347
348
349
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 347

def notification_EssentialNodeQueriesComplete(nodeId, byte, value) 
    #OpenZWave::RefreshedNodes[nodeId] = true
end

#notification_Group(nodeId, byte, value) ⇒ Object

The associations for the node have changed. The application should rebuild any group information it holds about the node.



265
266
267
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 265

def notification_Group(nodeId, byte, value) 
    puts 'TODO'  
end

#notification_MsgComplete(nodeId, byte, value) ⇒ Object

The last message that was sent is now complete.



341
342
343
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 341

def notification_MsgComplete(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeAdded(nodeId, byte, value) ⇒ Object

A new node has been added to OpenZWave’s list. This may be due to a device being added to the Z-Wave network, or because the application is initializing itself.



277
278
279
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 277

def notification_NodeAdded(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeEvent(nodeId, byte, value) ⇒ Object

A node has triggered an event. This is commonly caused when a node sends a Basic_Set command to the controller. The event value is stored in the notification.



305
306
307
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 305

def notification_NodeEvent(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeNaming(nodeId, byte, value) ⇒ Object

One of the node names has changed (name, manufacturer, product).



298
299
300
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 298

def notification_NodeNaming(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeNew(nodeId, byte, value) ⇒ Object

A new node has been found (not already stored in zwcfg*.xml file)



270
271
272
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 270

def notification_NodeNew(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeProtocolInfo(nodeId, byte, value) ⇒ Object

Basic node information has been receievd, such as whether the node is a listening device, a routing device and its baud rate and basic, generic and specific types. It is after this notification that you can call Manager::GetNodeType to obtain a label containing the device description. */



293
294
295
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 293

def notification_NodeProtocolInfo(nodeId, byte, value) 
    puts 'TODO'
end

#notification_NodeQueriesComplete(nodeId, byte, value) ⇒ Object

All the initialisation queries on a node have been completed.



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 352

def notification_NodeQueriesComplete(nodeId, byte, value)
    # node monitor phase 2:
=begin                
    @ValueMonitorMutex.synchronize do
        sleep(2)
        AnsibleValue[:_nodeId => nodeId].each { |val|    
            val.get()
        }
        # all values now should be fresh
        @ValueMonitors[nodeId] = false
        fire_callback(:onMonitorStop)
        puts "==> trigger change monitor ENDED<=="
    end
=end
end

#notification_NodeRemoved(nodeId, byte, value) ⇒ Object

A node has been removed from OpenZWave’s list. This may be due to a device being removed from the Z-Wave network, or because the application is closing.



284
285
286
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 284

def notification_NodeRemoved(nodeId, byte, value) 
    puts 'TODO'    
end

#notification_PollingDisabled(nodeId, byte, value) ⇒ Object

Polling of a node has been successfully turned off by a call to Manager::DisablePoll



311
312
313
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 311

def notification_PollingDisabled(nodeId, byte, value) 
    puts 'TODO'    
end

#notification_PollingEnabled(nodeId, byte, value) ⇒ Object

Polling of a node has been successfully turned on by a call to Manager::EnablePoll



317
318
319
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 317

def notification_PollingEnabled(nodeId, byte, value) 
    puts 'TODO'
end

#notification_ValueAdded(nodeId, byte, value) ⇒ Object

a value has been added to the Z-Wave network



234
235
236
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 234

def notification_ValueAdded(nodeId, byte, value)
        #@@Values[homeID].push(value)
end

#notification_ValueChanged(nodeId, byte, value) ⇒ Object

A node value has been updated from the Z-Wave network.



245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 245

def notification_ValueChanged(nodeId, byte, value)               
    # OpenZWave peculiarity: we got a ValueChanged event, but the value
    # reported by OpenZWave is unchanged. Thus we need to poll the
    # device using :RequestNodeDynamic, wait for NodeQueriesComplete
    # then re-get the value
    #trigger_value_monitor(value)
    #
    # as of r471 ValueChanged behaves correctly
    AnsibleValue[:_nodeId => nodeId, :_gerne => 1].each { |v|
        v.get
    }
end

#notification_ValueRefreshed(nodeId, byte, value) ⇒ Object

A node value has been refreshed from the Z-Wave network.



259
260
261
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 259

def notification_ValueRefreshed(nodeId, byte, value) 
    #value.get unless value.nil?
end

#notification_ValueRemoved(nodeId, byte, value) ⇒ Object

a value has been removed from the Z-Wave network



239
240
241
242
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 239

def notification_ValueRemoved(nodeId, byte, value)            
        #  A node value has been removed from OpenZWave's list.  This only occurs when a node is removed. 
        #@@Values[homeID].delete(value)
end

#runObject

transceiver main loop, runs in its own Ruby thread



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 171

def run
    # 1) subscribe to zwave's notification channel 
    stomp.subscribe '/queue/zwave/monitor' do |msg|
        # Invoked every time the broker delivers a MESSAGE frame for the
        # SUBSCRIBE frame generated by this method call.
        puts "\n------ ZWAVE MESSAGE (#{Time.now}) ------" if $DEBUG
        begin
            value = nil
            # lookup or create ValueID related to Notification
            if msg.headers["HomeID"] and msg.headers["ValueID"] then 
                homeID = msg.headers["HomeID"]
                valueID = msg.headers["ValueID"]
                # sync current HomeID
                h = homeID.to_i(16)
                unless Ansible::ZWave.const_defined?(:HomeID) then
                    if h > 0 then
                        puts "------ SETTING HOME ID: #{homeID}"
                        Ansible::ZWave.const_set("HomeID", h) 
                    end
                end
                # get or create ValueID object
                value = Ansible::ZWave::ValueID.get_or_create(homeID, valueID)
            end
            # bind other notification parameters
            if msg.headers["NotificationType"] then
                node = msg.headers["NotificationNodeId"].to_i(16)
                byte = msg.headers["NotificationByte"].to_i(16)
                notif_type = msg.headers["NotificationType"].to_i(16)
                name, desc = OpenZWave::NotificationTypes[notif_type]
                # dynamic notification handler dispatch mechanism
                if md = /Type_(.*)/.match(name) then
                    handler = "notification_" + md[1]
                    puts "#{handler} (n:#{node}) (b:#{byte}) (#{value})"
                    __send__(handler, node, byte, value) if respond_to?(handler)
                    # fire all notification-related callbacks for the value, if any
                    # e.g. onValueChanged, onValueRefreshed etc.
                    if value.is_a?AnsibleValue then
                        value.fire_callback("on#{md[1]}".to_sym)
                    end
                end
            end
            # controller state change notification mechanism
            if ctrl_state = msg.headers["ControllerState"] then
                controller_state(ctrl_state.to_i(16))
            end
        rescue Exception => e
            puts "ZWaveTransceiver::decode_monitor() exception: #{e}"
            puts "\t"+e.backtrace[0..3].join("\n\t")
        end
    end # do subscribe
    # 2) Sleep forever (how pretty)
    while true #@alive #FIXME
        sleep(1)
    end
end

#stompObject

get handle to stomp server, connect unless already connected caller must unlock @stompMutex when done with stomp



94
95
96
# File 'lib/ansible/zwave/zwave_transceiver.rb', line 94

def stomp
    @stomp_ok ? @stompserver : init_stomp()
end