diff --git a/controller/config/__init__.py b/controller/config/__init__.py new file mode 100644 index 0000000..6df2f0a --- /dev/null +++ b/controller/config/__init__.py @@ -0,0 +1,8 @@ + + +GPIO_PIN_DHT = 4 # BCM +SENSORDATA_URL = 'http://tfm.meteor.com/api/sensorData' +GPIO_PIN_VALVE = 21 # BCM +API_BASE_URL = 'http://tfm.meteor.com/api' +CONTROLLER_ID = '120' # every controller must have a different one +STATE_FILE = '/var/run/controller_state' diff --git a/controller/sensors.py b/controller/sensors.py new file mode 100644 index 0000000..b209b7b --- /dev/null +++ b/controller/sensors.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +import sys +import requests +import Adafruit_DHT +import config + + +SENSOR_TYPE = Adafruit_DHT.DHT11 + +if len(sys.argv) == 3: + owner = sys.argv[1] + controller_id = sys.argv[2] +else: + print 'usage: sudo ./controller.py [OWNER] [CONTROLLER_ID]#' + print 'example: sudo ./controller.py Senad 225 - Send temperature as Senad for controller 225' + sys.exit(1) + +# Try to grab a sensor reading. Use the read_retry method which will retry up +# to 15 times to get a sensor reading (waiting 2 seconds between each retry). +humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, GPIO_PIN_DHT) + +# Un-comment the line below to convert the temperature to Fahrenheit. +# temperature = temperature * 9/5.0 + 32 + +# Note that sometimes you won't get a reading and +# the results will be null (because Linux can't +# guarantee the timing of calls to read the sensor). +# If this happens try again! +if temperature is not None and humidity is not None: + response = requests.post(SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity}) + print 'Temp={0:0.1f}*C'.format(temperature) + print 'Humidity={0:0.1f}%'.format(humidity) + if response.status_code != 200: + print 'Failed to send temperature!' + +else: + print 'Failed to get reading. Try again!' diff --git a/controller/state/__init__.py b/controller/state/__init__.py new file mode 100644 index 0000000..27f08d3 --- /dev/null +++ b/controller/state/__init__.py @@ -0,0 +1,19 @@ + + +def sync(): + server = Server(API_BASE_URL, CONTROLLER_ID) + local = File(STATE_FILE) + server_state = server.get_state() + + if local.present(): + local.load() + else: + local.data = server_state + local.save() + + local_state = local.data + + changer = Changer(local_state, remote_state) + current_state = changer.process_change() + + server.post_state(current_state) diff --git a/controller/state/changer.py b/controller/state/changer.py new file mode 100644 index 0000000..a255b02 --- /dev/null +++ b/controller/state/changer.py @@ -0,0 +1,39 @@ +import RPi.GPIO as GPIO +import config + +class Changer(object): + + def __init__(self, local_state, remote_state): + self.local_state = local_state + self.remote_state = remote_state + GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme + GPIO.setup(GPIO_PIN_VALVE, GPIO.OUT) + + self.states = { + 'opening': self.open_valve, + 'closing': self.close_valve, + 'open': self.open_valve, + 'closed': self.close_valve + + } + + def process_change(self): + self.validate_states() + if self.local_state['out_valve'] != self.remote_state['out_valve']: + change = self.states.get(self.remote_state['out_valve'], None ) + if change is not None: + change() + return self.local_state + + def open_valve(): + GPIO.output(GPIO_PIN_VALVE, GPIO.HIGH) + self.local_state['out_valve'] = 'open' + + def close_valve(): + GPIO.output(GPIO_PIN_VALVE, GPIO.LOW) + self.local_state['out_valve'] = 'closed' + + def validate_states(self): + if self.local_state is None or self.remote_state is None: + raise ClassNotReadyException("Both local and remote states must be present!") + # TODO: add detailed validation diff --git a/controller/state/exceptions.py b/controller/state/exceptions.py new file mode 100644 index 0000000..468a58f --- /dev/null +++ b/controller/state/exceptions.py @@ -0,0 +1,14 @@ + +class ClassNotReadyException(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + +class ErrorCommunicatingWithServerException(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) diff --git a/controller/state/file.py b/controller/state/file.py new file mode 100644 index 0000000..1172205 --- /dev/null +++ b/controller/state/file.py @@ -0,0 +1,27 @@ +import json +import os.path + + + +class File(object): + "Holds controller state in the file" + def __init__(self, filename=None): + self.filename = filename + + def present(self): + os.path.isfile(self.filename) + + def load(self): + if self.filename is None: + raise ClassNotReadyException("Filename not set!") + with open(self.filename) as input_file: + self.data = json.load(input_file) + + def save(self): + if self.filename is None: + raise ClassNotReadyException("Filename not set!") + if self.data is None: + raise ClassNotReadyException("Data not loaded!") + + with open(self.filename, 'w') as out_file: + json.dump(self.data, out_file) diff --git a/controller/state/server.py b/controller/state/server.py new file mode 100644 index 0000000..092300a --- /dev/null +++ b/controller/state/server.py @@ -0,0 +1,33 @@ +import json +import requests + + +class Server(object): + "Gets state from server and sends it to the server after change" + def __init__(self, url_base=None, controller_id=None): + self.url_base = url_base + self.controller_id = controller_id + + def get_state(self): + result = requests.get(self.full_url('state/%s') % self.controller_id) + return handle_response(result) + + def post_state(self, local_state): + result = requests.post(self.full_url('state/%s') % self.controller_id, local_state) + return handle_response(result) + + + + def full_url(self, action): + if self.controller_id is None: + raise ClassNotReadyException("Controller id not set!") + if self.url_base is None: + raise ClassNotReadyException("URL base not set!") + return self.url_base + '/' + action + + +def handle_response(response): + if response.status_code != 200: + raise ErrorCommunicatingWithServerException("Response not 200!") + else: + return response.json() diff --git a/controller/sync_state.py b/controller/sync_state.py new file mode 100644 index 0000000..1fe4f3a --- /dev/null +++ b/controller/sync_state.py @@ -0,0 +1,3 @@ +import state + +state.sync()