diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96403d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/* diff --git a/ArduinoWeatherStation b/ArduinoWeatherStation new file mode 100755 index 0000000..36d2fad --- /dev/null +++ b/ArduinoWeatherStation @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 +from arduinows import start +start() diff --git a/arduinows.py b/arduinows.py new file mode 100644 index 0000000..7491f40 --- /dev/null +++ b/arduinows.py @@ -0,0 +1,523 @@ +import requests +from threading import Thread +from time import time, sleep +from datetime import datetime +# from itertools import groupby + +from tango import AttrWriteType, DevState, DispLevel, DeviceProxy +from tango.server import run, Device, attribute, device_property + + +class ArduinoWeatherStation(Device): + + address = device_property( + dtype=str, + doc="IP address", + ) + port = device_property( + dtype=int, + default_value=5209, + doc="Port", + ) + polling_time = device_property( + dtype=int, + default_value=2, + doc="Polling time", + ) + timeout = device_property( + dtype=int, + default_value=5, + doc="Data request timeout", + ) + stale_time = device_property( + dtype=int, + default_value=60, + doc="Stale time", + ) + # buffer_size = device_property( + # dtype=int, + # default_value=30, + # doc="Data buffer size", + # ) + + fw_version = attribute( + label="FW version", + dtype=str, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_fw_version" + ) + eeprom_slots = attribute( + label="EEPROM slots", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_eeprom_slots" + ) + eeprom_writes = attribute( + label="EEPROM write count", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_eeprom_writes" + ) + sht30_status = attribute( + label="SHT30 status", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_sht30_status" + ) + + t_outA = attribute( + label="Outside temperature A", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_outA_temp" + ) + t_outB = attribute( + label="Outside temperature B", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_outB_temp" + ) + t_out_avg = attribute( + label="Average outside temperature", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_out_avg_temp" + ) + t_box = attribute( + label="Box temperature", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_box_temp" + ) + t_chip = attribute( + label="Chip temperature", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_chip_temp" + ) + + rh = attribute( + label="Relative humidity", + dtype=float, unit="%RH", + access=AttrWriteType.READ, + fget="get_rh" + ) + ah = attribute( + label="Absolute humidity", + dtype=float, unit="g/m3", + access=AttrWriteType.READ, + fget="get_ah" + ) + dew = attribute( + label="Dew point", + dtype=float, unit="C", + access=AttrWriteType.READ, + fget="get_dew" + ) + + bat1ADC = attribute( + label="Battery1 ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_bat1ADC" + ) + bat2ADC = attribute( + label="Battery2 ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_bat2ADC" + ) + pv1ADC = attribute( + label="PV1 ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_pv1ADC" + ) + pv2ADC = attribute( + label="PV2 ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_pv2ADC" + ) + bat1V = attribute( + label="Battery1 Voltage", + dtype=float, unit="V", + access=AttrWriteType.READ, + fget="get_bat1V" + ) + bat2V = attribute( + label="Battery2 Voltage", + dtype=float, unit="V", + access=AttrWriteType.READ, + fget="get_bat2V" + ) + pv1V = attribute( + label="PV1 Voltage", + dtype=float, unit="V", + access=AttrWriteType.READ, + fget="get_pv1V" + ) + pv2V = attribute( + label="PV2 Voltage", + dtype=float, unit="V", + access=AttrWriteType.READ, + fget="get_pv2V" + ) + + rdADC = attribute( + label="Rain detector ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_rdADC" + ) + piezoADC = attribute( + label="Piezo sensor ADC", + dtype=int, + access=AttrWriteType.READ, + display_level=DispLevel.EXPERT, + fget="get_piezoADC" + ) + rd = attribute( + label="Rain detector", + dtype=float, unit="%", + access=AttrWriteType.READ, + fget="get_rd" + ) + piezo = attribute( + label="Piezo sensor", + dtype=float, + access=AttrWriteType.READ, + fget="get_piezo" + ) + + bat1Coef = attribute( + label="Bat1Coef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_bat1coef", fset="set_bat1coef" + ) + bat2Coef = attribute( + label="Bat2Coef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_bat2coef", fset="set_bat2coef" + ) + pv1Coef = attribute( + label="PV1Coef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_pv1coef", fset="set_pv1coef" + ) + pv2Coef = attribute( + label="PV2Coef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_pv2coef", fset="set_pv2coef" + ) + rdCoef = attribute( + label="RDCoef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_rdcoef", fset="set_rdcoef" + ) + piezoCoef = attribute( + label="PiezoCoef", + dtype=float, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_piezocoef", fset="set_piezocoef" + ) + bat1Off = attribute( + label="Bat1Off", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_bat1off", fset="set_bat1off" + ) + bat2Off = attribute( + label="Bat2Off", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_bat2off", fset="set_bat2off" + ) + pv1Off = attribute( + label="PV1Off", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_pv1off", fset="set_pv1off" + ) + pv2Off = attribute( + label="PV2Off", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_pv2off", fset="set_pv2off" + ) + rdOff = attribute( + label="rdOff", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_rdoff", fset="set_rdoff" + ) + piezoOff = attribute( + label="PiezoOff", + dtype=int, + access=AttrWriteType.READ_WRITE, + display_level=DispLevel.EXPERT, + fget="get_piezooff", fset="set_piezooff" + ) + + def init_device(self): + Device.init_device(self) + self.set_state(DevState.INIT) + + self.url = "http://%s:%d/" % (self.address, self.port) + self.data = {} + + self.comm_thread = CommunicationThread(self) + self.comm_thread.start() + + self.set_state(DevState.ON) + + def delete_device(self): + self.comm_thread.stop() + + def get_fw_version(self): + return self.data["status"]["fw_version"] + + def get_eeprom_slots(self): + return self.data["status"]["eeprom_slots"] + + def get_eeprom_writes(self): + return self.data["status"]["eeprom_writes"] + + def get_sht30_status(self): + return self.data["status"]["sht30_status"] + + def get_outA_temp(self): + return self.data["temperature"]["out_a"] + + def get_outB_temp(self): + return self.data["temperature"]["out_b"] + + def get_out_avg_temp(self): + return self.data["temperature"]["out_avg"] + + def get_box_temp(self): + return self.data["temperature"]["box"] + + def get_chip_temp(self): + return self.data["temperature"]["chip"] + + def get_rh(self): + return self.data["humidity"]["rel"] + + def get_ah(self): + return self.data["humidity"]["abs"] + + def get_dew(self): + return self.data["humidity"]["dew"] + + def get_bat1ADC(self): + return self.data["adc"]["bat1"] + + def get_bat2ADC(self): + return self.data["adc"]["bat2"] + + def get_pv1ADC(self): + return self.data["adc"]["pv1"] + + def get_pv2ADC(self): + return self.data["adc"]["pv2"] + + def get_bat1V(self): + return self.data["power"]["bat1"] + + def get_bat2V(self): + return self.data["power"]["bat2"] + + def get_pv1V(self): + return self.data["power"]["pv1"] + + def get_pv2V(self): + return self.data["power"]["pv2"] + + def get_rdADC(self): + return self.data["adc"]["rd"] + + def get_piezoADC(self): + return self.data["adc"]["piezo"] + + def get_rd(self): + return self.data["rain"]["rd"] + + def get_piezo(self): + return self.data["rain"]["piezo"] + + def get_bat1coef(self): + return self.data["config"]["bat1_coef"] + + def set_bat1coef(self, v): + self.comm_thread.write({"bat1_coef": v}) + + def get_bat2coef(self): + return self.data["config"]["bat2_coef"] + + def set_bat2coef(self, v): + self.comm_thread.write({"bat2_coef": v}) + + def get_pv1coef(self): + return self.data["config"]["pv1_coef"] + + def set_pv1coef(self, v): + self.comm_thread.write({"pv1_coef": v}) + + def get_pv2coef(self): + return self.data["config"]["pv2_coef"] + + def set_pv2coef(self, v): + self.comm_thread.write({"pv2_coef": v}) + + def get_bat1off(self): + return self.data["config"]["bat1_off"] + + def set_bat1off(self, v): + self.comm_thread.write({"bat1_off": v}) + + def get_bat2off(self): + return self.data["config"]["bat2_off"] + + def set_bat2off(self, v): + self.comm_thread.write({"bat2_off": v}) + + def get_pv1off(self): + return self.data["config"]["pv1_off"] + + def set_pv1off(self, v): + self.comm_thread.write({"pv1_off": v}) + + def get_pv2off(self): + return self.data["config"]["pv2_off"] + + def set_pv2off(self, v): + self.comm_thread.write({"pv2_off": v}) + + def get_rdcoef(self): + return self.data["config"]["rd_coef"] + + def set_rdcoef(self, v): + self.comm_thread.write({"rd_coef": v}) + + def get_piezocoef(self): + return self.data["config"]["piezo_coef"] + + def set_piezocoef(self, v): + self.comm_thread.write({"piezo_coef": v}) + + def get_rdoff(self): + return self.data["config"]["rd_off"] + + def set_rdoff(self, v): + self.comm_thread.write({"rd_off": v}) + + def get_piezooff(self): + return self.data["config"]["piezo_off"] + + def set_piezooff(self, v): + self.comm_thread.write({"piezo_off": v}) + + +class CommunicationThread(Thread): + def __init__(self, dev): + Thread.__init__(self) + self.dev = dev + self.quit = False + self.write_buffer = None + self.last_read = None + + self.data_buffer = [] + + def run(self): + while not self.quit: + if self.write_buffer is not None: + self._write() + self._read() + self._check_stale_data() + sleep(self.dev.polling_time) + + def _read(self): + try: + r = requests.get(self.dev.url, timeout=self.dev.timeout) + self.dev.data = r.json() + except Exception as e: + print("Read generated exception: %s" % str(e)) + else: + self.last_read = time() + # self.data_buffer.append(data) + # if len(self.data_buffer) > self.dev.buffer_size: + # self.data_buffer.pop(0) + + def _write(self): + requests.post(self.dev.url, json=self.write_buffer) + self.write_buffer = None + + def _check_stale_data(self): + print("checking for stale data") + if self.last_read is not None: + if time() - self.last_read >= self.dev.stale_time: + print("all data stale, resetting") + DeviceProxy(self.dev.get_name()).Init() + self.dev.set_status("Device reset at %s due to stale data." % datetime.now().strftime("%Y-%m-%d/%H:%M:%S.%f/%Z")) + else: + print("not stale") + else: + print("no read yet") + # stale = [] + # if len(self.data_buffer) >= self.dev.buffer_size: + # for k, v in self.data_buffer[0].items(): + # if k in ("status", "config"): + # continue + # print("processing section %s" % k) + # for kk, vv in v.items(): + # print("processing value %s" % kk) + # data = [s[k][kk] for s in self.data_buffer] + # print(data) + # r = self.all_equal(data) + # print("is stale? %s" % str(r)) + # stale.append(r) + # if all(stale): + + # def all_equal(self, iterable): + # g = groupby(iterable) + # return next(g, True) and not next(g, False) + + def write(self, data): + self.write_buffer = data + + def stop(self): + self.quit = True + + +def start(): + run((ArduinoWeatherStation,)) + + +if __name__ == "__main__": + start()