fluent-plugin-everysense 0.0.7 → 0.1.0

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
  SHA1:
3
- metadata.gz: 2c47e532dfd4ce2294d6cd5df989342922b83004
4
- data.tar.gz: 7d88128bf365d6ad28b62a712508c8ead55e3519
3
+ metadata.gz: 3dd0be119277f2fb805c2fad1aa564fc92b45a8b
4
+ data.tar.gz: 2153d7ad6bbf73d36c5426f1cd16cc4b0f3cb3ea
5
5
  SHA512:
6
- metadata.gz: ef5128c21c0355240721c01ed4eb9c0761f520af29ed40f4341ae8c25a917c98f8876393e9506c3ab0d5b3af7bc3b7661349b026af629b9d8cc8076319187f6c
7
- data.tar.gz: 600400a7606d61478a0f35a44be298eca60598cb96e241c7027cfce3554e64982d9b3fa1269b92d9baaaf5b0de184e0f795bad58a0be2e539b690af48e3151c5
6
+ metadata.gz: 79f83a1a7912eee72d7544b331b2909118b1de946f4de2b279a6c4db2403c3aa5bee759fa5bd4a739b3cb1a9e0ff2e3d4f22a1819f91a32697288c87ef13ef4f
7
+ data.tar.gz: c56f8ff65e2fa979c675d93d71f7ea3b20798cb5cb26daa05fa633d4c495075162babe02ef6097f5556a8e7c8e274fe19bd03231ff2f450d7d5d75fb955ec200
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ ## 0.0.8, 0.1.0 (2017-04-01)
2
+
3
+ Features:
4
+ - compatible with fluentd v0.14
5
+ - change license from MIT License to Apache License Version 2.0
6
+
7
+ Deprecations:
8
+
9
+ - deprecated support of fluentd v0.12
data/LICENSE CHANGED
@@ -1,21 +1,191 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2016 Toyokazu Akiyama
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ https://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ Copyright 2016-2017 Toyokazu Akiyama
180
+
181
+ Licensed under the Apache License, Version 2.0 (the "License");
182
+ you may not use this file except in compliance with the License.
183
+ You may obtain a copy of the License at
184
+
185
+ https://www.apache.org/licenses/LICENSE-2.0
186
+
187
+ Unless required by applicable law or agreed to in writing, software
188
+ distributed under the License is distributed on an "AS IS" BASIS,
189
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190
+ See the License for the specific language governing permissions and
191
+ limitations under the License.
data/README.md CHANGED
@@ -20,7 +20,7 @@ gem install fluent-plugin-everysense
20
20
  fluent-plugin-everysense has Input and Output Plugins for EverySense platform.
21
21
 
22
22
 
23
- ### Input Plugin (Fluent::EverySenseInput)
23
+ ### Input Plugin (Fluent::Plugin::EverySenseInput)
24
24
 
25
25
  Input Plugin can receive events from EverySense Server. It can be used via source directive in the configuration.
26
26
 
@@ -44,32 +44,40 @@ Input Plugin can receive events from EverySense Server. It can be used via sourc
44
44
  - **recipe_id** (device_id or recipe_id is required): the target recipe id to obtain input data
45
45
  - **polling_interval**: interval to poll EverySense JSON over HTTP API
46
46
 
47
- Since each device may have multiple sensors, time field is generated when Input Plugin received data from EverySense. It can be used only for inside the fluentd network because the real timestamps for the sensors are recorded inside JSON data and not synchronized to the time field. An example record format of the Input Plugin is as follows:
47
+ Time field is added for every sensor in EverySense because sometimes remotely deployed sensors are integrated into one device. So, time field of the whole device is generated when Input Plugin received data from EverySense. It can be used only for inside the fluentd network. The real timestamp for each sensors is recorded inside JSON data and not synchronized to the time field. An example record format of the Input Plugin is as follows:
48
48
 
49
49
  ```
50
- {"json":
50
+ # device with farm_uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
51
+ {
52
+ "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
53
+ "device":
51
54
  [
52
- { "data": {
55
+ # list of sensors connected to this device
56
+ { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
57
+ "data": {
53
58
  "at": "2016-05-15 12:14:30 +0900",
54
59
  "unit":"degree Celsius",
55
60
  "value":23
56
61
  },
57
- "sensor_name":"collection_data_1"
62
+ "sensor_name":"collection_data_1",
63
+ "data_class_name":"AirTemperature"
58
64
  },
59
- { "data": {
65
+ { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
66
+ "data": {
60
67
  "at":"2016-05-15 12:14:30 +0900",
61
68
  "unit":"%RH",
62
69
  "value":30
63
70
  },
64
- "sensor_name":"collection_data_2"
71
+ "sensor_name":"collection_data_2",
72
+ "data_class_name":"AirHygrometer"
65
73
  }
66
74
  ]
67
75
  }
68
76
  ```
69
77
 
70
- While fluentd record must be a map (Hash), the output of EverySense becomes an array. So, a key ("json") is added to make it as a map.
78
+ While fluentd record must be a map (Hash), the output of a farm, which means a set of sensors in other words a virtual device, becomes an array of sensor data. So, the keys "farm_uuid" and "device" are added to make it as a map.
71
79
 
72
- ### Output Plugin (Fluent::EverySenseOutput)
80
+ ### Output Plugin (Fluent::Plugin::EverySenseOutput)
73
81
 
74
82
  Output Plugin can send events to EverySense server. It can be used via match directive. Output Plugin assumes the input format as described above.
75
83
 
@@ -98,15 +106,14 @@ Output Plugin can send events to EverySense server. It can be used via match dir
98
106
  - **password** (required): password for the login_name
99
107
  - **device_id** (required): The target device id to submit sensor data
