fluent-plugin-everysense 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -11
- data/fluent-plugin-everysense.gemspec +1 -1
- data/lib/fluent/plugin/everysense_proxy.rb +6 -7
- data/lib/fluent/plugin/in_everysense.rb +39 -19
- data/lib/fluent/plugin/out_everysense.rb +158 -15
- data/tutorial/ja/json_over_http_api_tutorial.md +14 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c77e04a5016b57e7dc362cfcad0fbb920cce6807
|
4
|
+
data.tar.gz: f98ffde7cf600e3653b1ede752fa854623684b55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80d75ae58e2500dab081e641606738e0f966ee0b2fc4a0b65525a96c27ce2db2656b0b8208d5308fc08c9653c7c381f966a2f436f845840e65ef697a13d99c66
|
7
|
+
data.tar.gz: 3028ecae8d2185213aebffebb2d628deda3bc61f2a5dadef59c02181859b9109d10351980185d0478e0436ce7dd510e9cfacec4851681cb7b1ce635e50f1a565
|
data/README.md
CHANGED
@@ -31,8 +31,8 @@ Input Plugin can be used via source directive in the configuration.
|
|
31
31
|
tag tag_name
|
32
32
|
login_name your_login_name
|
33
33
|
password your_password
|
34
|
-
|
35
|
-
polling_interval
|
34
|
+
recipe_id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
|
35
|
+
polling_interval 30
|
36
36
|
</source>
|
37
37
|
```
|
38
38
|
|
@@ -44,30 +44,64 @@ Input Plugin can be used via source directive in the configuration.
|
|
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:
|
48
|
+
|
49
|
+
```
|
50
|
+
{"json":
|
51
|
+
[
|
52
|
+
{"data": {
|
53
|
+
"at": "2016-05-15 12:14:30 +0900",
|
54
|
+
"unit":"degree Celsius",
|
55
|
+
"value":23
|
56
|
+
},
|
57
|
+
"sensor_name":"collection_data_1"
|
58
|
+
},
|
59
|
+
{"data": {
|
60
|
+
"at":"2016-05-15 12:14:30 +0900",
|
61
|
+
"unit":"%RH",
|
62
|
+
"value":30
|
63
|
+
},
|
64
|
+
"sensor_name":"collection_data_2"}
|
65
|
+
]
|
66
|
+
}
|
67
|
+
```
|
68
|
+
|
69
|
+
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.
|
47
70
|
|
48
71
|
### Output Plugin (Fluent::EverySenseOutput)
|
49
72
|
|
50
|
-
Output Plugin can be used via match directive.
|
73
|
+
Output Plugin can be used via match directive. Output Plugin assumes the input format as described above.
|
51
74
|
|
52
75
|
```
|
53
76
|
<match tag_name>
|
54
77
|
@type everysense
|
55
78
|
login_name your_login_name
|
56
79
|
password your_password
|
57
|
-
format json
|
58
80
|
device_id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
|
81
|
+
flush_interval 10
|
82
|
+
aggr_type none
|
83
|
+
<sensor>
|
84
|
+
input_name collection_data_1
|
85
|
+
output_name FESTIVAL_Test1_Sensor
|
86
|
+
type_of_value Integer
|
87
|
+
</sensor>
|
88
|
+
<sensor>
|
89
|
+
input_name collection_data_2
|
90
|
+
output_name FESTIVAL_Test1_Sensor2
|
91
|
+
type_of_value Integer
|
92
|
+
</sensor>
|
59
93
|
</match>
|
60
94
|
```
|
61
95
|
|
62
|
-
- **format** (required): type of formatter can be specified to parse input data
|
63
96
|
- **login_name** (required): login_name to login https://service.every-sense.com/
|
64
97
|
- **password** (required): password for the login_name
|
65
|
-
- **device_id** (required):
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
98
|
+
- **device_id** (required): The target device id to submit sensor data
|
99
|
+
- **flush_interval**: Upload interval (default: 30)
|
100
|
+
- **aggr_type**: Buffered sensor data can be aggregated with specified statistic method. Currently avg (average) and none (without aggregation) can be specified (default: none).
|
101
|
+
- **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.
|
102
|
+
- **input_name**: sensor_name of the input sensor data.
|
103
|
+
- **output_name**: sensor_name of the output sensor data.
|
104
|
+
- **type_of_value**: type of value (default: Integer)
|
71
105
|
|
72
106
|
|
73
107
|
## Contributing
|
@@ -12,12 +12,9 @@ module Fluent
|
|
12
12
|
base.desc 'EverySense API URI'
|
13
13
|
base.config_param :url, :string, :default => 'https://api.every-sense.com:8001/'
|
14
14
|
base.desc 'login_name for EverySense API'
|
15
|
-
base.config_param :login_name, :string
|
15
|
+
base.config_param :login_name, :string
|
16
16
|
base.desc 'password for EverySense API'
|
17
|
-
base.config_param :password, :string
|
18
|
-
base.config_param :device_id, :string, :default => nil
|
19
|
-
base.config_param :recipe_id, :string, :default => nil
|
20
|
-
base.config_param :format, :string, :default => 'json'
|
17
|
+
base.config_param :password, :string
|
21
18
|
base.config_param :limit, :integer, :default => 1000
|
22
19
|
#base.config_param :keep_alive, :integer, :default => 2
|
23
20
|
base.config_param :from, :string, :default => Time.now.iso8601
|
@@ -26,7 +23,7 @@ module Fluent
|
|
26
23
|
end
|
27
24
|
|
28
25
|
def start_proxy
|
29
|
-
$log.debug "start everysense proxy #{@
|
26
|
+
$log.debug "start everysense proxy #{@url}"
|
30
27
|
|
31
28
|
@uri = URI.parse(@url)
|
32
29
|
@https = Net::HTTP.new(@uri.host, @uri.port)
|
@@ -88,6 +85,7 @@ module Fluent
|
|
88
85
|
end
|
89
86
|
|
90
87
|
def put_message(message)
|
88
|
+
$log.debug "put_message: #{message}"
|
91
89
|
put_message_res = @https.request(put_message_request(message))
|
92
90
|
error_handler(put_message_res, "put_message: '#{message}' failed.")
|
93
91
|
end
|
@@ -123,7 +121,7 @@ module Fluent
|
|
123
121
|
get_messages_req.query = URI.encode_www_form(get_messages_params)
|
124
122
|
$log.debug "#{@uri + target_path}?#{URI.encode_www_form(get_messages_params)}"
|
125
123
|
# currently time window is automatically updated
|
126
|
-
@from =
|
124
|
+
@from = Time.now.iso8601
|
127
125
|
get_messages_req
|
128
126
|
end
|
129
127
|
|
@@ -134,6 +132,7 @@ module Fluent
|
|
134
132
|
end
|
135
133
|
get_messages_res = @https.get(get_messages_request)
|
136
134
|
return nil if !error_handler(get_messages_res,"get_messages failed.")
|
135
|
+
$log.debug "get_message: #{get_messages_res.body}"
|
137
136
|
get_messages_res.body
|
138
137
|
end
|
139
138
|
end
|
@@ -5,8 +5,38 @@ module Fluent
|
|
5
5
|
|
6
6
|
Plugin.register_input('everysense', self)
|
7
7
|
|
8
|
+
config_param :format, :string, :default => 'json'
|
9
|
+
|
8
10
|
# currently EverySenseParser is only used by EverySense Plugin
|
9
11
|
# Parser is implemented internally.
|
12
|
+
# received message format of EverySense is as follows
|
13
|
+
#
|
14
|
+
# [
|
15
|
+
# [
|
16
|
+
# {
|
17
|
+
# "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
18
|
+
# "sensor_name": "collection_data_1",
|
19
|
+
# "data_class_name": "AirTemperature",
|
20
|
+
# "data": {
|
21
|
+
# "at": "2016-05-12 21:38:52 UTC",
|
22
|
+
# "memo": null,
|
23
|
+
# "value": 23,
|
24
|
+
# "unit": "degree Celsius"
|
25
|
+
# }
|
26
|
+
# },
|
27
|
+
# {
|
28
|
+
# "farm_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
29
|
+
# "sensor_name": "collection_data_2",
|
30
|
+
# "data_class_name": "AirHygrometer",
|
31
|
+
# "data": {
|
32
|
+
# "at": "2016-05-12 21:38:52 UTC",
|
33
|
+
# "memo": null,
|
34
|
+
# "value": 30,
|
35
|
+
# "unit": "%RH"
|
36
|
+
# }
|
37
|
+
# }
|
38
|
+
# ]
|
39
|
+
# ]
|
10
40
|
class EverySenseParser
|
11
41
|
def initialize(format, parser)
|
12
42
|
case format
|
@@ -25,9 +55,9 @@ module Fluent
|
|
25
55
|
# each message is re-formatted to JSON. After that it is re-parsed
|
26
56
|
# by fluent JSON parser which supports time_format etc. options...
|
27
57
|
def parse(messages)
|
28
|
-
|
58
|
+
#$log.debug messages
|
29
59
|
JSON.parse(messages).map do |message|
|
30
|
-
|
60
|
+
#$log.debug message
|
31
61
|
@parser.parse(message.to_json) do |time, record|
|
32
62
|
yield(time, record)
|
33
63
|
end
|
@@ -39,10 +69,10 @@ module Fluent
|
|
39
69
|
config_param :tag, :string, :default => nil # TODO: mandatory option
|
40
70
|
desc 'Polling interval to get message from EverySense API'
|
41
71
|
config_param :polling_interval, :integer, :default => 60
|
42
|
-
desc '
|
43
|
-
config_param :
|
44
|
-
desc '
|
45
|
-
config_param :
|
72
|
+
desc 'Device ID'
|
73
|
+
config_param :device_id, :string, :default => nil
|
74
|
+
desc 'Recipe ID'
|
75
|
+
config_param :recipe_id, :string, :default => nil
|
46
76
|
|
47
77
|
# Define `router` method of v0.12 to support v0.10 or earlier
|
48
78
|
unless method_defined?(:router)
|
@@ -68,7 +98,6 @@ module Fluent
|
|
68
98
|
while (true)
|
69
99
|
begin
|
70
100
|
messages = get_messages
|
71
|
-
$log.debug "get_messages: #{messages}"
|
72
101
|
emit(messages) if !(messages.nil? || messages.empty?)
|
73
102
|
sleep @polling_interval
|
74
103
|
rescue Exception => e
|
@@ -81,30 +110,21 @@ module Fluent
|
|
81
110
|
end
|
82
111
|
end
|
83
112
|
|
84
|
-
def add_recv_time(record)
|
85
|
-
if @recv_time
|
86
|
-
# recv_time is recorded in ms
|
87
|
-
record.merge({@recv_time_key => Time.now.instance_eval { self.to_i * 1000 + (usec/1000) }})
|
88
|
-
else
|
89
|
-
record
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
113
|
def parse(messages)
|
94
114
|
@everysense_parser.parse(messages) do |time, record|
|
95
115
|
if time.nil?
|
96
116
|
$log.debug "Since time_key field is nil, Fluent::Engine.now is used."
|
97
117
|
time = Fluent::Engine.now
|
98
118
|
end
|
99
|
-
|
100
|
-
{time: time, record:
|
119
|
+
#$log.debug "#{time}, #{record}"
|
120
|
+
{time: time, record: record}
|
101
121
|
end
|
102
122
|
end
|
103
123
|
|
104
124
|
def emit(messages)
|
105
125
|
begin
|
106
126
|
parse(messages).each do |msg|
|
107
|
-
router.emit(@tag, msg[:time], msg[:record])
|
127
|
+
router.emit(@tag, msg[:time], {json: msg[:record]})
|
108
128
|
end
|
109
129
|
rescue Exception => e
|
110
130
|
$log.error :error => e.to_s
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module Fluent
|
2
|
+
# EverySenseOutput
|
3
|
+
# output data to EverySense server
|
4
|
+
# this module assumes the input format follows everysense output specification
|
2
5
|
class EverySenseOutput < BufferedOutput
|
3
6
|
require 'fluent/plugin/everysense_proxy'
|
4
7
|
include EverySenseProxy
|
@@ -7,8 +10,38 @@ module Fluent
|
|
7
10
|
|
8
11
|
# config_param defines a parameter. You can refer a parameter via @path instance variable
|
9
12
|
# Without :default, a parameter is required.
|
10
|
-
|
11
|
-
|
13
|
+
desc 'Device ID'
|
14
|
+
config_param :device_id, :string
|
15
|
+
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"
|
20
|
+
# <sensor> is mandatory option
|
21
|
+
# an example configuraton is shown below
|
22
|
+
#
|
23
|
+
# <sensor>
|
24
|
+
# input_name collection_data_1
|
25
|
+
# output_name FESTIVAL_Test1_Sensor
|
26
|
+
# type_of_value Integer
|
27
|
+
# </sensor>
|
28
|
+
# <sensor>
|
29
|
+
# input_name collection_data_1
|
30
|
+
# output_name FESTIVAL_Test1_Sensor2
|
31
|
+
# type_of_value Integer
|
32
|
+
# </sensor>
|
33
|
+
config_section :sensor, required: true, multi: true do
|
34
|
+
desc 'Input sensor name'
|
35
|
+
config_param :input_name, :string, :default => nil
|
36
|
+
desc 'Output sensor name'
|
37
|
+
config_param :output_name, :string
|
38
|
+
# type_of_value: "Integer", "Float", "String", etc. The same as Ruby Class name.
|
39
|
+
desc 'Type of value'
|
40
|
+
config_param :type_of_value, :string, :default => "Integer"
|
41
|
+
# unit: "degree Celsius", "%RH", ...
|
42
|
+
#desc 'unit'
|
43
|
+
#config_param :unit, :string, :default => nil
|
44
|
+
end
|
12
45
|
|
13
46
|
# This method is called before starting.
|
14
47
|
# 'conf' is a Hash that includes configuration parameters.
|
@@ -16,11 +49,23 @@ module Fluent
|
|
16
49
|
def configure(conf)
|
17
50
|
super
|
18
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
|
59
|
+
else
|
60
|
+
@sensor_hash[sensor.to_h[:input_name]] = sensor.to_h
|
61
|
+
end
|
62
|
+
end
|
63
|
+
$log.debug @sensor_hash.inspect
|
19
64
|
end
|
20
65
|
|
21
66
|
def configure_formatter(conf)
|
22
|
-
|
23
|
-
|
67
|
+
#@formatter = Plugin.new_formatter(@format)
|
68
|
+
#@formatter.configure(conf)
|
24
69
|
end
|
25
70
|
|
26
71
|
# This method is called when starting.
|
@@ -30,25 +75,123 @@ module Fluent
|
|
30
75
|
start_proxy
|
31
76
|
end
|
32
77
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
78
|
+
def format(tag, time, record)
|
79
|
+
$log.debug "tag: #{tag}, time: #{time}, record: #{record}"
|
80
|
+
[tag, time, record].to_msgpack
|
81
|
+
end
|
82
|
+
|
83
|
+
def force_type(value)
|
84
|
+
case @type_of_value
|
85
|
+
when "Integer"
|
86
|
+
return value.to_i
|
87
|
+
when "Float"
|
88
|
+
return value.to_f
|
89
|
+
when "String"
|
90
|
+
return value.to_s
|
37
91
|
else
|
38
|
-
|
92
|
+
return value.to_i
|
39
93
|
end
|
40
94
|
end
|
41
95
|
|
42
|
-
|
43
|
-
|
96
|
+
# output message format of EverySense is as follows
|
97
|
+
#
|
98
|
+
# [
|
99
|
+
# {
|
100
|
+
# "data": {
|
101
|
+
# "at":"2016-04-14 17:15:00 +0900",
|
102
|
+
# "unit":"degree Celsius",
|
103
|
+
# "value":23
|
104
|
+
# },
|
105
|
+
# "sensor_name":"FESTIVAL_Test1_Sensor"
|
106
|
+
# },
|
107
|
+
# {
|
108
|
+
# "data": {
|
109
|
+
# "at":"2016-04-14 17:15:00 +0900",
|
110
|
+
# "unit":"%RH",
|
111
|
+
# "value":30
|
112
|
+
# },
|
113
|
+
# "sensor_name":"FESTIVAL_Test1_Sensor2"
|
114
|
+
# }
|
115
|
+
# ]
|
116
|
+
def transform_sensor_data(sensor_data, output_name) # modify sensor_name
|
117
|
+
{
|
118
|
+
data: {
|
119
|
+
at: Time.parse(sensor_data["data"]["at"]),
|
120
|
+
unit: sensor_data["data"]["unit"],
|
121
|
+
value: force_type(sensor_data["data"]["value"])
|
122
|
+
},
|
123
|
+
sensor_name: output_name
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_output_name_by_name(input_name)
|
128
|
+
@sensor_hash[input_name][:output_name]
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_output_name_by_index(index)
|
132
|
+
@sensor_hash[@sensor_hash.keys[index]][:output_name]
|
133
|
+
end
|
134
|
+
|
135
|
+
def transform_device_data(device_data)
|
136
|
+
if device_data[0]["sensor_name"].nil?
|
137
|
+
# if first input data does not include sensor_name,
|
138
|
+
# 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))
|
142
|
+
end
|
143
|
+
end.compact
|
144
|
+
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
|
+
end
|
152
|
+
|
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)
|
170
|
+
end
|
171
|
+
$log.debug avg_device_data.to_json
|
172
|
+
put_message(avg_device_data.to_json)
|
173
|
+
end
|
174
|
+
|
175
|
+
def max(chunk)
|
176
|
+
# TODO
|
177
|
+
end
|
178
|
+
|
179
|
+
def min(chunk)
|
180
|
+
# TODO
|
44
181
|
end
|
45
182
|
|
46
183
|
def write(chunk)
|
47
|
-
|
48
|
-
|
49
|
-
|
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."
|
50
194
|
end
|
51
|
-
$log.flush
|
52
195
|
end
|
53
196
|
|
54
197
|
# This method is called when shutting down.
|
@@ -6,14 +6,14 @@ Ruby のインタラクティブシェル irb あるいは pry などを利用
|
|
6
6
|
|
7
7
|
https://service.every-sense.com で遊ぶためには最低限以下の用語を抑えておく必要があります。
|
8
8
|
|
9
|
-
- **ファームオーナー**: EverySenseにアップロードするデータを持っている人。デバイスのクラスを使って自分のデバイスを **ファーム** に登録できます。デバイスを登録すると **デバイスのUUID**
|
9
|
+
- **ファームオーナー**: EverySenseにアップロードするデータを持っている人。デバイスのクラスを使って自分のデバイスを **ファーム** に登録できます。デバイスを登録すると **デバイスのUUID** を取得できます。なお、ここで述べているデバイスは、EverySenseサーバ上の仮想デバイスを指します。物理デバイスからのデータアップロード時に取得したデバイスのUUIDを付与することで、物理的なデバイスとEverySenseサーバ上の仮想デバイスを対応づけます。
|
10
10
|
- **レストランオーナー**: EverySenseからデータをダウンロードして活用する人。自分が欲しいデータの条件を **レシピ** として登録し、**レシピのUUID** を取得します。**レシピ** は検索条件で指定した **ファームオーナー** に送ることができます。検索条件はファームオーナーのキーワードから指定できます。
|
11
|
-
- **デバイスベンダー**:
|
12
|
-
- **レシピ**: **レストランオーナー** から **ファームオーナー**
|
13
|
-
- **デバイスのクラス**: スマートフォンのようにデバイスには複数のセンサが搭載されていると想定されています。デバイスにどのような精度のセンサが搭載されているのか、例えば製品ごとに事前にクラスとして登録しておくことができます。**ファームオーナー**
|
14
|
-
- **デバイスとセンサ**: **デバイスのクラス**
|
11
|
+
- **デバイスベンダー**: 物理デバイスの開発元や物理デバイスにとっても詳しいデバイス所有者。デバイスのクラスを定義する権限をもちます。
|
12
|
+
- **レシピ**: **レストランオーナー** から **ファームオーナー** に送られる欲しいデータの条件。**ファームオーナー** は送られてきたレシピに対してデータを送信しても良いかどうか承認処理を行います。必要であれば、さらにレストランオーナー側にも承認プロセスを入れることも可能です。承認されたレシピでは承認した **ファームオーナー** のデバイス(仮想デバイス)またはファーム(複数のデバイスを含むことが可能)に送られたデータを取得することができます。
|
13
|
+
- **デバイスのクラス**: スマートフォンのようにデバイスには複数のセンサが搭載されていると想定されています。デバイスにどのような精度のセンサが搭載されているのか、例えば製品ごとに事前にクラスとして登録しておくことができます。**ファームオーナー** は保持している物理デバイスに対応するクラスを選択して、EverySenseに登録することになります。
|
14
|
+
- **デバイスとセンサ**: **デバイスのクラス** を定義する際には、物理デバイスに接続された **センサ** を登録する必要があります。1つのデバイスに対して複数の **センサ** が取り付けられることが想定されています。**センサ** は無線接続されたものも含むため、センサごとに設置箇所を登録できます。登録時に指定した **センサ** の名称はデータアップロード時に利用するのでメモしておきましょう。
|
15
15
|
|
16
|
-
![EverySense基礎用語の概説図](https://raw.githubusercontent.com/toyokazu/fluent-plugin-everysense/master/tutorial/ja/images/every-sense-overview.png "EverySense基礎用語の概説図
|
16
|
+
![EverySense基礎用語の概説図](https://raw.githubusercontent.com/toyokazu/fluent-plugin-everysense/master/tutorial/ja/images/every-sense-overview.png "EverySense基礎用語の概説図")
|
17
17
|
|
18
18
|
## アカウントの登録
|
19
19
|
|
@@ -83,9 +83,9 @@ session_req.content_type = 'application/json'
|
|
83
83
|
session_res = @http.request(session_req)
|
84
84
|
```
|
85
85
|
|
86
|
-
###
|
86
|
+
### EverySense上の仮想デバイスへのデータのアップロードとダウンロード
|
87
87
|
|
88
|
-
|
88
|
+
次にデバイス(仮想デバイス)へのデータのアップロード、ダウンロードについて試してみるため、テスト用のデバイスを登録してください。以下では仮に TestDevice という名称でデバイスを登録するものとして記述します。なお、デバイスの登録は以下から行えます。
|
89
89
|
|
90
90
|
https://service.every-sense.com/ja/devices
|
91
91
|
|
@@ -98,7 +98,7 @@ https://service.every-sense.com/ja/devices
|
|
98
98
|
|
99
99
|
デバイスのデータのアップロード、ダウンロードをする際にUUIDが必要になります。
|
100
100
|
|
101
|
-
####
|
101
|
+
#### 仮想デバイスへのデータのアップロード
|
102
102
|
|
103
103
|
まずはアップロードから試してみましょう。なんと、アップロードには認証は不要です。ということは、デバイスのUUIDを知っている人は誰でもアップロードできてしまうということですね。皆さんUUIDの取り扱いには注意しましょう。
|
104
104
|
|
@@ -167,8 +167,10 @@ irb(main):023:0> upload_res.body
|
|
167
167
|
upload_req.body = [{"data" => {"at" => Time.now.to_s, "unit" => "degree Celsius", "value" => 23}, "sensor_name" => "FESTIVAL_Test1_Sensor"}, {"data" => {"at" => Time.now.to_s, "unit" => "%RH", "value" => 30}, "sensor_name" => "FESTIVAL_Test1_Sensor2"}].to_json
|
168
168
|
```
|
169
169
|
|
170
|
+
なおEverySenseでは、デバイスから受け取ったデータについて、レシピで指定された取得間隔の中で最初に受け取ったデータを送出するため、受け取ったデータを重複して送出しないように、データを受け取ってから、しばらく後続のデータを待つ仕様になっています。すぐにデータの送出を確認したい場合は、取得間隔の中で複数回データをアップロードしてみてください。
|
170
171
|
|
171
|
-
|
172
|
+
|
173
|
+
#### 仮想デバイスからのデータのダウンロード
|
172
174
|
|
173
175
|
データの取得にはログインID (UUID) + パスワードあるいは、session_keyが要求されます。以下では session_key を利用しています。
|
174
176
|
|
@@ -226,6 +228,8 @@ recipe_data_res.body
|
|
226
228
|
|
227
229
|
取得したデータ中の `"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"` はファームのUUIDを示しています。
|
228
230
|
|
231
|
+
なお、レシピに対してデバイスからデータが届いているかどうかは、レストランオーナーであれば、「登録中のレシピ」でレシピ一覧を表示し、該当レシピ部分を確認するとダウンロードできるデータの件数が表示されます。また、ファームオーナーであれば、「稼働中のオーダー」から該当レシピのオーダー詳細を参照すると、下部のファーム稼働状況のグラフでデータが送信されているかどうかが確認できます。
|
232
|
+
|
229
233
|
|
230
234
|
### session_keyの無効化について
|
231
235
|
|
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.
|
4
|
+
version: 0.0.5
|
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-
|
11
|
+
date: 2016-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|