524 lines
11 KiB
Python
524 lines
11 KiB
Python
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()
|