100
108
  - **flush_interval**: Upload interval (default: 30)
101
- - **aggr_type**: Buffered sensor data can be aggregated with specified statistic method. Currently avg (average) and none (without aggregation) can be specified (default: none).
102
109
  - **sensor** (required): Output sensor names must be specified by sensor directive. Sensor data with the specified input_name will be uploaded to EverySense server with the output_name. If the input device data includes multiple sensor data, multiple directives can be specified. If sensor_name of input sensor data is not specified in the configuration, that sensor data will be skipped. If the sensor_names of input data are nil, sensor data will be uploaded with the number of sensor directives with the specified output_name.
103
110
  - **input_name**: sensor_name of the input sensor data.
104
111
  - **output_name**: sensor_name of the output sensor data.
105
112
  - **type_of_value**: type of value (default: Integer)
106
113
 
107
- ## Filter Plugin (Fluent::EverySenseFilter)
114
+ ## Filter Plugin (Fluent::Plugin::EverySenseFilter)
108
115
 
109
- Filter Plugin can split an EverySense Server event into multiple fluentd events if it has multiple data. It can be used via filter directive.
116
+ Filter Plugin can split an EverySense Server device data into multiple fluentd events if it has multiple sensor data. It can be used via filter directive.
110
117
 
111
118
  ```
112
119
  <filter tag_name>
@@ -114,54 +121,64 @@ Filter Plugin can split an EverySense Server event into multiple fluentd events
114
121
  </filter>
115
122
  ```
116
123
 
117
- The following input data from EverySense Server will be splitted into multiple fluentd events.
124
+ The following input data from EverySense Server will be split into multiple fluentd events.
125
+
126
+ Target device data with multiple sensors in fluentd event form:
118
127
 
119
128
  ```
120
- {"json":
129
+ {
130
+ "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
131
+ "device":
121
132
  [
122
133
  { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
123
- "sensor_name":"collection_data_1",
124
- "data_class_name":"Illuminance",
125
134
  "data": {
126
- "at":"2016-08-24 00:15:00 UTC",
127
- "value":137.0,
128
- "unit":"lx"
129
- }
135
+ "at": "2016-05-15 12:14:30 +0900",
136
+ "unit":"degree Celsius",
137
+ "value":23
138
+ },
139
+ "sensor_name":"collection_data_1",
140
+ "data_class_name":"AirTemperature"
130
141
  },
131
142
  { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
132
- "sensor_name":"collection_data_2",
133
- "data_class_name":"Location",
134
143
  "data": {
135
- "at":"2016-08-24 00:15:00 UTC",
136
- "values":[138.442062,35.8162422,671.599975],
137
- "unit":"degree"
138
- }
144
+ "at":"2016-05-15 12:14:30 +0900",
145
+ "unit":"%RH",
146
+ "value":30
147
+ },
148
+ "sensor_name":"collection_data_2",
149
+ "data_class_name":"AirHygrometer"
139
150
  }
140
151
  ]
141
152
  }
142
153
  ```
143
154
 
155
+ will be split into the following fluentd events.
156
+
144
157
  ```
145
- { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
158
+ {
159
+ "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
160
+ "data": {
161
+ "at": "2016-05-15 12:14:30 +0900",
162
+ "unit":"degree Celsius",
163
+ "value":23
164
+ },
146
165
  "sensor_name":"collection_data_1",
147
- "data_class_name":"Illuminance",
166
+ "data_class_name":"AirTemperature"
167
+ }
168
+ {
169
+ "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
148
170
  "data": {
149
- "at":"2016-08-24 00:15:00 UTC",
150
- "value":137.0,
151
- "unit":"lx"
152
- }
153
- },
154
- { "farm_uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
171
+ "at":"2016-05-15 12:14:30 +0900",
172
+ "unit":"%RH",
173
+ "value":30
174
+ },
155
175
  "sensor_name":"collection_data_2",
156
- "data_class_name":"Location",
157
- "data": {
158
- "at":"2016-08-24 00:15:00 UTC",
159
- "values":[138.442062,35.8162422,671.599975],
160
- "unit":"degree"
161
- }
176
+ "data_class_name":"AirHygrometer"
162
177
  }
163
178
  ```
164
179
 
180
+ This filter can be used to store data into a time series database, e.g. Elasticsearch.
181
+
165
182
  ## Contributing
166
183
 
