Newer
Older
greenhouse / watering.py
#!/bin/env python3

from pyLoraRFM9x import LoRa, ModemConfig
import time
from collections import namedtuple
from struct import *
import pykka
import paho.mqtt.client as paho
from paho import mqtt
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
from threading import Thread

client = paho.Client(paho.CallbackAPIVersion.VERSION2)
client.connect("stanley")

Watering = namedtuple('Watering', 'voltage moisture intervals dry wet state delay_hours max_time_mins pump_speed pump_running errors')
WateringStruct = '<HHHHHBBBB?B'

class WaterActor(pykka.ThreadingActor):
    def __init__(self, client):
        super().__init__()
        self.client = client
        self.state = None
        self.updates = []

    def greenhouse(self, payload):
        try:
            message = Watering._make(unpack(WateringStruct, payload.message))
            self.state = message
            to_send = message._asdict()
            to_send['rssi'] = payload.rssi
            to_send['snr'] = payload.snr
            client.publish('events/greenhouse', payload=json.dumps(to_send))
        except Exception as e:
            client.publish('events/greenhouse', payload=str(e))
        updated_state = self.state
        for update in self.updates:
            updated_state = updated_state._replace(**update)
        self.updates = []
        if self.state != updated_state:
            reply = pack(WateringStruct, *updated_state)
            print("Sending: " + str(updated_state))
            lora.send(reply, 2)
            lora.set_mode_rx()

    def queue_update(self, update):
        self.updates.append(update)

a = WaterActor.start(client).proxy()

class SprinklerHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        current_state = a.state.get()
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.flush_headers()
        if self.path == '/on':
            a.queue_update({
                'state': 0,
                'intervals': current_state.delay_hours * 360 + 1
            })
            self.wfile.write(bytes("Switching pump on", "utf-8"))
        elif self.path == '/off':
            a.queue_update({
                'state': 1,
                'intervals': 0,
                'wet': current_state.moisture + 1
            })
            self.wfile.write(bytes("Switching pump off", "utf-8"))

httpd = HTTPServer(('', 80), SprinklerHTTPRequestHandler)

Thread(target=httpd.serve_forever, daemon=True).start()

def on_radio_recv(payload):
    a.greenhouse(payload)

lora = LoRa(1, 24, 1, reset_pin = 25, modem_config=ModemConfig.Bw125Cr45Sf128, tx_power=14, acks=False, freq=868, receive_all=False)
lora.on_recv = on_radio_recv
lora.set_mode_rx()

def on_mqtt_message(client, userdata, message):
    try:
        update = json.loads(message.payload)
        a.queue_update(update)
    except Exception as e:
        client.publish('events/greenhouse', payload=str(e))

client.on_message = on_mqtt_message
client.subscribe("commands/greenhouse")

#client.loop_forever(retry_first_connection=False)
Thread(target=client.loop_forever,
       kwargs={"retry_first_connection": False},
       daemon=True).start()