Class: Xap::Schema::XapBscDevice

Inherits:
XapDevice show all
Defined in:
lib/xap/schema/xap_bsc_device.rb

Overview

Represents an xAP BSC Device. See the xAP Basic Status and Control Schema. www.xapautomation.org/index.php?title=Basic_Status_and_Control_Schema

Instance Attribute Summary

Attributes inherited from XapDevice

#address, #interval, #uid

Instance Method Summary collapse

Methods inherited from XapDevice

#to_s

Constructor Details

#initialize(address, uid, endpoints, interval = 5) ⇒ XapBscDevice

Initializes an XapBscDevice with the given address, uid. Endpoints is an array of hashes containing :State, :Level, :Text, and/or optionally :DisplayText. :State should be a boolean value or nil, :Level should be an array of [numerator, denominator], and :Text and :DisplayText should be Strings. Each input and output block must also have an :endpoint key that contains the endpoint name for the given block and may include a :uid key that contains an integer from 1 to 254. Endpoint names and UIDs must be unique within this device. Output hashes (i.e. those endpoints that can be changed via xAP) are identified by including a :callback key that is a proc to be called with the endpoint hash when any of the output endpoint’s attributes are changed by an incoming xAP message.

See add_endpoint for a summary of endpoint fields.

Example:

XapBscDevice.new XapAddress.new(‘vendor’, ‘dev’, ‘hostname’), Xap.random_uid, [ { :endpoint => ‘Input 1’, :uid => 1, :State => true }, { :endpoint => ‘Output 1’, :State => true, :callback => proc { |ep| puts ‘Output 1’ } } ]



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/xap/schema/xap_bsc_device.rb', line 31

def initialize address, uid, endpoints, interval = 5
	super address, uid, interval

	# TODO: Make endpoints a hash, with what is currently the
	# :endpoint field as the hash key, then store the hash key back
	# into the hash (will simplify calls to XapBscDevice.new, so
	# the user can type => instead of :endpoint)

	@input_count = 0
	@output_count = 0
	@endpoints = {} # Mapping from endpoint name to endpoint hash
	@uids = []
	@outputs = [] # Array containing only output endpoints to simplify handling command messages
	endpoints.each do |ep|
		add_endpoint ep
	end
end

Instance Method Details

#add_endpoint(ep) ⇒ Object

Adds a new endpoint hash to the list of endpoints, generating an xAPBSC.info message if the addition is successful. The endpoint’s name must be unique. UID collision will result in an exception being raised. If the UID is not specified, the lowest available UID will be used.

Summary of endpoint fields: :endpoint - Name of endpoint - mandatory, must be unique when downcased, String :uid - UID of endpoint - optional, must be unique, Fixnum 1-254 :callback - Output change callback - mandatory for outputs, may be nil :State - On/off/? state - mandatory according to xAP BSC spec :Level - numerator / denominator - optional :Text - Stream text - optional (mutually exclusive with :Level according to xAP BSC spec) :DisplayText - UI display text - optional

Example: add_endpoint { :endpoint => ‘Input 1’, :uid => 4, :State => false, :Level => [ 0, 30 ] }



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/xap/schema/xap_bsc_device.rb', line 153

def add_endpoint ep
	unless ep.include?(:endpoint) && ep.include?(:State) && (!ep.include?(:uid) || ep[:uid].is_a?(Fixnum))
		raise 'An endpoint is missing one or more required fields (:endpoint, :State).'
	end

	raise "Duplicate endpoint name #{ep[:endpoint]}." if @endpoints.include? ep[:endpoint].downcase

	ep[:uid] ||= find_free_uid
	raise "Duplicate UID #{ep[:uid]}." if @uids[ep[:uid]]

	# TODO: Additional verification of :Level, :Text, and :DisplayText

	if ep.include? :callback
		@output_count = @output_count + 1
		@outputs << ep
	else
		@input_count = @input_count + 1
	end

	@endpoints[ep[:endpoint].downcase] = ep
	@uids[ep[:uid]] = ep

	send_info ep if @handler
end

#find_free_uidObject

Finds and returns the lowest-available endpoint UID, or nil if there are no free endpoint IDs.



195
196
197
198
199
200
# File 'lib/xap/schema/xap_bsc_device.rb', line 195

def find_free_uid
	for idx in 1..254
		return idx unless @uids[idx]
	end
	nil
end

#get_display_text(endpoint) ⇒ Object

Returns the DisplayText field of the endpoint with the given name.



282
283
284
# File 'lib/xap/schema/xap_bsc_device.rb', line 282

def get_display_text endpoint
	@endpoints[endpoint.downcase][:DisplayText]
end

#get_level(endpoint) ⇒ Object

Returns the Level field of the endpoint with the given name.



231
232
233
# File 'lib/xap/schema/xap_bsc_device.rb', line 231

def get_level endpoint
	@endpoints[endpoint.downcase][:Level]
end

#get_state(endpoint) ⇒ Object

Returns the State field of the endpoint with the given name.



209
210
211
# File 'lib/xap/schema/xap_bsc_device.rb', line 209

def get_state endpoint
	@endpoints[endpoint.downcase][:State]
end

#get_text(endpoint) ⇒ Object

Returns the Text field of the endpoint with the given name.



261
262
263
# File 'lib/xap/schema/xap_bsc_device.rb', line 261

def get_text endpoint
	@endpoints[endpoint.downcase][:Text]
end

#get_uid(endpoint) ⇒ Object

Returns the UID for the endpoint with the given name, or nil if no such endpoint exists.



