mqtt-homie 0.1.0 → 0.1.2

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: 6e92a85d38e5d824f9e40a7afe8ec0bbe51f4a7a483d01fa66ee7765812b5939
4
- data.tar.gz: 56356ede9f137963ff41f26a7cc046e0223c647cec0da5703a57da165f4337ac
3
+ metadata.gz: ccd07988b5eafc7e3387a4a545d986dcea8f1d1355d9b6dd48bf4de2231eda61
4
+ data.tar.gz: 3889c445feaee69b229c8fe8c08d4c619cc0a83eb389f150aba2430c406bb39b
5
5
  SHA512:
6
- metadata.gz: d019acaf6ec7b635bfc50eb53be9d185b40a80926f74b36dbfbd8770ed2325d480cf27e05bf6eaeb5f37007c97ad60d4b5385e27aaba4657074409b9fd21e7ad
7
- data.tar.gz: cb4c85f3c03ea59b28a7f9ecda820a7d03a3f6f1e77cc8e2abde98daabcd21f0dea0bbdda3c9d76939c03b5e4fd80546762928a49de2e586465f62576670b69b
6
+ metadata.gz: 2ed023aa563488d443537cb4ec9fd1656c8b91218bee305883489cb54ed380567c164e7731f3a1a4e7795c0dcb6e1aea6b0cb0b5f5926e8db90d23f47de60aa6
7
+ data.tar.gz: f5bb67c340d10e4413a68e4517383cbbad57b84f410adcd0b1e9f9d1b5be6000da0b73c1561d7e2ebe0fe820724dd88f499dc2739e28c39a5a52fd94fd6fde7f
data/.gitignore CHANGED
@@ -6,6 +6,8 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /vendor/
10
+ /Gemfile.lock
9
11
 
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/README.md CHANGED
@@ -1,68 +1,68 @@
1
- # MQTT::Homie
2
-
3
- A ruby interface for creating a device conforming to the MQTT [Homie] convention.
4
- This gem builds upon the [ruby-mqtt] ruby gem.
5
-
6
- The [Homie] convention defines a standardized way of how IoT devices and services announce themselves and their data to a MQTT broker.
7
-
8
- ## Installation
9
-
10
- Add this line to your application's Gemfile:
11
-
12
- ```ruby
13
- gem 'mqtt-homie'
14
- ```
15
-
16
- And then execute:
17
-
18
- $ bundle
19
-
20
- Or install it yourself as:
21
-
22
- $ gem install mqtt-homie
23
-
24
- ## Quick Start
25
-
26
- ~~~ ruby
27
- require 'rubygems'
28
- require 'mqtt/homie'
29
-
30
- # Set up a device, with a node and properties
31
- device = MQTT::Homie.device_builder(id: 'device', name: 'Device'
32
- localip: '192.168.1.1',
33
- mac: '80:1f:02:cc:15:dd'
34
- ).node(id: "gate", name: "Front gate", type: "Gate")
35
- .property(id: "state", name: "Gate state", enum: [:open, :closed, :opening, :closing], value: :closed)
36
- .property(id: "position", name: "Gate position", datatype: :integer, unit: "%", value: 0)
37
- .property(id: "command", name: "Send gate command", settable: true, enum: [:open, :close]).build
38
-
39
- # Create a client and connect to a MQTT broker
40
- client = MQTT::Homie::Client.new(device: device, host: 'localhost')
41
- client.connect
42
-
43
- # access nodes and properties of the device
44
- node = device.node('gate')
45
- state = node.property('state')
46
- state.value = :open # publishes new state to MQTT
47
-
48
- # listen for changes to properties via the Observer interface
49
- node.property('command').add_observer(self)
50
- ~~~
51
-
52
- ## Overview
53
-
54
- TODO
55
-
56
- ## License
57
-
58
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
59
-
60
- ## Code of Conduct
61
-
62
- Everyone interacting in the Mqtt::Homie project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/mqtt-homie/blob/master/CODE_OF_CONDUCT.md).
63
-
64
-
65
-
66
- [Homie]: https://homieiot.github.io/
67
- [MQTT]: http://www.mqtt.org/
1
+ [![Build Status](https://travis-ci.com/sobakasu/mqtt-homie.svg?branch=develop)](https://travis-ci.com/sobakasu/mqtt-homie)
2
+
3
+ # MQTT::Homie
4
+
5
+ A ruby interface for creating a device conforming to the MQTT [Homie] convention.
6
+ This gem builds upon the [ruby-mqtt] ruby gem.
7
+
8
+ The [Homie] convention defines a standardized way of how IoT devices and services announce themselves and their data to a MQTT broker.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'mqtt-homie'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install mqtt-homie
25
+
26
+ ## Quick Start
27
+
28
+ ~~~ ruby
29
+ require 'rubygems'
30
+ require 'mqtt/homie'
31
+
32
+ # Set up a device, with a node and properties
33
+ device = MQTT::Homie.device_builder(id: 'device', name: 'Device')
34
+ .node(id: "gate", name: "Front gate", type: "Gate")
35
+ .property(id: "state", name: "Gate state", enum: [:open, :closed, :opening, :closing], value: :closed)
36
+ .property(id: "position", name: "Gate position", datatype: :integer, unit: "%", value: 0)
37
+ .property(id: "command", name: "Send gate command", settable: true, enum: [:open, :close]).build
38
+
39
+ # Create a client and connect to a MQTT broker
40
+ client = MQTT::Homie::Client.new(device: device, host: 'localhost')
41
+ client.connect
42
+
43
+ # access nodes and properties of the device
44
+ node = device.node('gate')
45
+ state = node.property('state')
46
+ state.value = :open # publishes new state to MQTT
47
+
48
+ # listen for changes to properties via the Observer interface
49
+ node.property('command').add_observer(self)
50
+ ~~~
51
+
52
+ ## Overview
53
+
54
+ TODO
55
+
56
+ ## License
57
+
58
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
59
+
60
+ ## Code of Conduct
61
+
62
+ Everyone interacting in the Mqtt::Homie project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/mqtt-homie/blob/master/CODE_OF_CONDUCT.md).
63
+
64
+
65
+
66
+ [Homie]: https://homieiot.github.io/
67
+ [MQTT]: http://www.mqtt.org/
68
68
  [ruby-mqtt]: https://github.com/njh/ruby-mqtt
@@ -1,162 +1,173 @@
1
- module MQTT
2
- module Homie
3
- # https://homieiot.github.io/specification/
4
-
5
- class Client
6
- DEFAULT_ROOT_TOPIC = "homie"
7
-
8
- attr_accessor :host, :root_topic
9
- attr_reader :device
10
-
11
- def initialize(options = {})
12
- @device = options[:device]
13
- @host = options[:host]
14
- @root_topic = options[:root_topic] || DEFAULT_ROOT_TOPIC
15
- raise "device required" unless @device
16
-
17
- # observe all node properties so we can publish values when they change
18
- @device.nodes.each do |node|
19
- node.properties.each do |property|
20
- property.add_observer(self)
21
- end
22
- end
23
- end
24
-
25
- def connect
26
- return if connected?
27
-
28
- @device.state = :init
29
- @client = create_mqtt_client
30
- @client.connect
31
-
32
- publish(@device, topic)
33
- publish_statistics
34
-
35
- @threads = []
36
-
37
- # run a thread to publish statistics
38
- @threads << Thread.new { run_statistics }
39
-
40
- # run a thread to listen for settings
41
- @threads << Thread.new { run_set_listener }
42
-
43
- @device.state = :ready
44
- publish_state
45
- end
46
-
47
- def disconnect
48
- @device.state = :disconnected
49
- publish_state
50
-
51
- @client.disconnect
52
- @client = nil
53
-
54
- @threads.each { |i| i[:done] = true }
55
- @threads = []
56
- end
57
-
58
- def topic
59
- @root_topic + "/" + @device.id
60
- end
61
-
62
- def connected?
63
- @device.state == :ready
64
- end
65
-
66
- def update(time, object)
67
- if object.kind_of?(MQTT::Homie::Property)
68
- publish_property_value(object)
69
- end
70
- end
71
-
72
- private
73
-
74
- def create_mqtt_client
75
- client = ::MQTT::Client.new
76
- client.host = @host
77
- client.will_topic = topic + "/$state"
78
- client.will_payload = :lost
79
- client.will_retain = true
80
- client
81
- end
82
-
83
- def run_set_listener
84
- # subscribe to 'set' topics for all settable properties
85
- @device.nodes.each do |node|
86
- node.properties.each do |property|
87
- if property.settable?
88
- set_topic = topic + "/" + node.topic + "/" + property.topic + "/set"
89
- debug("subscribe #{set_topic}")
90
- @client.subscribe(set_topic) if @client
91
- end
92
- end
93
- end
94
-
95
- if @client
96
- @client.get do |topic, message|
97
- debug("received message: #{topic}, message: #{message}")
98
- property = find_property_by_set_topic(topic)
99
- property.value = message if property
100
- break if Thread.current[:done]
101
- end
102
- end
103
- debug("set listener thread exiting")
104
- end
105
-
106
- def run_statistics
107
- while !Thread.current[:done]
108
- publish_statistics
109
- sleep @device.interval
110
- end
111
- debug("statistics thread exiting")
112
- end
113
-
114
- def find_property_by_set_topic(set_topic)
115
- @device.nodes.each do |node|
116
- node.properties.each do |property|
117
- return property if set_topic == topic + "/" + node.topic + "/" + property.topic + "/set"
118
- end
119
- end
120
- nil
121
- end
122
-
123
- def publish_statistics
124
- publish(@device.statistics, topic + "/$stats")
125
- end
126
-
127
- def publish_property_value(property)
128
- node = @device.nodes.find { |i| i.properties.include?(property) }
129
- data = {
130
- property.id => property.value,
131
- }
132
- publish(data, topic + "/" + node.topic)
133
- end
134
-
135
- def publish_state
136
- data = {
137
- "$state" => @device.state,
138
- }
139
- publish(data, topic)
140
- end
141
-
142
- def publish(object, prefix = nil)
143
- data = {}
144
- if object.respond_to?(:homie_attributes)
145
- data = object.homie_attributes
146
- else
147
- data = object
148
- end
149
-
150
- data.each do |k, v|
151
- topic = prefix + "/" + k
152
- debug("mqtt publish #{topic} -> #{v}")
153
- @client.publish(topic, v, true)
154
- end
155
- end
156
-
157
- def debug(message)
158
- MQTT::Homie.debug(message)
159
- end
160
- end
161
- end
162
- end
1
+ module MQTT
2
+ module Homie
3
+ # https://homieiot.github.io/specification/
4
+
5
+ class Client
6
+ DEFAULT_ROOT_TOPIC = "homie"
7
+
8
+ attr_accessor :host, :root_topic
9
+ attr_reader :device
10
+
11
+ def initialize(options = {})
12
+ @device = options[:device]
13
+ @host = options[:host]
14
+ @root_topic = options[:root_topic] || DEFAULT_ROOT_TOPIC
15
+
16
+ raise "device required" unless @device
17
+
18
+ # next version of homie doesn't use stats or firmware details
19
+ @use_stats = true
20
+ if options[:develop]
21
+ @device.use_stats = false
22
+ @device.use_fw = false
23
+ @use_stats = false
24
+ end
25
+
26
+ # observe all node properties so we can publish values when they change
27
+ @device.nodes.each do |node|
28
+ node.properties.each do |property|
29
+ property.add_observer(self)
30
+ end
31
+ end
32
+ end
33
+
34
+ def connect
35
+ return if connected?
36
+
37
+ @device.state = :init
38
+ @client = create_mqtt_client
39
+ @client.connect
40
+
41
+ publish(@device, topic)
42
+ publish_statistics if @use_stats
43
+
44
+ @threads = []
45
+
46
+ # run a thread to publish statistics
47
+ @threads << Thread.new { run_statistics } if @use_stats
48
+
49
+ # run a thread to listen for settings
50
+ @threads << Thread.new { run_set_listener }
51
+
52
+ @device.state = :ready
53
+ publish_state
54
+ end
55
+
56
+ def disconnect
57
+ @device.state = :disconnected
58
+ publish_state
59
+
60
+ @client.disconnect
61
+ @client = nil
62
+
63
+ @threads.each { |i| i[:done] = true }
64
+ @threads = []
65
+ end
66
+
67
+ def topic
68
+ @root_topic + "/" + @device.id
69
+ end
70
+
71
+ def connected?
72
+ @device.state == :ready
73
+ end
74
+
75
+ def update(time, object)
76
+ if object.kind_of?(MQTT::Homie::Property)
77
+ publish_property_value(object)
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def create_mqtt_client
84
+ client = ::MQTT::Client.new
85
+ client.host = @host
86
+ client.will_topic = topic + "/$state"
87
+ client.will_payload = :lost
88
+ client.will_retain = true
89
+ client
90
+ end
91
+
92
+ def run_set_listener
93
+ # subscribe to 'set' topics for all settable properties
94
+ @device.nodes.each do |node|
95
+ node.properties.each do |property|
96
+ if property.settable?
97
+ set_topic = topic + "/" + node.topic + "/" + property.topic + "/set"
98
+ debug("subscribe #{set_topic}")
99
+ @client.subscribe(set_topic) if @client
100
+ end
101
+ end
102
+ end
103
+
104
+ if @client
105
+ @client.get do |topic, message|
106
+ debug("received message: #{topic}, message: #{message}")
107
+ property = find_property_by_set_topic(topic)
108
+ property.value = message if property
109
+ break if Thread.current[:done]
110
+ end
111
+ end
112
+ debug("set listener thread exiting")
113
+ end
114
+
115
+ def run_statistics
116
+ while !Thread.current[:done]
117
+ publish_statistics
118
+
119
+ # halve interval, if we miss a notification then we will be marked as offline
120
+ sleep @device.stats.interval / 2
121
+ end
122
+ debug("statistics thread exiting")
123
+ end
124
+
125
+ def find_property_by_set_topic(set_topic)
126
+ @device.nodes.each do |node|
127
+ node.properties.each do |property|
128
+ return property if set_topic == topic + "/" + node.topic + "/" + property.topic + "/set"
129
+ end
130
+ end
131
+ nil
132
+ end
133
+
134
+ def publish_statistics
135
+ publish(@device.stats, topic + "/$stats")
136
+ end
137
+
138
+ def publish_property_value(property)
139
+ node = @device.nodes.find { |i| i.properties.include?(property) }
140
+ data = {
141
+ property.id => property.value,
142
+ }
143
+ publish(data, topic + "/" + node.topic)
144
+ end
145
+
146
+ def publish_state
147
+ data = {
148
+ "$state" => @device.state,
149
+ }
150
+ publish(data, topic)
151
+ end
152
+
153
+ def publish(object, prefix = nil)
154
+ data = {}
155
+ if object.respond_to?(:homie_attributes)
156
+ data = object.homie_attributes
157
+ else
158
+ data = object
159
+ end
160
+
161
+ data.each do |k, v|
162
+ topic = prefix + "/" + k
163
+ debug("mqtt publish #{topic} -> #{v}")
164
+ @client.publish(topic, v, true)
165
+ end
166
+ end
167
+
168
+ def debug(message)
169
+ MQTT::Homie.debug(message)
170
+ end
171
+ end
172
+ end
173
+ end