From 0bb7b32b3ef837fbfeb2d97bad821e472f0409b7 Mon Sep 17 00:00:00 2001 From: TheYOSH Date: Wed, 22 Nov 2017 14:09:21 +0100 Subject: [PATCH 01/20] Add CORS headers for Ajax REST calls --- terrariumWebserver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/terrariumWebserver.py b/terrariumWebserver.py index e2ff2cd0a..ef07fc1ce 100644 --- a/terrariumWebserver.py +++ b/terrariumWebserver.py @@ -235,6 +235,7 @@ def __update_api_call(self,path): def __get_api_call(self,path): response.headers['Expires'] = (datetime.datetime.utcnow() + datetime.timedelta(minutes=1)).strftime('%a, %d %b %Y %H:%M:%S GMT') + response.headers['Access-Control-Allow-Origin'] = '*' result = {} parameters = path.strip('/').split('/') From 535f2d5ed9a3c4fd9a9477acbf20680511bf9409 Mon Sep 17 00:00:00 2001 From: TheYOSH Date: Wed, 22 Nov 2017 14:10:23 +0100 Subject: [PATCH 02/20] Add static url parser --- terrariumUtils.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/terrariumUtils.py b/terrariumUtils.py index a168fb83e..e0fbffccf 100644 --- a/terrariumUtils.py +++ b/terrariumUtils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import re class terrariumUtils(): @@ -93,3 +94,16 @@ def to_BOARD_port_number(value): return pinout[index] return False + + @staticmethod + def parse_url(url): + url = url.strip() + if '' == url: + return False + + regex = ur"^((?Phttps?|ftp):\/)?\/?((?P.*?)(:(?P.*?)|)@)?(?P[^:\/\s]+)(:(?P(\d*))?)?(?P(\/\w+)*\/)(?P[-\w.]+[^#?\s]*)?(?P\?([^#]*))?(#(?P(.*))?)?$" + matches = re.search(regex, url) + if matches: + return matches.groupdict() + + return False From 05ce95be2fbeb160caecd27e433c5ec75423fe85 Mon Sep 17 00:00:00 2001 From: TheYOSH Date: Wed, 22 Nov 2017 14:15:08 +0100 Subject: [PATCH 03/20] Add support for remote (HTTP/HTTPS) temperature and humidity sensors through JSON REST API. #51 --- install.sh | 2 +- static/js/terrariumpi.js | 43 +++++++++++++++++++++++++++++++--- terrariumSensor.py | 49 ++++++++++++++++++++++++++++++++++++--- terrariumTranslations.py | 2 +- views/sensor_settings.tpl | 14 ++++++++--- 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/install.sh b/install.sh index da350985b..e28c74c2a 100755 --- a/install.sh +++ b/install.sh @@ -15,7 +15,7 @@ apt-get -y autoremove # Install required packages to get the terrarium software running aptitude -y update aptitude -y safe-upgrade -aptitude -y install libftdi1 screen python-imaging python-dateutil python-ow python-rpi.gpio python-psutil git subversion watchdog build-essential python-dev python-picamera python-opencv python-pip python-pigpio i2c-tools owfs ow-shell sqlite3 vlc-nox python-mediainfodll libasound2-dev +aptitude -y install libftdi1 screen python-imaging python-dateutil python-ow python-rpi.gpio python-psutil git subversion watchdog build-essential python-dev python-picamera python-opencv python-pip python-pigpio python-requests i2c-tools owfs ow-shell sqlite3 vlc-nox python-mediainfodll libasound2-dev # Basic config: raspi-config diff --git a/static/js/terrariumpi.js b/static/js/terrariumpi.js index d46a0c489..aa7c61d5b 100644 --- a/static/js/terrariumpi.js +++ b/static/js/terrariumpi.js @@ -1462,6 +1462,37 @@ function check_form_data(form) { return fieldsok; } +function parse_remote_sensor(url) { + $.get(url,function(data) { + json_path = url.indexOf('#'); + if (json_path != -1) { + json_path = url.substring(json_path+1).split('/'); + // TerrariumPI API is known, and can be used to fill in all values + var is_remote_terrarium_pi = json_path.length == 3 + && json_path[0] === 'sensors' + && json_path[1] === '0' + && json_path[2] === 'current'; + + if (is_remote_terrarium_pi) { + // Loop through the fields and fill in the fields with remote information + $.each(data[json_path[0]][json_path[1]],function(fieldname,value) { + // Never overrule fields in array below + if ($.inArray(fieldname,['address','hardwaretype','id']) == -1) { + $('input[name="sensor_[nr]_' + fieldname + '"]').val(value); + $('select[name="sensor_[nr]_' + fieldname + '"]').val(value).change(); + } + }); + } else { + // Here we loop over the JSON structure to get the end value which should be the current value + $.each(json_path,function(index,value){ + data = data[value]; + }); + $('input[name="sensor_[nr]_current"]').val(data); + } + } + }); +} + function add_sensor() { var form = $('.new-sensor-form'); if (!check_form_data(form)) return false; @@ -1476,8 +1507,11 @@ function add_sensor() { form.find('input[name="sensor_[nr]_limit_min"]').val(), form.find('input[name="sensor_[nr]_limit_max"]').val(), -1); - - $('.new-sensor-form').modal('hide'); + // Reset form + form.find('input').val(''); + form.find('select').val(null).trigger('change'); + // Hide form + form.modal('hide'); } function add_sensor_row(id,hardwaretype,address,type,name,alarm_min,alarm_max,limit_min,limit_max,current) { @@ -1497,11 +1531,14 @@ function add_sensor_row(id,hardwaretype,address,type,name,alarm_min,alarm_max,li sensor_row.find("select").select2({ placeholder: '{{_('Select an option')}}', allowClear: false, - minimumResultsForSearch: Infinity + minimumResultsForSearch: Infinity, }).on('change',function() { + // Changing should not be possible. But disabling will cripple the form post data + /* if (this.name.indexOf('hardwaretype') >= 0) { $("input[name='" + this.name.replace('hardwaretype','address') + "']").attr("readonly", this.value == 'owfs' || this.value == 'w1'); } + */ }); } diff --git a/terrariumSensor.py b/terrariumSensor.py index 0ce93cb7d..d4ca5dc0f 100644 --- a/terrariumSensor.py +++ b/terrariumSensor.py @@ -8,6 +8,7 @@ import Adafruit_DHT as dht import glob import re +import requests from hashlib import md5 from terrariumUtils import terrariumUtils @@ -18,7 +19,7 @@ class terrariumSensor: VALID_DHT_SENSORS = { 'dht11' : dht.DHT11, 'dht22' : dht.DHT22, 'am2302': dht.AM2302 } - VALID_HARDWARE_TYPES = ['owfs','w1'] + VALID_DHT_SENSORS.keys() + VALID_HARDWARE_TYPES = ['owfs','w1','remote'] + VALID_DHT_SENSORS.keys() W1_BASE_PATH = '/sys/bus/w1/devices/' W1_TEMP_REGEX = re.compile(r'(?Pt|f)=(?P[0-9]+)',re.IGNORECASE) @@ -40,6 +41,12 @@ def __init__(self, id, hardware_type, sensor_type, sensor, name = '', alarm_min self.sensor = dht # Dirty hack to replace OWFS sensor object for GPIO pin nr self.sensor_address = sensor + elif self.get_hardware_type() == 'remote': + self.sensor_address = None + url_data = terrariumUtils.parse_url(sensor) + if url_data: + self.sensor = url_data + self.sensor_address = sensor self.set_name(name) self.set_type(sensor_type,indicator) @@ -183,6 +190,24 @@ def update(self, force = False): if 'temperature' == self.get_type(): if self.get_hardware_type() == 'owfs': current = float(self.sensor.temperature) + elif 'remote' == self.get_hardware_type() and self.sensor_address is not None: + authentication=(self.sensor['username'], self.sensor['password']) + data = requests.get(self.sensor_address,auth=authentication) + + if data.status_code == 200: + data = data.json() + json_path = self.sensor['fragment'].split('/') if 'fragment' in self.sensor and self.sensor['fragment'] is not None else [] + + for item in json_path: + # Dirty hack to process array data.... + try: + item = int(item) + except Exception, ex: + item = str(item) + + data = data[item] + current = float(data) + elif self.get_hardware_type() == 'w1': data = '' with open(terrariumSensor.W1_BASE_PATH + self.get_address() + '/w1_slave', 'r') as w1data: @@ -191,8 +216,7 @@ def update(self, force = False): w1data = terrariumSensor.W1_TEMP_REGEX.search(data) if w1data: # Found data - temperature = float(w1data.group('value')) / 1000 - current = float(temperature) + current = float(w1data.group('value')) / 1000 elif self.get_hardware_type() in terrariumSensor.VALID_DHT_SENSORS.keys(): humidity, temperature = self.sensor.read_retry(terrariumSensor.VALID_DHT_SENSORS[self.get_hardware_type()], float(terrariumUtils.to_BCM_port_number(self.sensor_address)), @@ -203,9 +227,28 @@ def update(self, force = False): elif 'humidity' == self.get_type(): if self.get_hardware_type() == 'owfs': current = float(self.sensor.humidity) + elif 'remote' == self.get_hardware_type() and self.sensor_address is not None: + authentication=(self.sensor['username'], self.sensor['password']) + data = requests.get(self.sensor_address,auth=authentication) + + if data.status_code == 200: + data = data.json() + json_path = self.sensor['fragment'].split('/') if 'fragment' in self.sensor and self.sensor['fragment'] is not None else [] + + for item in json_path: + # Dirty hack to process array data.... + try: + item = int(item) + except Exception, ex: + item = str(item) + + data = data[item] + current = float(data) + elif self.get_hardware_type() == 'w1': # Not tested / No hardware to test with pass + elif self.get_hardware_type() in terrariumSensor.VALID_DHT_SENSORS.keys(): humidity, temperature = self.sensor.read_retry(terrariumSensor.VALID_DHT_SENSORS[self.get_hardware_type()], float(terrariumUtils.to_BCM_port_number(self.sensor_address)), diff --git a/terrariumTranslations.py b/terrariumTranslations.py index 1d044b3a5..42233cec4 100644 --- a/terrariumTranslations.py +++ b/terrariumTranslations.py @@ -24,7 +24,7 @@ def __load(self): # Sensors self.translations['sensor_field_hardware'] = _('Holds the sensor hardware type. Supported hardware types are: %s.') % ('' + ', '.join(terrariumSensor.VALID_HARDWARE_TYPES) + '') - self.translations['sensor_field_address'] = _('Holds the sensor address. Depending on hardware type, it is either a read only hex number or GPIO pin. For GPIO use physical GPIO pin numbering.') + self.translations['sensor_field_address'] = _('Holds the sensor address. Depending on hardware type, it is either a read only hex number, a GPIO pin or a full HTTP(S) adress. Full url specification can be found on the %s wiki page. For GPIO use physical GPIO pin numbering.') % ('\'' + _('Remote data') + '\'') self.translations['sensor_field_type'] = _('Holds the sensor type. Supported sensor types are: %s.') % ('' + ', '.join(terrariumSensor.VALID_SENSOR_TYPES) + '') self.translations['sensor_field_name'] = _('Holds the name of the sensor.') self.translations['sensor_field_alarm_min'] = _('Holds the sensor lower alarm value of the sensor. When below this value, alarms can be triggered.') diff --git a/views/sensor_settings.tpl b/views/sensor_settings.tpl index ae87fad8b..9c4c57af5 100644 --- a/views/sensor_settings.tpl +++ b/views/sensor_settings.tpl @@ -92,6 +92,7 @@ + @@ -148,15 +149,22 @@