204
205
206
# File 'lib/xap/schema/xap_bsc_device.rb', line 204

def get_uid endpoint
	@endpoints[endpoint.downcase][:uid]
end

#handler=(handler) ⇒ Object

Assigns the handler that owns this device, then sends the initial state of all input and output blocks as xAPBSC.info messages.



51
52
53
54
# File 'lib/xap/schema/xap_bsc_device.rb', line 51

def handler= handler
	super handler
	announce_endpoints
end

#receive_message(msg) ⇒ Object

Called when a message targeting this device’s address is received.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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
# File 'lib/xap/schema/xap_bsc_device.rb', line 71

def receive_message msg
	if msg.is_a? XapBscCommand
		Xap.log "Command message for #{self}"

		if @output_count > 0
			eps = []
			if msg.target_addr.wildcard?
				@outputs.each do |out|
					eps << out if msg.target_addr.endpoint_match out[:endpoint]
				end
			else
				ep = @endpoints[msg.target_addr.endpoint.downcase]
				eps << ep if ep
			end

			# For each message block, if ID=*, apply the
			# change to all matched endpoints.  If ID!=*,
			# apply the change to the matching endpoint,
			# iff that endpoint is in the eps list.
			msg.each_block do |blk|
				id = blk.id
				if id == nil || id == '*'
					eps.each do |ep|
						update_endpoint ep, blk
					end
				else
					ep = @uids[id.to_i]
					if ep && eps.include?(ep)
						update_endpoint ep, blk
					end
				end
			end
		end

	elsif msg.is_a? XapBscQuery
		Xap.log "Query message for #{self}, target #{msg.target_addr}, wildcard #{msg.target_addr.wildcard?}"

		if msg.target_addr.wildcard?
			@endpoints.each do |name, ep|
				if msg.target_addr.endpoint_match name
					Xap.log "Matching endpoint found: #{ep[:endpoint]}"
					send_info ep
				end
			end
		elsif msg.target_addr.endpoint
			ep = @endpoints[msg.target_addr.endpoint.to_s.downcase]
			if ep
				Xap.log "Matching endpoint found: #{ep[:endpoint]}"
				send_info ep
			else
				Xap.log "No matching endpoint found"
			end
		else
			Xap.log "Error: No endpoint was given in the query"
		end

	elsif msg.is_a? XapBscInfo
		Xap.log "Info message for #{self}"

	elsif msg.is_a? XapBscEvent
		Xap.log "Event message for #{self}"

	end
end

#remove_endpoint(ep) ⇒ Object

Removes the given endpoint, which may be the endpoint hash or name. Doesn’t verify that the endpoint actually exists.



180
181
182
183
184
185
186
# File 'lib/xap/schema/xap_bsc_device.rb', line 180

def remove_endpoint ep
	if ep.is_a? String
		ep = @endpoints[ep.downcase]
	end
	@endpoints.delete ep[:endpoint].downcase
	@uids[ep[:uid]] = nil
end

#set_address(address) ⇒ Object

Sets the xAP address of this virtual device, then sends an xAPBSC.info message for all endpoints.



65
66
67
68
# File 'lib/xap/schema/xap_bsc_device.rb', line 65

def set_address address
	super address
	announce_endpoints
end

#set_display_text(endpoint, text) ⇒ Object

Sets the DisplayText field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/xap/schema/xap_bsc_device.rb', line 289

def set_display_text endpoint, text
	ep = @endpoints[endpoint.downcase]

	old = ep[:DisplayText]
	ep[:DisplayText] = old

	if text != old
		send_event ep
	else
		send_info ep
	end
end

#set_level(endpoint, level) ⇒ Object

Sets the Level field of the endpoint with the given name. If level is an array, then both the numerator and denominator are replaced. If level is a Fixnum, only the numerator is replaced. If the new level is different from the old level, an event message will be generated. Otherwise, an info message will be generated. Error checking is not performed on the level parameter. Pass nil to remove the level from this endpoint.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/xap/schema/xap_bsc_device.rb', line 242

def set_level endpoint, level
	ep = @endpoints[endpoint.downcase]

	old = ep[:Level]
	if level != nil
		level = [ level, ep[:Level][1] ] if level.is_a? Fixnum
		ep[:Level] = level
	else
		ep.delete :Level
	end

	if level != old
		send_event ep
	else
		send_info ep
	end
end

#set_state(endpoint, state) ⇒ Object

Sets the State field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/xap/schema/xap_bsc_device.rb', line 216

def set_state endpoint, state
	raise 'state must be true, false, or nil.' unless state == true || state == false || state == nil

	ep = @endpoints[endpoint.downcase]
	old = ep[:State]
	ep[:State] = state

	if state != old
		send_event ep
	else
		send_info ep
	end
end

#set_text(endpoint, text) ⇒ Object

Sets the Text field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/xap/schema/xap_bsc_device.rb', line 268

def set_text endpoint, text
	ep = @endpoints[endpoint.downcase]

	old = ep[:Text]
	ep[:Text] = old

	if text != old
		send_event ep
	else
		send_info ep
	end
end

#uid=(uid) ⇒ Object

Sets the xAP UID of this virtual device, then sends an xAPBSC.info message for all endpoints.



58
59
60
61
# File 'lib/xap/schema/xap_bsc_device.rb', line 58

def uid= uid
	super uid
	announce_endpoints
end

#uid_endpoint(uid) ⇒ Object

Returns the endpoint having the given UID, if any.



189
190
191
# File 'lib/xap/schema/xap_bsc_device.rb', line 189

def uid_endpoint uid
	@uids[uid]
end