167
184
  1. Fork it ( http://github.com/toyokazu/fluent-plugin-everysense/fork )
@@ -173,4 +190,4 @@ The following input data from EverySense Server will be splitted into multiple f
173
190
 
174
191
  ## License
175
192
 
176
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
193
+ The gem is available as open source under the terms of the [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require "rake/testtask"
4
4
  Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
6
  t.libs << "lib"
7
- t.test_files = FileList['test/**/*_test.rb']
7
+ t.test_files = FileList['test/**/test_*.rb']
8
8
  end
9
9
 
10
10
  task :default => :test
@@ -11,7 +11,7 @@ https://www.elastic.co/downloads/elasticsearch
11
11
 
12
12
  Linuxの場合は以下のページにある方法でパッケージ管理システム (apt, yum) を用いてインストールするのが簡単です.
13
13
 
14
- https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-service.html
14
+ https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html
15
15
 
16
16
  Kibanaも同様にパッケージ管理システムを用いてインストールできます.
17
17
 
@@ -19,6 +19,8 @@ https://www.elastic.co/guide/en/kibana/4.5/_upgrading_kibana.html
19
19
 
20
20
  インストールが完了したらサービスを開始します.
21
21
 
22
+ https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-service.html
23
+
22
24
  ```
23
25
  sudo /bin/systemctl daemon-reload
24
26
  sudo /bin/systemctl enable elasticsearch.service
@@ -4,25 +4,27 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "fluent-plugin-everysense"
7
- spec.version = "0.0.7"
7
+ spec.version = "0.1.0"
8
8
  spec.authors = ["Toyokazu Akiyama"]
9
9
  spec.email = ["toyokazu@gmail.com"]
10
10
 
11
11
  spec.summary = %q{Fluent Input/Output plugin for EverySense Framework}
12
12
  spec.description = %q{Fluent Input/Output plugin for EverySense Framework}
13
13
  spec.homepage = "https://github.com/toyokazu/fluent-plugin-everysense"
14
- spec.license = "MIT"
14
+ spec.license = "Apache License Version 2.0"
15
15
 
16
16
  spec.files = `git ls-files`.gsub(/.+images\/[\w\.-]+\n/, "").split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.required_ruby_version = '>= 2.1.0'
21
+ spec.required_ruby_version = '>= 2.2.0'
22
22
 
23
- spec.add_dependency 'fluentd', '~> 0.12.0'
23
+ spec.add_dependency 'fluentd', '~> 0.14.0'
24
+ #spec.add_dependency 'fluentd', '~> 0.12.0'
24
25
  #spec.add_dependency 'fluentd', '>= 0.10.0'
25
26
 
26
- spec.add_development_dependency "bundler", "~> 1.10"
27
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "bundler", "~> 1.14"
28
+ spec.add_development_dependency "rake", "~> 12.0"
29
+ spec.add_development_dependency "test-unit"
28
30
  end
@@ -1,4 +1,4 @@
1
- module Fluent
1
+ module Fluent::Plugin
2
2
  module EverySenseProxy
3
3
  require 'uri'
4
4
  require 'net/http'
@@ -8,22 +8,8 @@ module Fluent
8
8
  class EverySenseProxyError
9
9
  end
10
10
 
11
- def self.included(base)
12
- base.desc 'EverySense API URI'
13
- base.config_param :url, :string, :default => 'https://api.every-sense.com:8001/'
14
- base.desc 'login_name for EverySense API'
15
- base.config_param :login_name, :string
16
- base.desc 'password for EverySense API'
17
- base.config_param :password, :string
18
- base.config_param :limit, :integer, :default => 1000
19
- #base.config_param :keep_alive, :integer, :default => 2
20
- base.config_param :from, :string, :default => Time.now.iso8601
21
- base.config_param :keep, :bool, :default => false
22
- base.config_param :inline, :bool, :default => false
23
- end
24
-
25
11
  def start_proxy
26
- $log.debug "start everysense proxy #{@url}"
12
+ log.debug "start everysense proxy #{@url}"
27
13
 
28
14
  @uri = URI.parse(@url)
29
15
  @https = Net::HTTP.new(@uri.host, @uri.port)
@@ -32,17 +18,17 @@ module Fluent
32
18
  end
33
19
 
34
20
  def shutdown_proxy
35
- $log.debug "shutdown_proxy #{@session_key}"
21
+ log.debug "shutdown_proxy #{@session_key}"
36
22
  delete_session
37
23
  @https.finish() if @https.active?
38
24
  end
39
25
 
40
26
  def error_handler(response, message)
41
27
  if response.code != "200"
42
- $log.error :error => message
43
- $log.debug "code: #{response.code}"
44
- $log.debug "message: #{response.message}"
45
- $log.debug "body: #{response.body}"
28
+ log.error error: message
29
+ log.debug "code: #{response.code}"
30
+ log.debug "message: #{response.message}"
31
+ log.debug "body: #{response.body}"
46
32
  return false
47
33
  end
48
34
  return true
@@ -85,7 +71,7 @@ module Fluent
85
71
  end
86
72
 
87
73
  def put_message(message)
88
- $log.debug "put_message: #{message}"
74
+ log.debug "put_message: #{message}"
89
75
  put_message_res = @https.request(put_message_request(message))
90
76
  error_handler(put_message_res, "put_message: '#{message}' failed.")
91
77
  end
@@ -96,7 +82,7 @@ module Fluent
96
82
  elsif !@recipe_id.nil?
97
83
  return "/recipe_data/#{@recipe_id}.#{@format}"
98
84
  else
99
- raise ConfigError, "device_id or recipe_id must be specified."
85
+ raise Fluent::ConfigError, "device_id or recipe_id must be specified."
100
86
  end
101
87
  end
102
88
 
@@ -104,7 +90,7 @@ module Fluent
104
90
  params = {
105
91
  session_key: @session_key,
106
92
  from: @from,
107
- to: Time.now.iso8601,
93
+ to: @to,
108
94
  limit: @limit
109
95
  }
110
96
  if !@device_id.nil?
@@ -117,22 +103,27 @@ module Fluent
117
103
  end
118
104
 
119
105
  def get_messages_request
106
+ from = Time.parse(@from)
107
+ to = from + @interval
108
+ to = Time.now if to > Time.now
109
+ @to = to.iso8601
120
110
  get_messages_req = @uri + target_path
121
111
  get_messages_req.query = URI.encode_www_form(get_messages_params)
122
- $log.debug "#{@uri + target_path}?#{URI.encode_www_form(get_messages_params)}"
112
+ log.debug "#{get_messages_req}?#{get_messages_req.query}"
123
113
  # currently time window is automatically updated
124
- @from = Time.now.iso8601
114
+ #@from = Time.now.iso8601
115
+ @from = @to
125
116
  get_messages_req
126
117
  end
127
118
 
128
119
  def get_messages
129
120
  if !valid_session?
130
121
  return nil if create_session.nil?
131
- $log.debug "session #{@session_key} created."
122
+ log.debug "session #{@session_key} created."
132
123
  end
133
124
  get_messages_res = @https.get(get_messages_request)
134
125
  return nil if !error_handler(get_messages_res,"get_messages failed.")
135
- $log.debug "get_message: #{get_messages_res.body}"
126
+ log.debug "get_message: #{get_messages_res.body}"
136
127
  get_messages_res.body
137
128
  end
138
129
  end
@@ -1,26 +1,29 @@
1
- module Fluent
1
+ require 'fluent/plugin/output'
2
+ require 'fluent/event'
3
+
4
+ module Fluent::Plugin
2
5
  # EverySenseFilter
3
6
  # Split EverySense data into multiple output entries
4
7
  # to store each datum separately
5
8
  class EverySenseFilter < Filter
6
- Plugin.register_filter('everysense', self)
9
+ Fluent::Plugin.register_filter('everysense', self)
7
10
 
8
11
  def filter_stream(tag, es)
9
- new_es = MultiEventStream.new
12
+ new_es = Fluent::MultiEventStream.new
10
13
  es.each do |time, record|
11
- # puts "filter_everysense: #{record}"
12
- split(time, record, new_es)
14
+ # log.debug "filter_everysense: #{record}"
15
+ split_record(time, record, new_es)
13
16
  end
14
17
  new_es
15
18
  end
16
19
 
17
20
  private
18
21
 
19
- def split(time, record, new_es)
20
- if record[:json].instance_of?(Array)
21
- record[:json].each { |r| new_es.add(time, r) }
22
- else
23
- new_es.add(time, record)
22
+ def split_record(time, record, new_es)
23
+ log.debug "split_record: #{record.inspect}"
24
+ # add each sensor as an event of fluentd
25
+ record[:device].each do |sensor|
26
+ new_es.add(Time.parse(sensor["data"]["at"]), sensor)
24
27
  end
25
28
  end
26
29
  end
@@ -1,14 +1,43 @@
1
- module Fluent
1
+ require 'fluent/plugin/input'
2
+ require 'fluent/event'
3
+ require 'fluent/time'
4
+ require 'fluent/plugin/everysense_proxy'
5
+
6
+ module Fluent::Plugin
2
7
  class EverySenseInput < Input
3
- require 'fluent/plugin/everysense_proxy'
4
8
  include EverySenseProxy
5
9
 
6
- Plugin.register_input('everysense', self)
10
+ Fluent::Plugin.register_input('everysense', self)
11
+
12
+ helpers :timer, :compat_parameters, :parser
7
13
 
8
- config_param :format, :string, :default => 'json'
14
+ desc 'EverySense API URI'
15
+ config_param :url, :string, default: 'https://api.every-sense.com:8001/'
16
+ desc 'login_name for EverySense API'
17
+ config_param :login_name, :string
18
+ desc 'password for EverySense API'
19
+ config_param :password, :string, secret: true
20
+ desc 'The tag of the event.'
21
+ config_param :tag, :string
22
+ desc 'Polling interval to get message from EverySense API (default: 60 seconds)'
23
+ config_param :polling_interval, :integer, default: 60
24
+ desc 'Device ID'
25
+ config_param :device_id, :string, default: nil
26
+ desc 'Recipe ID'
27
+ config_param :recipe_id, :string, default: nil
28
+ desc 'Maximum number of returned entries (default: 1000)'
29
+ config_param :limit, :integer, default: 1000
30
+ desc 'Start time of returned entries (default: now)'
31
+ config_param :from, :string, default: Time.now.iso8601
32
+ desc 'Time interval of returned entries (default: 300 seconds)'
33
+ config_param :interval, :integer, default: 300
34
+ desc 'EverySense API option (keep data at server or not)'
35
+ config_param :keep, :bool, default: false
36
+ desc 'EverySense API option (include farm_uuid inline or not)'
37
+ config_param :inline, :bool, default: false
38
+ desc 'EverySense API option (upload/download data format)'
39
+ config_param :format, :string, default: "json"
9
40
 
10
- # currently EverySenseParser is only used by EverySense Plugin
11
- # Parser is implemented internally.
12
41
  # received message format of EverySense is as follows
13
42
  #
14
43
  # [
@@ -37,47 +66,6 @@ module Fluent
37
66
  # }
38
67
  # ]
39
68
  # ]
40
- class EverySenseParser
41
- def initialize(format, parser)
42
- case format
43
- when 'json'
44
- @parser = parser
45
- return
46
- when 'xml'
47
- raise NotImplementedError, "XML parser is not implemented yet."
48
- else
49
- raise NotImplementedError, "XML parser is not implemented yet."
50
- end
51
- end
52
-
53
- # TODO: parser should be impelented prettier way...
54
- # currently once parse JSON array is parsed and in map loop
55
- # each message is re-formatted to JSON. After that it is re-parsed
56
- # by fluent JSON parser which supports time_format etc. options...
57
- def parse(messages)
58
- #$log.debug messages
59
- JSON.parse(messages).map do |message|
60
- #$log.debug message
61
- @parser.parse(message.to_json) do |time, record|
62
- yield(time, record)
63
- end
64
- end
65
- end
66
- end
67
-
68
- desc 'Tag name assigned to inputs'
69
- config_param :tag, :string, :default => 'everysense'
70
- desc 'Polling interval to get message from EverySense API'
71
- config_param :polling_interval, :integer, :default => 60
72
- desc 'Device ID'
73
- config_param :device_id, :string, :default => nil
74
- desc 'Recipe ID'
75
- config_param :recipe_id, :string, :default => nil
76
-
77
- # Define `router` method of v0.12 to support v0.10 or earlier
78
- unless method_defined?(:router)
79
- define_method("router") { Fluent::Engine }
80
- end
81
69
 
82
70
  def configure(conf)
83
71
  super
@@ -85,55 +73,98 @@ module Fluent
85
73
  end
86
74
 
87
75
  def configure_parser(conf)
88
- @parser = Plugin.new_parser(@format)
89
- @parser.configure(conf)
90
- @everysense_parser = EverySenseParser.new(@format, @parser)
76
+ compat_parameters_convert(conf, :parser)
77
+ parser_config = conf.elements('parse').first
78
+ @parser = parser_create(conf: parser_config)
91
79
  end
92
80
 
93
81
  def start
94
82
  #raise StandardError.new if @tag.nil?
95
83
  super
96
84
  start_proxy
97
- @proxy_thread = Thread.new do
98
- while (true)
99
- begin
100
- messages = get_messages
101
- emit(messages) if !(messages.nil? || messages.empty?)
102
- sleep @polling_interval
103
- rescue Exception => e
104
- $log.error :error => e.to_s
105
- $log.debug(e.backtrace.join("\n"))
106
- #$log.debug_backtrace(e.backtrace)
107
- sleep @polling_interval
108
- end
85
+ timer_execute(:in_everysense, @polling_interval) do
86
+ begin
87
+ messages = get_messages
88
+ emit(messages) if !(messages.nil? || messages.empty?)
89
+ rescue Exception => e
90
+ log.error error: e.to_s
91
+ log.debug_backtrace(e.backtrace)
109
92
  end
110
93
  end
111
94
  end
112
95
 
113
- def parse(messages)
114
- @everysense_parser.parse(messages) do |time, record|
115
- if time.nil?
116
- $log.debug "Since time_key field is nil, Fluent::Engine.now is used."
117
- time = Fluent::Engine.now
118
- end
119
- #$log.debug "#{time}, #{record}"
120
- {time: time, record: record}
96
+ def parse_time(record)
97
+ if record["data"]["at"].nil?
98
+ log.debug "Since time_key field is nil, Fluent::EventTime.now is used."
99
+ Fluent::EventTime.now
100
+ else
101
+ @parser.parse_time(record["data"]["at"])
121
102
  end
122
103
  end
123
104
 
105
+ # Converted record for emission
106
+ # [
107
+ # {"farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
108
+ # "device":
109
+ # [
110
+ # {
111
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
112
+ # "sensor_name": "collection_data_1",
113
+ # "data_class_name": "AirTemperature",
114
+ # "data": {
115
+ # "at": "2016-05-12 21:38:52 UTC",
116
+ # "memo": null,
117
+ # "value": 23,
118
+ # "unit": "degree Celsius"
119
+ # }
120
+ # },
121
+ # {
122
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
123
+ # "sensor_name": "collection_data_2",
124
+ # "data_class_name": "AirHygrometer",
125
+ # "data": {
126
+ # "at": "2016-05-12 21:38:52 UTC",
127
+ # "memo": null,
128
+ # "value": 30,
129
+ # "unit": "%RH"
130
+ # }
131
+ # }
132
+ # ]
133
+ # }
134
+ # ]
124
135
  def emit(messages)
125
136
  begin
126
- parse(messages).each do |msg|
127
- router.emit(@tag, msg[:time], {json: msg[:record]})
137
+ time, record = @parser.parse(messages) do |time, record|
138
+ [time, record]
139
+ end
140
+ #log.debug "time: #{time.inspect}"
141
+ #log.debug "record: #{record.inspect}"
142
+ if record.is_a?(Array) && record[0].is_a?(Array) # Multiple devices case
143
+ mes = Fluent::MultiEventStream.new
144
+ record.each do |single_record|
145
+ # use timestamp of the first sensor (single_record[0])
146
+ mes.add(parse_time(single_record[0]), {
147
+ farm_uuid: single_record[0]["farm_uuid"],
148
+ device: single_record
149
+ })
150
+ end
151
+ router.emit_stream(@tag, mes)
152
+ elsif record.is_a?(Array) && record[0].is_a?(Hash) # Single device case
153
+ # use timestamp of the first sensor (single_record[0])
154
+ router.emit(@tag, parse_time(record[0]), {
155
+ farm_uuid: record[0]["farm_uuid"],
156
+ device: record
157
+ })
158
+ else # The other case
159
+ raise Fluent::Plugin::Parser::ParserError, "Unexpected input format."
128
160
  end
129
161
  rescue Exception => e
130
- $log.error :error => e.to_s
131
- $log.debug_backtrace(e.backtrace)
162
+ log.error error: e.to_s
163
+ log.debug_backtrace(e.backtrace)
132
164
  end
133
165
  end
134
166
 
135
167
  def shutdown
136
- @proxy_thread.kill
137
168
  shutdown_proxy
138
169
  super
139
170
  end
@@ -1,22 +1,33 @@
1
- module Fluent
1
+ require 'fluent/plugin/output'
2
+ require 'fluent/event'
3
+ require 'fluent/time'
4
+ require 'fluent/plugin/everysense_proxy'
5
+
6
+ module Fluent::Plugin
2
7
  # EverySenseOutput
3
8
  # output data to EverySense server
4
9
  # this module assumes the input format follows everysense output specification
5
- class EverySenseOutput < BufferedOutput
6
- require 'fluent/plugin/everysense_proxy'
10
+ class EverySenseOutput < Output
7
11
  include EverySenseProxy
8
12
 
9
- Plugin.register_output('everysense', self)
13
+ Fluent::Plugin.register_output('everysense', self)
14
+
15
+ helpers :compat_parameters, :formatter, :inject
10
16
 
11
17
  # config_param defines a parameter. You can refer a parameter via @path instance variable
12
18
  # Without :default, a parameter is required.
19
+
20
+ desc 'EverySense API URI'
21
+ config_param :url, :string, default: 'https://api.every-sense.com:8001/'
22
+ desc 'login_name for EverySense API'
23
+ config_param :login_name, :string
24
+ desc 'password for EverySense API'
25
+ config_param :password, :string, secret: true
13
26
  desc 'Device ID'
14
27
  config_param :device_id, :string
15
28
  desc 'Flush interval to put message to EverySense API'
16
- config_param :flush_interval, :integer, :default => 30
17
- # aggr_type: "none", "avg", "max", "min"
18
- desc 'Aggregation type'
19
- config_param :aggr_type, :string, :default => "none"
29
+ config_param :flush_interval, :integer, default: 30
30
+ config_param :format, :string, default: "json"
20
31
  # <sensor> is mandatory option
21
32
  # an example configuraton is shown below
22
33
  #
@@ -26,21 +37,21 @@ module Fluent
26
37
  # type_of_value Integer
27
38
  # </sensor>
28
39
  # <sensor>
29
- # input_name collection_data_1
40
+ # input_name collection_data_2
30
41
  # output_name FESTIVAL_Test1_Sensor2
31
42
  # type_of_value Integer
32
43
  # </sensor>
33
- config_section :sensor, required: true, multi: true do
44
+ config_section :sensor, param_name: :sensors, required: true, multi: true do
34
45
  desc 'Input sensor name'
35
- config_param :input_name, :string, :default => nil
46
+ config_param :input_name, :string, default: nil
36
47
  desc 'Output sensor name'
37
48
  config_param :output_name, :string
38
49
  # type_of_value: "Integer", "Float", "String", etc. The same as Ruby Class name.
39
50
  desc 'Type of value'
40
- config_param :type_of_value, :string, :default => "Integer"
51
+ config_param :type_of_value, :string, default: "Integer"
41
52
  # unit: "degree Celsius", "%RH", ...
42
53
  #desc 'unit'
43
- #config_param :unit, :string, :default => nil
54
+ #config_param :unit, :string, default: nil
44
55
  end
45
56
 
46
57
  # This method is called before starting.
@@ -48,24 +59,19 @@ module Fluent
48
59
  # If the configuration is invalid, raise Fluent::ConfigError.
49
60
  def configure(conf)
50
61
  super
51
- configure_formatter(conf)
52
- #@sensor.each do |s|
53
- # $log.debug s.to_h.inspect
54
- #end
55
- @sensor_hash = {}
56
- @sensor.each do |sensor|
57
- if sensor.to_h[:input_name].nil?
58
- @sensor_hash[sensor.to_h[:output_name]] = sensor.to_h
62
+ compat_parameters_convert(conf, :formatter, :inject, :buffer, default_chunk_key: "time")
63
+ formatter_config = conf.elements(name: 'format').first
64
+ @formatter = formatter_create(conf: formatter_config)
65
+ @has_buffer_section = conf.elements(name: 'buffer').size > 0
66
+ @out_sensors = {}
67
+ @sensors.each do |sensor|
68
+ if sensor.input_name.nil?
69
+ @out_sensors[sensor.output_name] = sensor
59
70
  else
60
- @sensor_hash[sensor.to_h[:input_name]] = sensor.to_h
71
+ @out_sensors[sensor.input_name] = sensor
61
72
  end
62
73
  end
63
- $log.debug @sensor_hash.inspect
64
- end
65
-
66
- def configure_formatter(conf)
67
- #@formatter = Plugin.new_formatter(@format)
68
- #@formatter.configure(conf)
74
+ log.debug @out_sensors.inspect
69
75
  end
70
76
 
71
77
  # This method is called when starting.
@@ -75,13 +81,12 @@ module Fluent
75
81
  start_proxy
76
82
  end
77
83
 
78
- def format(tag, time, record)
79
- $log.debug "tag: #{tag}, time: #{time}, record: #{record}"
80
- [tag, time, record].to_msgpack
84
+ def prefer_buffered_processing
85
+ @has_buffer_section
81
86
  end
82
87
 
83
- def force_type(value)
84
- case @type_of_value
88
+ def force_type(value, out_sensor)
89
+ case out_sensor.type_of_value
85
90
  when "Integer"
86
91
  return value.to_i
87
92
  when "Float"
@@ -113,85 +118,119 @@ module Fluent
113
118
  # "sensor_name":"FESTIVAL_Test1_Sensor2"
114
119
  # }
115
120
  # ]
116
- def transform_sensor_data(sensor_data, output_name) # modify sensor_name
121
+ def transform_in_sensor(in_sensor, out_sensor) # modify sensor_name
117
122
  {
118
123
  data: {
119
- at: Time.parse(sensor_data["data"]["at"]),
120
- unit: sensor_data["data"]["unit"],
121
- value: force_type(sensor_data["data"]["value"])
124
+ at: Time.parse(in_sensor["data"]["at"]),
125
+ unit: in_sensor["data"]["unit"],
126
+ value: force_type(in_sensor["data"]["value"], out_sensor)
122
127
  },
123
- sensor_name: output_name
128
+ sensor_name: out_sensor.output_name
124
129
  }
125
130
  end
126
131
 
127
- def get_output_name_by_name(input_name)
128
- @sensor_hash[input_name][:output_name]
132
+ def get_out_sensor_by_name(input_name)
133
+ @out_sensors[input_name]
129
134
  end
130
135
 
131
- def get_output_name_by_index(index)
132
- @sensor_hash[@sensor_hash.keys[index]][:output_name]
136
+ def get_out_sensor_by_index(index)
137
+ @out_sensors[@out_sensors.keys[index]]
133
138
  end
134
139
 
135
- def transform_device_data(device_data)
136
- if device_data[0]["sensor_name"].nil?
140
+ # Assumed input message format is as follows
141
+ #
142
+ # [
143
+ # [
144
+ # {
145
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
146
+ # "sensor_name": "collection_data_1",
147
+ # "data_class_name": "AirTemperature",
148
+ # "data": {
149
+ # "at": "2016-05-12 21:38:52 UTC",
150
+ # "memo": null,
151
+ # "value": 23,
152
+ # "unit": "degree Celsius"
153
+ # }
154
+ # },
155
+ # {
156
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
157
+ # "sensor_name": "collection_data_2",
158
+ # "data_class_name": "AirHygrometer",
159
+ # "data": {
160
+ # "at": "2016-05-12 21:38:52 UTC",
161
+ # "memo": null,
162
+ # "value": 30,
163
+ # "unit": "%RH"
164
+ # }
165
+ # }
166
+ # ]
167
+ # ]
168
+ def transform_in_device(in_device)
169
+ if in_device[0]["sensor_name"].nil?
137
170
  # if first input data does not include sensor_name,
138
171
  # output_names are used in the specified order.
139
- return device_data.map.with_index do |sensor_data, i|
140
- if !get_output_name_by_index(i).nil?
141
- transform_sensor_data(sensor_data, get_output_name_by_index(i))
172
+ return in_device.map.with_index do |in_sensor, i|
173
+ if !get_out_sensor_by_index(i).nil?
174
+ transform_in_sensor(in_sensor, get_out_sensor_by_index(i))
175
+ end
176
+ end.compact
177
+ else
178
+ return in_device.map do |in_sensor|
179
+ #log.debug in_sensor["sensor_name"]
180
+ if @out_sensors.keys.include?(in_sensor["sensor_name"])
181
+ transform_in_sensor(in_sensor, get_out_sensor_by_name(in_sensor["sensor_name"]))
142
182
  end
143
183
  end.compact
144
184
  end
145
- device_data.map do |sensor_data|
146
- #$log.debug sensor_data["sensor_name"]
147
- if @sensor_hash.keys.include?(sensor_data["sensor_name"])
148
- transform_sensor_data(sensor_data, get_output_name_by_name(sensor_data["sensor_name"]))
149
- end
150
- end.compact
151
185
  end
152
186
 
153
- def avg(chunk)
154
- device_data_list = []
155
- chunk.msgpack_each do |tag, time, device_data|
156
- device_data_list << transform_device_data(device_data)
157
- end
158
- avg_device_data = device_data_list[0]
159
- device_data_list[1..-1].each do |device_data|
160
- avg_device_data.each_with_index do |avg_sensor_data, i|
161
- avg_sensor_data[:data][:at].to_i += device_data[i][:data][:at].to_i
162
- avg_sensor_data[:data][:value] += device_data[i][:data][:value]
163
- end
164
- end
165
- avg_device_data.each_with_index do |avg_sensor_data, i|
166
- # average time
167
- avg_sensor_data[:data][:at] = Time.at((avg_sensor_data[:data][:at] / device_data_list.size).to_i)
168
- # average value
169
- avg_sensor_data[:data][:value] = force_type(avg_sensor_data[:data][:value] / device_data_list.size)
187
+ # Emitted record inside fluentd network
188
+ # [
189
+ # {"farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
190
+ # "device":
191
+ # [
192
+ # {
193
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
194
+ # "sensor_name": "collection_data_1",
195
+ # "data_class_name": "AirTemperature",
196
+ # "data": {
197
+ # "at": "2016-05-12 21:38:52 UTC",
198
+ # "memo": null,
199
+ # "value": 23,
200
+ # "unit": "degree Celsius"
201
+ # }
202
+ # },
203
+ # {
204
+ # "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
205
+ # "sensor_name": "collection_data_2",
206
+ # "data_class_name": "AirHygrometer",
207
+ # "data": {
208
+ # "at": "2016-05-12 21:38:52 UTC",
209
+ # "memo": null,
210
+ # "value": 30,
211
+ # "unit": "%RH"
212
+ # }
213
+ # }
214
+ # ]
215
+ # }
216
+ # ]
217
+ def put_event_stream(tag, es)
218
+ es = inject_values_to_event_stream(tag, es)
219
+ es.each do |time, record|
220
+ log.debug "#{tag}, #{record}"
221
+ put_message(@formatter.format(tag, time, transform_in_device(record["device"])))
170
222
  end
171
- $log.debug avg_device_data.to_json
172
- put_message(avg_device_data.to_json)
173
223
  end
174
224
 
175
- def max(chunk)
176
- # TODO
177
- end
178
-
179
- def min(chunk)
180
- # TODO
225
+ def process(tag, es)
226
+ put_event_stream(tag, es)
181
227
  end
182
228
 
183
229
  def write(chunk)
184
- case @aggr_type
185
- when "none"
186
- chunk.msgpack_each do |tag, time, record|
187
- #$log.debug transform_device_data(record["json"]).to_json
188
- put_message(transform_device_data(record["json"]).to_json)
189
- end
190
- when "avg", "max", "min"
191
- method(@aggr_type).call(chunk)
192
- else
193
- raise NotImplementedError, "specified aggr_type is not implemented."
194
- end
230
+ return if chunk.empty?
231
+ tag = chunk.metadata.tag
232
+
233
+ put_event_stream(tag, es)
195
234
  end
196
235
 
197
236
  # This method is called when shutting down.
@@ -9,11 +9,19 @@ https://service.every-sense.com で遊ぶためには最低限以下の用語を
9
9
  - **ファームオーナー**: EverySenseにアップロードするデータを持っている人。デバイスのクラスを使って自分のデバイスを **ファーム** に登録できます。デバイスを登録すると **デバイスのUUID** を取得できます。なお、ここで述べているデバイスは、EverySenseサーバ上の仮想デバイスを指します。物理デバイスからのデータアップロード時に取得したデバイスのUUIDを付与することで、物理的なデバイスとEverySenseサーバ上の仮想デバイスを対応づけます。
10
10
  - **レストランオーナー**: EverySenseからデータをダウンロードして活用する人。自分が欲しいデータの条件を **レシピ** として登録し、**レシピのUUID** を取得します。**レシピ** は検索条件で指定した **ファームオーナー** に送ることができます。検索条件はファームオーナーのキーワードから指定できます。
11
11
  - **デバイスベンダー**: 物理デバイスの開発元や物理デバイスにとっても詳しいデバイス所有者。デバイスのクラスを定義する権限をもちます。
12
- - **レシピ**: **レストランオーナー** から **ファームオーナー** に送られる欲しいデータの条件。**ファームオーナー** は送られてきたレシピに対してデータを送信しても良いかどうか承認処理を行います。必要であれば、さらにレストランオーナー側にも承認プロセスを入れることも可能です。承認されたレシピでは承認した **ファームオーナー** のデバイス(仮想デバイス)またはファーム(複数のデバイスを含むことが可能)に送られたデータを取得することができます。
12
+ - **レシピ**: **レストランオーナー** から **ファームオーナー** に送られる欲しいデータの条件。条件にマッチするデータを出力するデバイス群(**ファーム**)を保持する**ファームオーナー**に**オーダー**として送信されます。**ファームオーナー** は送られてきたオーダーに対してデータを送信しても良いかどうか確認し、承認処理を行います。必要であれば、さらにレストランオーナー側にも承認プロセスを入れることも可能です。承認されたレシピでは承認した **ファームオーナー** のデバイス(仮想デバイス)またはファーム(複数のデバイスを含むことが可能)に送られたデータを取得することができます。
13
13
  - **デバイスのクラス**: スマートフォンのようにデバイスには複数のセンサが搭載されていると想定されています。デバイスにどのような精度のセンサが搭載されているのか、例えば製品ごとに事前にクラスとして登録しておくことができます。**ファームオーナー** は保持している物理デバイスに対応するクラスを選択して、EverySenseに登録することになります。
14
14
  - **デバイスとセンサ**: **デバイスのクラス** を定義する際には、物理デバイスに接続された **センサ** を登録する必要があります。1つのデバイスに対して複数の **センサ** が取り付けられることが想定されています。**センサ** は無線接続されたものも含むため、センサごとに設置箇所を登録できます。登録時に指定した **センサ** の名称はデータアップロード時に利用するのでメモしておきましょう。
15
+ - **ファーム**: **ファームオーナー**は保持している**デバイス**をEverySenseサーバに登録する必要があります。登録時には物理デバイスに対応する仮想デバイスを**デバイスのクラス**を指定して生成します。さらに登録した仮想デバイスから**オーダー**の対象となる**ファーム**を作成します。複数のデバイスを組み合わせて**ファーム**を作成することもできます。
16
+
17
+ ![ファームへのデバイス登録とレシピからのオーダー生成](https://raw.githubusercontent.com/toyokazu/fluent-plugin-everysense/master/tutorial/ja/images/every-sense-overview01.png "ファームへのデバイス登録とレシピからのオーダー生成")
18
+
19
+ 図1: ファームへのデバイス登録とレシピからのオーダー生成
20
+
21
+ ![オーダーの承認とセンサデータの送信](https://raw.githubusercontent.com/toyokazu/fluent-plugin-everysense/master/tutorial/ja/images/every-sense-overview02.png "オーダーの承認とセンサデータの送信")
22
+
23
+ 図2: オーダーの承認とセンサデータの送信
15
24
 
16
- ![EverySense基礎用語の概説図](https://raw.githubusercontent.com/toyokazu/fluent-plugin-everysense/master/tutorial/ja/images/every-sense-overview.png "EverySense基礎用語の概説図")
17
25
 
18
26
  ## アカウントの登録
19
27
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-everysense
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toyokazu Akiyama
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-24 00:00:00.000000000 Z
11
+ date: 2017-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,42 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.12.0
19
+ version: 0.14.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: 0.12.0
26
+ version: 0.14.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.10'
33
+ version: '1.14'
34
34
  type: :development
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: '1.10'
40
+ version: '1.14'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '12.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '12.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: Fluent Input/Output plugin for EverySense Framework
56
70
  email:
57
71
  - toyokazu@gmail.com
@@ -60,6 +74,7 @@ extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
62
76
  - ".gitignore"
77
+ - CHANGELOG.md
63
78
  - Gemfile
64
79
  - LICENSE
65
80
  - README.md
@@ -75,7 +90,7 @@ files:
75
90
  - tutorial/ja/json_over_http_api_tutorial.md
76
91
  homepage: https://github.com/toyokazu/fluent-plugin-everysense
77
92
  licenses:
78
- - MIT
93
+ - Apache License Version 2.0
79
94
  metadata: {}
80
95
  post_install_message:
81
96
  rdoc_options: []
@@ -85,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
100
  requirements:
86
101
  - - ">="
87
102
  - !ruby/object:Gem::Version
88
- version: 2.1.0
103
+ version: 2.2.0
89
104
  required_rubygems_version: !ruby/object:Gem::Requirement
90
105
  requirements:
91
106
  - - ">="
@@ -93,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
108
  version: '0'
94
109
  requirements: []
95
110
  rubyforge_project:
96
- rubygems_version: 2.4.5.1
111
+ rubygems_version: 2.6.8
97
112
  signing_key:
98
113
  specification_version: 4
99
114
  summary: Fluent Input/Output plugin for EverySense Framework