lora-rb 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +28 -0
- data/LICENSE +15 -0
- data/README.md +140 -0
- data/lib/config/config.rb +9 -0
- data/lib/config/private.yml +19 -0
- data/lib/config/private_EXAMPLE.yml +19 -0
- data/lib/core/configuration.rb +27 -0
- data/lib/core/configuration_dynamic.rb +67 -0
- data/lib/generators/install_generator.rb +33 -0
- data/lib/lora-rb.rb +48 -0
- data/lib/lora-rb/base.rb +71 -0
- data/lib/lora-rb/help.rb +16 -0
- data/lib/lora-rb/http/call.rb +54 -0
- data/lib/lora-rb/tls/call.rb +73 -0
- data/lib/vendor/deep_symbolize.rb +60 -0
- data/lib/version.rb +5 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b74cb1cacf8fcf8faaa5a3157968cfe1a0a35f3c
|
4
|
+
data.tar.gz: 0eb50e5238019cea923f4815eb21d6d9e2219a10
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a205809d83d84912b972778bf6328f9a1252b5145ca79bb5c6f4cde174644516abc79af6a0b8cb0adb4ec46d069e0d17ce2440d9ab8bd7f974260254907a1bad
|
7
|
+
data.tar.gz: 3f2a2b319b69ae7bf047267814b69593b6b26147dc72e5ab527ba4ccac77809c2752751001d6805b4620930113b03fabb3f24f6f73bb4832e1852369b432e360
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
v0.5.0 [☰](https://bitbucket.org/fractalgarden/loriot-rb/branches/compare/v0.4.0..v0.5.0) June 27th, 2017
|
2
|
+
------------------------------
|
3
|
+
* Now support http push to send data with an internal http client (only send data, to receive you must have an http server)
|
4
|
+
* Now support the italian lora network Resiot.io
|
5
|
+
|
6
|
+
v0.4.0 [☰](https://bitbucket.org/fractalgarden/loriot-rb/branches/compare/v0.3.0..v0.4.0) April 7th, 2017
|
7
|
+
------------------------------
|
8
|
+
* the method `listen` now accept a block to do something every iteration, moreover, it has been included in the unit test.
|
9
|
+
|
10
|
+
v0.3.0 April 7th, 2017
|
11
|
+
------------------------------
|
12
|
+
* added the configuration in the config.rb
|
13
|
+
* moved sensible data to a yaml accessible dinamically with LoriotRb::Settings
|
14
|
+
* added a rails generator
|
15
|
+
* improved the code organization
|
16
|
+
* improved the readme
|
17
|
+
|
18
|
+
v0.2.0 April 5th, 2017
|
19
|
+
------------------------------
|
20
|
+
* dividing the project into modules based on the connection protocol
|
21
|
+
* new method listen to waiting for data
|
22
|
+
* improved debug option
|
23
|
+
|
24
|
+
v0.1.0 March 10th, 2017
|
25
|
+
------------------------------
|
26
|
+
* Starting project
|
27
|
+
* Connection with tls
|
28
|
+
* Basic unit test
|
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
LoraRb
|
2
|
+
|
3
|
+
Copyright (C) 2017 Marco Mastrodonato, marco.mastrodonato@fractalgarden.com
|
4
|
+
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation, either version 3 of the License.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# IOT at you service with LoraWan and Ruby
|
2
|
+
### Manage your devices inside a Lora Network
|
3
|
+
|
4
|
+
[![Version ](https://badge.fury.io/rb/lora-rb.svg) ](https://rubygems.org/gems/lora-rb)
|
5
|
+
[![Travis CI ](http://img.shields.io/travis/marcomd/lora-rb/master.svg) ](https://travis-ci.org/marcomd/lora-rb)
|
6
|
+
[![Quality ](http://img.shields.io/codeclimate/github/marcomd/lora-rb.svg)](https://codeclimate.com/github/marcomd/lora-rb)
|
7
|
+
|
8
|
+
### What is Lora?
|
9
|
+
|
10
|
+
LoRaWAN is a Low Power Wide Area Network with features that support low-cost, mobile, and secure bi-directional communication for Internet of Things (IoT), machine-to-machine (M2M), and smart city, and industrial applications. LoRaWAN is optimized for low power consumption and is designed to support large networks with millions and millions of devices. Innovative features of LoRaWAN include support for redundant operation, geolocation, low-cost, and low-power - devices can even run on energy harvesting technologies enabling the mobility and ease of use of Internet of Things.
|
11
|
+
|
12
|
+
|
13
|
+
![](/assets/LoraWan_Description.jpg)
|
14
|
+
|
15
|
+
|
16
|
+
## Install
|
17
|
+
|
18
|
+
gem install lora-rb
|
19
|
+
|
20
|
+
To use it in a bundle, add to gem file `gem 'lora-rb'` and run `bundle install`
|
21
|
+
|
22
|
+
## Connection to lora server
|
23
|
+
|
24
|
+
Establish a secure connection to your `Lora Application` on the cloud.
|
25
|
+
|
26
|
+
This version only support the TLS protocol.
|
27
|
+
|
28
|
+
From irb require the library:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'lora-rb'
|
32
|
+
```
|
33
|
+
|
34
|
+
then create an instance
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
lora = LoraClient.new
|
38
|
+
```
|
39
|
+
|
40
|
+
if you are in debug mode you should also receive a json:
|
41
|
+
|
42
|
+
```json
|
43
|
+
{ "hello":"LORIOT.io TLS Server", "version":"1.0.4" }
|
44
|
+
```
|
45
|
+
|
46
|
+
### Send data to a device
|
47
|
+
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
lora.send_cmd(EUI: 'Insert here your EUI code', confirmed: true, data: "0101")
|
51
|
+
```
|
52
|
+
|
53
|
+
Request example:
|
54
|
+
|
55
|
+
```json
|
56
|
+
{ "cmd":"tx", "EUI":"BE7A0000000010B7", "port":40, "confirmed":false, "data":"0101" }
|
57
|
+
```
|
58
|
+
|
59
|
+
Response example:
|
60
|
+
|
61
|
+
```json
|
62
|
+
{ "cmd":"txd", "EUI":"BE7A0000000010B7", "seqdn":114, "seqq":113, "ts":1489133179385 }
|
63
|
+
```
|
64
|
+
|
65
|
+
`"cmd":"txd"` show the data sent to the gateway
|
66
|
+
|
67
|
+
The response could also be:
|
68
|
+
|
69
|
+
```json
|
70
|
+
{ "cmd":"tx", "EUI":"BE7A0000000010CF", "seqdn": 4, "data": "0301", "success": "Data enqueued" }
|
71
|
+
```
|
72
|
+
|
73
|
+
### Receive Data
|
74
|
+
|
75
|
+
To listen your devices:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
lora.listen
|
79
|
+
```
|
80
|
+
|
81
|
+
### Close the connection
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
lora.quit
|
85
|
+
```
|
86
|
+
|
87
|
+
## Compatibility
|
88
|
+
|
89
|
+
Ruby `2.3+`
|
90
|
+
|
91
|
+
## Install
|
92
|
+
|
93
|
+
gem install lora-rb
|
94
|
+
|
95
|
+
To use it in a bundle, add to gem file `gem 'lora-rb'` and run `bundle install`
|
96
|
+
|
97
|
+
## To Do
|
98
|
+
|
99
|
+
- [x] Connect to a device with tls protocol `v0.1.0`
|
100
|
+
- [x] Send data to a device `v0.1.0`
|
101
|
+
- [x] Receive data from a device `v0.2.0`
|
102
|
+
- [x] Organization in modules to support new protocols `v0.2.0`
|
103
|
+
- [x] Add a configuration `v0.3.0`
|
104
|
+
- [x] Add a rails generator `v0.3.0`
|
105
|
+
- [ ] Test with many devices
|
106
|
+
|
107
|
+
## Contributing
|
108
|
+
|
109
|
+
1. Fork it
|
110
|
+
2. Create your feature branch (`git checkout -b my-feature`)
|
111
|
+
3. Commit your changes (`git commit -am 'I made extensive use of all my creativity'`)
|
112
|
+
4. Push to the branch (`git push origin my-feature`)
|
113
|
+
5. Create new Pull Request
|
114
|
+
|
115
|
+
## Testing
|
116
|
+
|
117
|
+
Wide coverage with `xx unit tests` and `xxx assertions`
|
118
|
+
|
119
|
+
To test locally install the development requirements:
|
120
|
+
|
121
|
+
bundle install
|
122
|
+
|
123
|
+
Then execute:
|
124
|
+
|
125
|
+
bundle exec ruby test/unit_test.rb
|
126
|
+
|
127
|
+
Performance tests:
|
128
|
+
|
129
|
+
bundle exec ruby test/performance_test.rb
|
130
|
+
|
131
|
+
|
132
|
+
## Found a bug?
|
133
|
+
|
134
|
+
Please open an issue.
|
135
|
+
|
136
|
+
|
137
|
+
## License
|
138
|
+
|
139
|
+
The GNU Lesser General Public License, version 3.0 (LGPL-3.0)
|
140
|
+
See LICENSE file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
#appid: "BE7A02C1"
|
3
|
+
appid: "a1b2c3d4e5f60001"
|
4
|
+
#token: "zHcD-1ouz3ytyOLE6SMhwA"
|
5
|
+
token: "17e3ce759c444b3977f4adbf9dd0e010"
|
6
|
+
test_eui: "BE7A00000000123C"
|
7
|
+
#foo: add every variable you need and use it with => LoraRb::Settings.foo
|
8
|
+
# bar: sub variable are accessible with hash => LoraRb::Settings.foo[:bar]
|
9
|
+
|
10
|
+
development:
|
11
|
+
<<: *defaults
|
12
|
+
|
13
|
+
test:
|
14
|
+
<<: *defaults
|
15
|
+
|
16
|
+
production:
|
17
|
+
<<: *defaults
|
18
|
+
appid: "YOUR PRODUCTION APP ID"
|
19
|
+
token: "YOUR PRODUCTION TOKEN"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
# Insert your credentials provided by Lora
|
3
|
+
appid:
|
4
|
+
token:
|
5
|
+
# Insert your device for test purpose
|
6
|
+
test_eui:
|
7
|
+
#foo: add every variable you need and use it with => LoraRb::Settings.foo
|
8
|
+
# bar: sub variable are accessible with hash => LoraRb::Settings.foo[:bar]
|
9
|
+
|
10
|
+
development:
|
11
|
+
<<: *defaults
|
12
|
+
|
13
|
+
test:
|
14
|
+
<<: *defaults
|
15
|
+
|
16
|
+
production:
|
17
|
+
<<: *defaults
|
18
|
+
appid:
|
19
|
+
token:
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module LoraRb
|
2
|
+
class Configuration
|
3
|
+
# attr_writer :allow_sign_up
|
4
|
+
|
5
|
+
attr_accessor :protocol, :host, :port, :env
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@protocol = nil
|
9
|
+
@host = nil
|
10
|
+
@port = nil
|
11
|
+
@env = 'development'
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.configuration=(config)
|
21
|
+
@configuration = config
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure
|
25
|
+
yield configuration
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative '../vendor/deep_symbolize'
|
3
|
+
|
4
|
+
module LoraRb
|
5
|
+
# we don't want to instantiate this class - it's a singleton,
|
6
|
+
# so just keep it as a self-extended module
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Appdata provides a basic single-method DSL with .parameter method
|
10
|
+
# being used to define a set of available settings.
|
11
|
+
# This method takes one or more symbols, with each one being
|
12
|
+
# a name of the configuration option.
|
13
|
+
def parameter(*names)
|
14
|
+
names.each do |name|
|
15
|
+
attr_accessor name
|
16
|
+
|
17
|
+
# For each given symbol we generate accessor method that sets option's
|
18
|
+
# value being called with an argument, or returns option's current value
|
19
|
+
# when called without arguments
|
20
|
+
define_method name do |*values|
|
21
|
+
value = values.first
|
22
|
+
value ? self.send("#{name}=", value) : instance_variable_get("@#{name}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# And we define a wrapper for the configuration block, that we'll use to set up
|
28
|
+
# our set of options
|
29
|
+
def configure_on_the_fly(&block)
|
30
|
+
instance_eval &block
|
31
|
+
end
|
32
|
+
|
33
|
+
module Settings
|
34
|
+
# again - it's a singleton, thus implemented as a self-extended module
|
35
|
+
extend self
|
36
|
+
|
37
|
+
@_settings = {}
|
38
|
+
attr_reader :_settings
|
39
|
+
|
40
|
+
# This is the main point of entry - we call Settings.load! and provide
|
41
|
+
# a name of the file to read as it's argument. We can also pass in some
|
42
|
+
# options, but at the moment it's being used to allow per-environment
|
43
|
+
# overrides in Rails
|
44
|
+
def load!(filename, options = {})
|
45
|
+
newsets = YAML::load_file(filename)
|
46
|
+
newsets.extend DeepSymbolizable
|
47
|
+
newsets = newsets.deep_symbolize
|
48
|
+
newsets = newsets[options[:env].to_sym] if options[:env] && \
|
49
|
+
newsets[options[:env].to_sym]
|
50
|
+
deep_merge!(@_settings, newsets)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Deep merging of hashes
|
54
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
55
|
+
def deep_merge!(target, data)
|
56
|
+
merger = proc{|key, v1, v2|
|
57
|
+
Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
58
|
+
target.merge! data, &merger
|
59
|
+
end
|
60
|
+
|
61
|
+
def method_missing(name, *args, &block)
|
62
|
+
@_settings[name.to_sym] ||
|
63
|
+
fail(NoMethodError, "unknown configuration root #{name}", caller)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module LoraRb
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
desc 'Installs LoraRb configuration files'
|
7
|
+
source_root File.expand_path('../../', __FILE__)
|
8
|
+
# class_option :protocol, :type => :string, :default => 'tls', :desc => "Choose your favorite emulator [tls]"
|
9
|
+
|
10
|
+
def copy_to_local
|
11
|
+
# copy_file '../config/settings.yml', 'config/lora_rb.yml'
|
12
|
+
copy_file '../config/private_EXAMPLE.yml', 'config/lora_rb_private.yml'
|
13
|
+
file = 'config/initializers/lora-rb.rb'
|
14
|
+
copy_file '../config/config.rb', file
|
15
|
+
append_file file do
|
16
|
+
<<-FILE.gsub(/^ /, '')
|
17
|
+
connection_protocol = LoraRb.configuration.protocol
|
18
|
+
raise 'Define your protocol in the configuration file!' unless connection_protocol
|
19
|
+
raise "Connection protocol #{connection_protocol} not recognized!" unless %w(tls).include?(connection_protocol.to_s)
|
20
|
+
require "lora-rb/#{connection_protocol}/call"
|
21
|
+
LoraClient.include LoraRb::Call
|
22
|
+
|
23
|
+
%w(lora_rb_private.yml).each do |file|
|
24
|
+
filepath = File.join(Rails.root,'config',file)
|
25
|
+
LoraRb::Settings.load!(filepath,env: LoraRb.configuration.env) if File.exist? filepath
|
26
|
+
end
|
27
|
+
raise 'Insert your secret data to login on the lora cloud!' unless LoraRb::Settings.appid.present? && LoraRb::Settings.token.present?
|
28
|
+
FILE
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/lora-rb.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# LoraRb
|
2
|
+
# Copyright (C) 2017 Marco Mastrodonato, marco.mastrodonato@fractalgarden.com
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'json'
|
17
|
+
require 'socket'
|
18
|
+
require 'openssl'
|
19
|
+
require 'http'
|
20
|
+
|
21
|
+
require_relative 'core/configuration'
|
22
|
+
require_relative 'core/configuration_dynamic'
|
23
|
+
|
24
|
+
module LoraRb
|
25
|
+
def self.root
|
26
|
+
File.expand_path('../..', __FILE__)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
require_relative 'version'
|
31
|
+
require_relative 'lora-rb/base'
|
32
|
+
|
33
|
+
unless defined?(Rails)
|
34
|
+
require_relative 'config/config'
|
35
|
+
|
36
|
+
connection_protocol = LoraRb.configuration.protocol
|
37
|
+
raise "Define your protocol in the configuration file!" unless connection_protocol
|
38
|
+
raise "Connection protocol #{connection_protocol} not recognized!" unless %w(tls http).include?(connection_protocol.to_s)
|
39
|
+
require_relative "lora-rb/#{connection_protocol}/call"
|
40
|
+
LoraClient.include LoraRb::Call
|
41
|
+
|
42
|
+
%w(private.yml).each do |file|
|
43
|
+
filepath = File.join(LoraRb.root, 'lib', 'config', file)
|
44
|
+
LoraRb::Settings.load!(filepath, env: LoraRb.configuration.env) if File.exist? filepath
|
45
|
+
end
|
46
|
+
raise "Insert your secret data to login on the lora cloud!" if LoraRb::Settings.appid.nil? || LoraRb::Settings.token.nil?
|
47
|
+
end
|
48
|
+
|
data/lib/lora-rb/base.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'help'
|
2
|
+
|
3
|
+
###################################
|
4
|
+
# An interface to Lora Service from Ruby
|
5
|
+
###################################
|
6
|
+
class LoraClient
|
7
|
+
include LoraRb::Base
|
8
|
+
|
9
|
+
# Establish a secure connection to your account on the cloud
|
10
|
+
def initialize(options = {})
|
11
|
+
options = { debug: false,
|
12
|
+
token: LoraRb::Settings.token,
|
13
|
+
appid: LoraRb::Settings.appid,
|
14
|
+
host: LoraRb.configuration.host,
|
15
|
+
port: LoraRb.configuration.port }.merge(options)
|
16
|
+
@debug = options[:debug]
|
17
|
+
@token = options[:token]
|
18
|
+
@appid = options[:appid]
|
19
|
+
|
20
|
+
welcome_response = sub_initialize(options)
|
21
|
+
|
22
|
+
raise("Lora-rb: Cannot connect to host #{options[:host]}:#{options[:port]}") unless welcome_response.key?('hello')
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Send the request to device
|
27
|
+
def send_cmd(options = {})
|
28
|
+
options = { debug: @debug,
|
29
|
+
token: @token,
|
30
|
+
appid: @appid,
|
31
|
+
cmd: "tx",
|
32
|
+
eui: nil,
|
33
|
+
port: 40,
|
34
|
+
confirmed: false,
|
35
|
+
data: "0301"}.merge(options)
|
36
|
+
|
37
|
+
raise("Eui is blank! Should i guess your device?") unless options[:eui]
|
38
|
+
response = sub_send_cmd(options)
|
39
|
+
puts "#{Time.now} Cmd response: #{response}" if options[:debug]
|
40
|
+
response
|
41
|
+
end
|
42
|
+
|
43
|
+
# Receive all data devices from the cloud
|
44
|
+
# Each device sends its data to the cloud
|
45
|
+
def read_data(options = {})
|
46
|
+
options = { debug: @debug }.merge(options)
|
47
|
+
puts "#{Time.now} Waiting for #{@appid} incoming data..." if options[:debug]
|
48
|
+
response = sub_read_data(options)
|
49
|
+
puts "#{Time.now} Received: #{response}" if options[:debug]
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
# Stay awaiting data from the cloud
|
54
|
+
def listen(options = {}, &block)
|
55
|
+
options = { debug: @debug }.merge(options)
|
56
|
+
|
57
|
+
puts "#{Time.now} Starting Listen #{@appid}" if options[:debug]
|
58
|
+
response = nil
|
59
|
+
begin
|
60
|
+
response = read_data(options)
|
61
|
+
block.call(response) if block
|
62
|
+
end while response && !options[:test]
|
63
|
+
# Return last response
|
64
|
+
response
|
65
|
+
end
|
66
|
+
|
67
|
+
# Close the secure connection with the cloud
|
68
|
+
def quit
|
69
|
+
sub_quit
|
70
|
+
end
|
71
|
+
end
|
data/lib/lora-rb/help.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# LoraRb methods
|
2
|
+
module LoraRb
|
3
|
+
# It contains generic methods
|
4
|
+
module Base
|
5
|
+
private
|
6
|
+
|
7
|
+
# Show tips on usage
|
8
|
+
def lorarb_help
|
9
|
+
<<-HELP.gsub(/^ /, '')
|
10
|
+
*************************************************************************
|
11
|
+
LoraRb version #{LoraRb.version}
|
12
|
+
*************************************************************************
|
13
|
+
HELP
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# LoraRb calling methods
|
2
|
+
module LoraRb
|
3
|
+
# It contains all the methods for selecting the items
|
4
|
+
module Call
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def sub_initialize(options={})
|
9
|
+
@host = options[:host]
|
10
|
+
{'hello' => 'Lora-Rb: Ready to start!'}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Downlink: Send the request to the device
|
14
|
+
def sub_send_cmd(options={})
|
15
|
+
json_request = {
|
16
|
+
"Command": "",
|
17
|
+
"appEUI": options[:appid],
|
18
|
+
"askConfirmation": true,
|
19
|
+
"devEUI": options[:eui],
|
20
|
+
"payload": options[:data],
|
21
|
+
"hexIdConnector": "69643d32",
|
22
|
+
"port": options[:port],
|
23
|
+
"raw": false
|
24
|
+
}
|
25
|
+
|
26
|
+
puts "#{Time.now} Cmq request: #{json_request}" if options[:debug]
|
27
|
+
response = HTTP.headers(_http_headers)
|
28
|
+
.post("https://#{@host}/api/application/#{@appid}/nodes/#{options[:eui]}/downlink", json: json_request)
|
29
|
+
# Return the json hash from the string
|
30
|
+
# JSON.parse(response)
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
# Uplink: http waits data with an http server.
|
35
|
+
# Lora Network server must exec an http push
|
36
|
+
def sub_read_data(options={})
|
37
|
+
raise('In http protocol, it expects data with an http server. Lora Network server must exec an http push ')
|
38
|
+
end
|
39
|
+
|
40
|
+
def sub_quit
|
41
|
+
end
|
42
|
+
|
43
|
+
def _http_headers(headers={})
|
44
|
+
headers.merge('Content-Type' => 'application/json', 'Authorization' => @token)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# Example
|
50
|
+
=begin
|
51
|
+
require 'lora-rb'
|
52
|
+
lora=LoraClient.new
|
53
|
+
lora.send_cmd(eui: 'BE7A00000000123C')
|
54
|
+
=end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# LoraRb calling methods
|
2
|
+
module LoraRb
|
3
|
+
# It contains all the methods for selecting the items
|
4
|
+
module Call
|
5
|
+
|
6
|
+
attr_reader :contex, :tcp_socket, :ssl_socket
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def sub_initialize(options={})
|
11
|
+
@context = OpenSSL::SSL::SSLContext.new
|
12
|
+
@tcp_socket = TCPSocket.new(options[:host], options[:port])
|
13
|
+
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, @context)
|
14
|
+
@ssl_socket.sync_close = true
|
15
|
+
@ssl_socket.connect
|
16
|
+
welcome_response = ''
|
17
|
+
@ssl_socket.sysread(512, welcome_response)
|
18
|
+
puts "Welcome response: #{welcome_response}" if options[:debug]
|
19
|
+
welcome_response
|
20
|
+
end
|
21
|
+
|
22
|
+
# Send the request to device
|
23
|
+
def sub_send_cmd(options={})
|
24
|
+
response = ''
|
25
|
+
str_app_token = "{\"appid\":\"#{options[:appid]}\", \"token\":\"#{options[:token]}\"}"
|
26
|
+
puts "#{Time.now} To: #{str_app_token}" if options[:debug]
|
27
|
+
@ssl_socket.puts str_app_token
|
28
|
+
str_request = "{\"cmd\":\"#{options[:cmd]}\",\"EUI\":\"#{options[:eui]}\",\"port\":#{options[:port]},\"confirmed\":#{options[:confirmed]},\"data\":\"#{options[:data]}\"} "
|
29
|
+
puts "#{Time.now} Cmq request: #{str_request}" if options[:debug]
|
30
|
+
@ssl_socket.puts(str_request)
|
31
|
+
begin
|
32
|
+
retries ||= 0
|
33
|
+
@ssl_socket.sysread(512, response)
|
34
|
+
rescue EOFError => e
|
35
|
+
if (retries += 1) < 3
|
36
|
+
puts "Attempt ##{retries}..." if options[:debug]
|
37
|
+
sleep 0.5
|
38
|
+
retry
|
39
|
+
else
|
40
|
+
raise e
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the json hash from the string
|
45
|
+
JSON.parse(response)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Send the request to device
|
49
|
+
def sub_read_data(options={})
|
50
|
+
response = ''
|
51
|
+
begin
|
52
|
+
retries ||= 0
|
53
|
+
@ssl_socket.sysread(512, response)
|
54
|
+
rescue EOFError => e
|
55
|
+
if (retries += 1) < 3
|
56
|
+
puts "Attempt ##{retries}..." if options[:debug]
|
57
|
+
sleep 0.5
|
58
|
+
retry
|
59
|
+
else
|
60
|
+
raise e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return the json hash from the string
|
65
|
+
JSON.parse(response)
|
66
|
+
end
|
67
|
+
|
68
|
+
def sub_quit
|
69
|
+
@ssl_socket.close
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Symbolizes all of hash's keys and subkeys.
|
2
|
+
# Also allows for custom pre-processing of keys (e.g. downcasing, etc)
|
3
|
+
# if the block is given:
|
4
|
+
#
|
5
|
+
# somehash.deep_symbolize { |key| key.downcase }
|
6
|
+
#
|
7
|
+
# Usage: either include it into global Hash class to make it available to
|
8
|
+
# to all hashes, or extend only your own hash objects with this
|
9
|
+
# module.
|
10
|
+
# E.g.:
|
11
|
+
# 1) class Hash; include DeepSymbolizable; end
|
12
|
+
# 2) myhash.extend DeepSymbolizable
|
13
|
+
|
14
|
+
module DeepSymbolizable
|
15
|
+
def deep_symbolize(&block)
|
16
|
+
method = self.class.to_s.downcase.to_sym
|
17
|
+
syms = DeepSymbolizable::Symbolizers
|
18
|
+
syms.respond_to?(method) ? syms.send(method, self, &block) : self
|
19
|
+
end
|
20
|
+
|
21
|
+
module Symbolizers
|
22
|
+
extend self
|
23
|
+
|
24
|
+
# the primary method - symbolizes keys of the given hash,
|
25
|
+
# preprocessing them with a block if one was given, and recursively
|
26
|
+
# going into all nested enumerables
|
27
|
+
def hash(hash, &block)
|
28
|
+
hash.inject({}) do |result, (key, value)|
|
29
|
+
# Recursively deep-symbolize subhashes
|
30
|
+
value = _recurse_(value, &block)
|
31
|
+
|
32
|
+
# Pre-process the key with a block if it was given
|
33
|
+
key = yield key if block_given?
|
34
|
+
# Symbolize the key string if it responds to to_sym
|
35
|
+
sym_key = key.to_sym rescue key
|
36
|
+
|
37
|
+
# write it back into the result and return the updated hash
|
38
|
+
result[sym_key] = value
|
39
|
+
result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# walking over arrays and symbolizing all nested elements
|
44
|
+
def array(ary, &block)
|
45
|
+
ary.map { |v| _recurse_(v, &block) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# handling recursion - any Enumerable elements (except String)
|
49
|
+
# is being extended with the module, and then symbolized
|
50
|
+
def _recurse_(value, &block)
|
51
|
+
if value.is_a?(Enumerable) && !value.is_a?(String)
|
52
|
+
# support for a use case without extended core Hash
|
53
|
+
value.extend DeepSymbolizable unless value.class.include?(DeepSymbolizable)
|
54
|
+
value = value.deep_symbolize(&block)
|
55
|
+
end
|
56
|
+
value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lora-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marco Mastrodonato
|
8
|
+
- Rudi Petazzi
|
9
|
+
- Stefano Piras
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2017-04-07 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '2.0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: openssl
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '2.0'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '2.0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: http
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '2.0'
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '2.0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: test-unit
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '3.0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '3.0'
|
71
|
+
description: This gem let you to connect to a Lora Network and manage your registered
|
72
|
+
devices.
|
73
|
+
email:
|
74
|
+
- marco.mastrodonato@fractalgarden.com
|
75
|
+
- rudi.petazzi@fractalgarden.com
|
76
|
+
- stefano.piras@fractalgarden.com
|
77
|
+
executables: []
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- CHANGELOG.md
|
82
|
+
- LICENSE
|
83
|
+
- README.md
|
84
|
+
- lib/config/config.rb
|
85
|
+
- lib/config/private.yml
|
86
|
+
- lib/config/private_EXAMPLE.yml
|
87
|
+
- lib/core/configuration.rb
|
88
|
+
- lib/core/configuration_dynamic.rb
|
89
|
+
- lib/generators/install_generator.rb
|
90
|
+
- lib/lora-rb.rb
|
91
|
+
- lib/lora-rb/base.rb
|
92
|
+
- lib/lora-rb/help.rb
|
93
|
+
- lib/lora-rb/http/call.rb
|
94
|
+
- lib/lora-rb/tls/call.rb
|
95
|
+
- lib/vendor/deep_symbolize.rb
|
96
|
+
- lib/version.rb
|
97
|
+
homepage: https://github.com/marcomd/lora-rb
|
98
|
+
licenses:
|
99
|
+
- LGPL-3.0
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.3.0
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements:
|
116
|
+
- The brave to dare
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.6.10
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Manage your devices inside a Lora Network
|
122
|
+
test_files: []
|