Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
Smart-building MSE-Lab
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
This is an archived project. Repository and other project resources are read-only.
Show more breadcrumbs
LSDS
Smart-building MSE-Lab
Commits
6eec668b
Commit
6eec668b
authored
5 years ago
by
marcoemi.poleggi
Browse files
Options
Downloads
Patches
Plain Diff
Doc + new client + util
parent
73db85d6
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
README.md
+66
-0
66 additions, 0 deletions
README.md
post_client.py
+430
-27
430 additions, 27 deletions
post_client.py
util/internet-sharing
+18
-0
18 additions, 0 deletions
util/internet-sharing
with
514 additions
and
27 deletions
README.md
0 → 100644
+
66
−
0
View file @
6eec668b
# Smart Building Lab 1 / Part 2: Z-Wave #
This is a template of a Python flask-based application for controlling a
Z-Wave IoT deployment.
The
`backend.py`
file is to be completed in the parts marked by:
```
#### COMPLETE THIS METHOD ####
```
Once done, you can start your server with the command
```
python2 flask-main.py
```
then, wait ~1 minute for the network to stabilize, and check the connection with a browser,
normally at the URL:
```
http://192.168.1.2:5000
```
## Raspberry set-up ##
In order to share a network card as an internet gateway in Linux, ip
[
forwarding and masquerading must be setup
](
https://wiki.archlinux.org/index.php/Internet_sharing
)
. The
`util/internet-sharing`
script does the trick:
```
#!/bin/bash
################################################################################
# internet-sharing
#
# Forward outbound traffic from a client interface to an internet gateway
# interface.
#
# <https://wiki.archlinux.org/index.php/Internet_sharing>
# <https://linoxide.com/firewall/ip-forwarding-connecting-private-interface-internet/>
################################################################################
gwint
=
${
1
:-
'net0'
}
# internet gateway interface -- all outbound traffic
clint
=
${
2
:-
'net1'
}
# client interface -- input traffic
sysctl net.ipv4.ip_forward
=
1 net.ipv6.conf.default.forwarding
=
1 net.ipv6.conf.all.forwarding
=
1
iptables
-t
nat
-A
POSTROUTING
-o
$gwint
-j
MASQUERADE
iptables
-A
FORWARD
-m
conntrack
--ctstate
RELATED,ESTABLISHED
-j
ACCEPT
iptables
-A
FORWARD
-i
$clint
-o
$gwint
-j
ACCEPT
```
By default, it expects two interfaces named
`net0`
and
`net1`
for,
respectively, the internet gateway and the client inbound interface. So, if
you want to reroute all incoming traffic from, say,
`eth0`
(where your
Raspberry is connected to) through your wireless connection
`wlan0`
, you'd call
the script like this (as
**superuser**
):
```
# ./internet-sharing wlan0 eth0
```
To make the changes persistent, consult your Linux distribution's documentation.
## Testing ##
A CL-based client script is available:
`post_client.py`
. See how to use it with
```
python3 post_client.py -h
```
This diff is collapsed.
Click to expand it.
post_client.py
+
430
−
27
View file @
6eec668b
import
requests
import
json
#!/usr/bin/env python3
################################################################################
"""
post_client.py -- demo client script for hepia
'
s LSDS IoT Z-Wave Lab
#############################################################
#### This script sends POST or PUT http request to server ###
#############################################################
#### You have to uncomment the request you want to send ###
#############################################################
#### POST requests contain parameters in JSON format ###
#############################################################
This script sends POST or GET HTTP requests to a REST server. POST requests
contain parameters in JSON format.
Usage:
#### Configuration of nodes
req
=
requests
.
post
(
'
http://192.168.1.2:5000/network/set_sensor_nodes_basic_configuration
'
,
headers
=
{
'
Content-Type
'
:
'
application/json
'
},
data
=
json
.
dumps
({
'
Group_Interval
'
:
'
240
'
,
'
Group_Reports
'
:
'
240
'
,
'
Wake-up_Interval
'
:
'
480
'
}))
post_client.py [OPTIONS] CLASS COMMAND
See `post_client.py -h` for the details
#### Config a specific parameter
#req = requests.post('http://192.168.1.2:5000/nodes/set_parameter',headers={'Content-Type': 'application/json'}, data=json.dumps({'node_id': '4','value':'480', 'parameter_index': '111', 'size': '4'}))
REST command paths
==================
#### Set node location
#req = requests.post('http://192.168.1.2:5000/nodes/set_location',headers={'Content-Type': 'application/json'}, data=json.dumps({'node_id': '4','value':'A402'}))
GET requests
------------
URL path:
#### Set node name
#req = requests.post('http://192.168.1.2:5000/nodes/set_name',headers={'Content-Type': 'application/json'}, data=json.dumps({'node_id': '4','value':'sensor'}))
/<class>[/<node_id>]/<command>[/<parameter_index>]
where `node_id` and `parameter_index` must be specified via explicit CL
options, respectively `-n` and `-i`.
#### Send command to switch
#req = requests.post('http://192.168.1.2:5000/switches/send_command',headers={'Content-Type': 'application/json'}, data=json.dumps({'node_id': '3','value':'on'}))
#### Send command to dimmer
#req = requests.post('http://192.168.1.2:5000/dimmers/set_level',headers={'Content-Type': 'application/json'}, data=json.dumps({'node_id': '6','value':'120'}))
POST requests
-------------
#### Put controller in inclusion mode
#req = requests.post('http://192.168.1.2:5000/nodes/add_node')
URL path:
/<class>/<command>
#### Put controller in exclusion mode
#req = requests.post('http://192.168.1.2:5000/nodes/remove_node')
with JSON payload specified via the CL option `-d`.
print
(
req
.
text
)
# print server response
Examples
========
Dump network topology:
post_client.py network info
Switch to inclusion mode (20s timeout):
post_client.py nodes add_node
Switch to exclusion mode (20s timeout):
post_client.py nodes remove_node
Get dimmer level for a specific node (given as CL option `-n`):
post_client.py dimmers get_level -n 5
Set dimmer level for a specific node (notice how the ID is specified in
the JSON payload):
post_client.py dimmers set_level -d
'
{
"
node_id
"
: 5,
"
value
"
: 50}
'
Get value for a generic parameter at index
'
111
'
for node
'
5
'
:
python3 post_client.py nodes get_parameter -n 5 -i 111
Set value for a generic parameter at index
'
111
'
for node
'
5
'
:
python3 post_client.py nodes set_parameter
\
-d
'
{
"
node_id
"
: 5,
"
value
"
: 480,
"
parameter_index
"
: 111,
"
size
"
: 4}
'
Get all measures for a given node
post_client.py sensors get_all_measures -n 2
Bugs
====
* At least CLASS and COMMAND must be given to get the script
'
s manual with
`-m`.
* Setting generic parameter doesn
'
t seem to work properly: (the parameter is
created if not existing,) it
'
s value stays always at 0.
"""
################################################################################
import
requests
,
argparse
,
json
,
sys
,
copy
,
logging
logging
.
basicConfig
(
level
=
logging
.
INFO
,
format
=
'
%(levelname)-8s %(message)s
'
)
logger
=
logging
.
getLogger
(
'
rest_client
'
)
################################################################################
# constants
http_headers
=
{
'
Content-Type
'
:
'
application/json
'
}
# cross reference with default node ID, par index and data (where needed)
command_xref
=
{
'
network
'
:
{
'
get_nodes_configuration
'
:
{
'
method
'
:
'
GET
'
,
},
'
info
'
:
{
'
method
'
:
'
GET
'
,
},
'
reset
'
:
{
# possibly broken
'
method
'
:
'
GET
'
,
},
'
set_sensor_nodes_basic_configuration
'
:
{
'
method
'
:
'
POST
'
,
'
data
'
:
{
'
Group_Interval
'
:
'
240
'
,
'
Group_Reports
'
:
'
240
'
,
'
Wake-up_Interval
'
:
'
480
'
}
},
'
start
'
:
{
'
method
'
:
'
GET
'
,
},
'
stop
'
:
{
'
method
'
:
'
GET
'
,
},
},
'
nodes
'
:
{
'
add_node
'
:
{
'
method
'
:
'
POST
'
,
},
'
get_battery
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
},
'
get_location
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
},
'
get_name
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
},
'
get_neighbours
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
},
'
get_nodes_list
'
:
{
'
method
'
:
'
GET
'
,
},
'
get_parameter
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
'
parameter_index
'
:
111
,
},
'
remove_node
'
:
{
'
method
'
:
'
POST
'
,
},
'
set_location
'
:
{
'
method
'
:
'
POST
'
,
'
data
'
:
{
'
node_id
'
:
'
1
'
,
'
value
'
:
'
A402
'
}
},
'
set_name
'
:
{
'
method
'
:
'
POST
'
,
'
data
'
:
{
'
node_id
'
:
'
2
'
,
'
value
'
:
'
sensor
'
}
},
'
set_parameter
'
:
{
'
method
'
:
'
POST
'
,
'
data
'
:
{
'
node_id
'
:
'
3
'
,
'
value
'
:
'
480
'
,
'
parameter_index
'
:
'
111
'
,
'
size
'
:
'
4
'
}
}
},
'
dimmers
'
:
{
'
get_dimmers_list
'
:
{
'
method
'
:
'
GET
'
,
},
'
get_level
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
1
,
},
'
set_level
'
:
{
'
method
'
:
'
POST
'
,
'
data
'
:
{
'
node_id
'
:
'
5
'
,
'
value
'
:
'
10
'
}
}
},
'
sensors
'
:
{
'
get_all_measures
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
2
,
},
'
get_humidity
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
2
,
},
'
get_luminance
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
2
,
},
'
get_motion
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
2
,
},
'
get_sensors_list
'
:
{
'
method
'
:
'
GET
'
,
},
'
get_temperature
'
:
{
'
method
'
:
'
GET
'
,
'
node_id
'
:
2
,
},
},
}
################################################################################
# functions
def
build_request_path
(
cclass
,
command
,
cmnd_s
,
node_id
=
None
,
parameter_index
=
None
,
):
"""
Build a request path.
:returns str: a string like
'
/<class>[/<node_id>]/<command>[/<parameter>]
'
or None on errors
Arguments
+++++++++
:param str cclass: the resource/object class
:param str command: the command name
:param dict cmnd_s: the command spec dict as in `command_xref`
Keywords arguments
++++++++++++++++++
:param int node_id: the target node to query
:param int parameter_index: the parameter index to query
"""
if
'
node_id
'
in
cmnd_s
.
keys
():
node_id
=
node_id
or
cmnd_s
[
'
node_id
'
]
elif
node_id
:
logger
.
error
(
"
/{}/{}: unexepected
'
node_id
'
({}) for this request
"
.
format
(
cclass
,
command
,
node_id
)
)
return
None
if
'
parameter_index
'
in
cmnd_s
.
keys
():
parameter_index
=
parameter_index
or
cmnd_s
[
'
parameter_index
'
]
elif
parameter_index
:
logger
.
error
(
"
/{}/{}: unexepected
'
parameter_index
'
({}) for this request
"
.
format
(
cclass
,
command
,
parameter_index
)
)
return
None
return
'
/
'
+
cclass
+
'
/
'
\
+
((
str
(
node_id
)
+
'
/
'
)
if
node_id
!=
None
else
''
)
+
command
\
+
((
'
/
'
+
str
(
parameter_index
)
)
if
parameter_index
!=
None
else
''
)
################################################################################
# main
parser
=
argparse
.
ArgumentParser
(
description
=
'
Demo client for REST-based Z-Wave deployments -- hepia/LSDS Smart-Building
'
,
formatter_class
=
argparse
.
ArgumentDefaultsHelpFormatter
)
parser
.
add_argument
(
'
-m
'
,
'
--manual
'
,
dest
=
'
manual
'
,
action
=
'
store_true
'
,
help
=
'
print the full documentation
'
)
parser
.
add_argument
(
'
-u
'
,
'
--server-url
'
,
dest
=
'
server_url
'
,
type
=
str
,
default
=
'
http://192.168.1.2
'
,
help
=
'
REST server URL
'
)
parser
.
add_argument
(
'
-p
'
,
'
--server-port
'
,
dest
=
'
server_port
'
,
type
=
int
,
default
=
5000
,
help
=
'
REST server PORT
'
)
parser
.
add_argument
(
'
-n
'
,
'
--node-id
'
,
dest
=
'
node_id
'
,
type
=
int
,
help
=
'
Node ID
'
)
parser
.
add_argument
(
'
-d
'
,
'
--data
'
,
dest
=
'
data
'
,
type
=
str
,
help
=
'
JSON payload string for POST requests
'
)
parser
.
add_argument
(
'
-i
'
,
'
--parameter-index
'
,
dest
=
'
parameter_index
'
,
type
=
int
,
help
=
'
Parameter index
'
)
parser
.
add_argument
(
'
class
'
,
# no metavar, else choices won't be proposed on -h
type
=
str
,
choices
=
(
'
network
'
,
'
nodes
'
,
'
dimmers
'
,
'
sensors
'
),
help
=
'
Device/object class
'
)
parser
.
add_argument
(
'
command
'
,
type
=
str
,
choices
=
(
# network
'
get_nodes_configuration
'
,
'
info
'
,
'
reset
'
,
'
set_sensor_nodes_basic_configuration
'
,
'
start
'
,
'
stop
'
,
# nodes
'
add_node
'
,
'
get_battery
'
,
'
get_location
'
,
'
get_name
'
,
'
get_neighbours
'
,
'
get_nodes_list
'
,
'
get_parameter
'
,
'
remove_node
'
,
'
set_location
'
,
'
set_name
'
,
'
set_parameter
'
,
# dimmers
'
get_dimmers_list
'
,
'
get_level
'
,
'
set_level
'
,
# sensors
'
get_all_measures
'
,
'
get_humidity
'
,
'
get_luminance
'
,
'
get_motion
'
,
'
get_sensors_list
'
,
'
get_temperature
'
),
help
=
'
Command to send
'
)
# easier with a dict
args
=
vars
(
parser
.
parse_args
())
if
args
[
'
manual
'
]:
print
(
__doc__
)
sys
.
exit
(
0
)
cclass
=
args
[
'
class
'
]
command
=
args
[
'
command
'
]
data
=
{}
if
args
[
'
data
'
]:
try
:
data
=
json
.
loads
(
args
[
'
data
'
])
except
Exception
as
e
:
logger
.
error
(
"
{}: invalid JSON data: {}
"
.
format
(
args
[
'
data
'
],
e
))
sys
.
exit
(
1
)
cmnd_s
=
{}
try
:
cmnd_s
=
command_xref
[
cclass
][
command
]
except
KeyError
:
parser
.
print_help
()
logger
.
error
(
"
{}: {}: no such command available for this class
"
.
format
(
cclass
,
command
))
sys
.
exit
(
1
)
base_url
=
"
{}:{}
"
.
format
(
args
[
'
server_url
'
],
args
[
'
server_port
'
])
command_path
=
build_request_path
(
cclass
,
command
,
cmnd_s
,
node_id
=
args
[
'
node_id
'
],
parameter_index
=
args
[
'
parameter_index
'
]
)
if
not
command_path
:
logger
.
fatal
(
"
Can
'
t build command path
"
)
sys
.
exit
(
1
)
method
=
cmnd_s
[
'
method
'
]
if
'
data
'
in
cmnd_s
.
keys
():
data
=
data
or
cmnd_s
[
'
data
'
]
elif
data
:
sys
.
exit
(
"
{}: unexpected data for command path
"
.
format
(
command_path
))
logger
.
info
(
"
Sending {} request path:
'
{}
'"
.
format
(
method
,
command_path
))
if
data
:
logger
.
info
(
"
With payload:
'
{}
'"
.
format
(
data
))
req
=
requests
.
request
(
method
,
base_url
+
command_path
,
headers
=
http_headers
,
data
=
json
.
dumps
(
data
)
)
print
(
"
Server response:
\n
[{}] {}
"
.
format
(
req
.
status_code
,
req
.
text
))
sys
.
exit
(
0
)
This diff is collapsed.
Click to expand it.
util/internet-sharing
0 → 100755
+
18
−
0
View file @
6eec668b
#!/bin/bash
################################################################################
# internet-sharing
#
# Forward outbound traffic from a client interface to an internet gateway
# interface.
#
# <https://wiki.archlinux.org/index.php/Internet_sharing>
# <https://linoxide.com/firewall/ip-forwarding-connecting-private-interface-internet/>
################################################################################
gwint
=
${
1
:-
'net0'
}
# internet gateway interface -- all outbound traffic
clint
=
${
2
:-
'net1'
}
# client interface -- input traffic
sysctl net.ipv4.ip_forward
=
1 net.ipv6.conf.default.forwarding
=
1 net.ipv6.conf.all.forwarding
=
1
iptables
-t
nat
-A
POSTROUTING
-o
$gwint
-j
MASQUERADE
iptables
-A
FORWARD
-m
conntrack
--ctstate
RELATED,ESTABLISHED
-j
ACCEPT
iptables
-A
FORWARD
-i
$clint
-o
$gwint
-j
ACCEPT
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment