Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
giuliof | 0bc765b82e | |
giuliof | 61978782c1 | |
giulio | bd38031a5f | |
giulio | 97259f47af | |
giulio | 82dcc0d0cf | |
giulio | d9b1b49890 | |
giulio | 5e99e11256 | |
giulio | e27b85db92 | |
giulio | d3961b9ae3 | |
giulio | 4f57713964 | |
giulio | 31fc484dc5 | |
giulio | 24e7be2e27 | |
giulio | 59486d8632 | |
giulio | ee917094c1 | |
giulio | 17544992fc | |
giulio | 6adf02b3d6 | |
giulio | c895436336 | |
giulio | 6bf508e7b5 | |
giuliof | c8a407e483 | |
giulio | e918ceee74 | |
giulio | 0376a56f05 |
12
README.md
12
README.md
|
@ -1,3 +1,13 @@
|
|||
# domotic-gateway
|
||||
|
||||
A firmware interface for Arduino™ (but easily portable) for domotic application.
|
||||
A firmware interface for Arduino™ (but easily portable) for domotic application.
|
||||
|
||||
**WARNING** - Testing branch, do not use for daily appications.
|
||||
This branch will be destroyed once merged with main
|
||||
|
||||
## Possible stucture
|
||||
- `web` folder should be linked to webserver document root directory (*TODO* .htaccess).
|
||||
- `dispatcher.py` will accept web requests (*TODO* .htpasswd or other authorization methods).
|
||||
- `scripts` will contain code for the automation (invoked by cron), like:
|
||||
- `log-temperatures.py` to push local ambient paramenters and server statistics into database, then plot the data;
|
||||
- `autoclima.py` to turn on the clima if necessary (*missing*).
|
||||
|
|
|
@ -55,11 +55,11 @@ def temperature_conversion(raw):
|
|||
|
||||
|
||||
class DomoticGateway:
|
||||
def __init__(self):
|
||||
def __init__(self, port = "/dev/ttyUSB0"):
|
||||
for i in range(0, 3):
|
||||
try:
|
||||
self.ser = serial.Serial(
|
||||
"/dev/ttyUSB0", 115200, timeout=0.5, exclusive=True)
|
||||
port, 115200, timeout=0.5, exclusive=True)
|
||||
sleep(0.1)
|
||||
except:
|
||||
sleep(1)
|
||||
|
@ -108,3 +108,78 @@ class DomoticGateway:
|
|||
return temperature_conversion(payload)
|
||||
else:
|
||||
raise ValueError('Invalid command')
|
||||
|
||||
# Populate this
|
||||
# Arguments: mode, temperature, fan speed
|
||||
# Convert to hex code, make packet and send it
|
||||
def setClima(self, mode, temperature, fan):
|
||||
# Code conversion (alpha, incomplete)
|
||||
CLIMA_HEADER = 0x4d0000
|
||||
CLIMA_MODES = {
|
||||
'caldo' : 0b0011,
|
||||
'freddo' : 0b1111,
|
||||
'ventilatore' : 0b1011,
|
||||
'deumidificatore' : 0b0100,
|
||||
'auto' : 0b1000,
|
||||
}
|
||||
# [0] element is 24°C.
|
||||
BASE_TMP = 24
|
||||
CLIMA_TEMPERATURES = (0b1011, 0b0011, 0b0010, 0b0110, 0b0111)
|
||||
FAN_SPEED = (0b0100, 0b0110, 0b1010, 0b1100)
|
||||
|
||||
# Static header
|
||||
status = CLIMA_HEADER
|
||||
|
||||
if mode == 'spento':
|
||||
status |= 0x841f
|
||||
else:
|
||||
# Mode (hot, cold, fan only, dehumidifier, auto)
|
||||
try:
|
||||
status |= CLIMA_MODES[mode]
|
||||
except:
|
||||
# NO! throw exception
|
||||
print ("Invalid clima option")
|
||||
exit(-1)
|
||||
|
||||
# Fan mode (not yet implemented)
|
||||
|
||||
# Temperature ()
|
||||
try:
|
||||
status |= CLIMA_TEMPERATURES[int(temperature) - BASE_TMP] << 4
|
||||
except:
|
||||
# NO! throw exception
|
||||
print ("Invalid clima option")
|
||||
exit(-1)
|
||||
|
||||
# No fan speed settings for these modes
|
||||
if mode == 'auto' or mode == 'dehum':
|
||||
status |= 0x00e000
|
||||
else:
|
||||
# Fan speed ()
|
||||
try:
|
||||
status |= FAN_SPEED[int(fan)] << 12
|
||||
except:
|
||||
# NO! throw exception
|
||||
print ("Invalid clima option")
|
||||
exit(-1)
|
||||
|
||||
# Clima packet is 4 bytes (uint32_t)
|
||||
payload = []
|
||||
payload.append(status & 0xff)
|
||||
status >>= 8
|
||||
payload.append(status & 0xff)
|
||||
status >>= 8
|
||||
payload.append(status & 0xff)
|
||||
payload.append(0x00)
|
||||
|
||||
self.sendCommand('CMD_CLIMA', payload)
|
||||
|
||||
# Make ambient packet, wait for response, convert values and return
|
||||
def getAmbient(self):
|
||||
self.sendCommand('CMD_TEMP')
|
||||
try:
|
||||
tmp = self.receiveData()
|
||||
except Exception as e:
|
||||
tmp = None
|
||||
|
||||
return dict(zip(('ambtmp',), [tmp]))
|
|
@ -0,0 +1,5 @@
|
|||
[clima]
|
||||
mode =
|
||||
temperature =
|
||||
fan =
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
# GET me using a web browser,
|
||||
# executing my code with a Python interpreter called by a CGI-compliant webserver!
|
||||
# Example URI:
|
||||
# http://www.example.org/path/dispatcher.py
|
||||
|
||||
import json
|
||||
import cgitb, cgi
|
||||
import configparser
|
||||
|
||||
import DomoticGateway as dg
|
||||
|
||||
|
||||
|
||||
# create a standard error JSON packet
|
||||
def error(msg):
|
||||
return {'ret' : 'error', 'msg' : msg}
|
||||
|
||||
|
||||
# Read configuration files (latest files in list override previous settings)
|
||||
cfg = configparser.ConfigParser()
|
||||
|
||||
# Start CGI handling for webserver
|
||||
cgitb.enable()
|
||||
inputvars = cgi.FieldStorage()
|
||||
|
||||
print('Content-Type: text/json; charset=utf-8')
|
||||
print('Access-Control-Allow-Origin: *')
|
||||
print()
|
||||
### End of HTTP headers: it is now safe to output things
|
||||
##########################################################
|
||||
result = {'ret' : 'ok'}
|
||||
|
||||
if 'payload' not in inputvars:
|
||||
result = error('No command provided')
|
||||
else:
|
||||
try:
|
||||
payload = json.loads(inputvars['payload'].value)
|
||||
except:
|
||||
result = error('JSON payload is invalid')
|
||||
|
||||
# Send a message to clima unit
|
||||
if payload['cmd'] == 'setClima':
|
||||
if 'clima' not in payload:
|
||||
result = error("Missing clima parameters")
|
||||
else:
|
||||
opts = payload['clima']
|
||||
|
||||
if not set(('mode', 'temperature', 'fan')).issubset(opts):
|
||||
result = error("Invalid clima options")
|
||||
else:
|
||||
# buìld appropriate package and send
|
||||
dgh = dg.DomoticGateway()
|
||||
try:
|
||||
dgh.setClima(**opts)
|
||||
except TypeError:
|
||||
result = error("Wrong parameter formatting")
|
||||
except:
|
||||
result = error("setClima error")
|
||||
|
||||
|
||||
# Get last default options for clima
|
||||
elif payload['cmd'] == 'getClima':
|
||||
cfg.read(['conf/clima.ini', 'conf/clima.custom.ini'])
|
||||
if 'clima' in cfg:
|
||||
result = {**result, 'clima' : dict(cfg['clima'].items())}
|
||||
else:
|
||||
result = error("Malformed clima CFG file")
|
||||
|
||||
# Set default options for clima
|
||||
elif payload['cmd'] == 'putClima':
|
||||
if 'clima' not in payload:
|
||||
result = error("Missing clima parameters")
|
||||
else:
|
||||
opts = payload['clima']
|
||||
|
||||
if not set(('mode', 'temperature', 'fan')).issubset(opts):
|
||||
result = error("Invalid clima options")
|
||||
else:
|
||||
cfg['clima'] = opts
|
||||
with open('conf/clima.custom.ini', 'w') as configfile:
|
||||
cfg.write(configfile)
|
||||
|
||||
|
||||
# Get ambient parameters
|
||||
elif payload['cmd'] == 'getAmbient':
|
||||
dgh = dg.DomoticGateway()
|
||||
result = {**result, **dgh.getAmbient()}
|
||||
|
||||
|
||||
else:
|
||||
result = error('Invalid command provided')
|
||||
|
||||
print(json.dumps(result, indent=4))
|
Loading…
Reference in New Issue