diff --git a/.gitignore b/.gitignore index 0d20b64..6f44278 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +.vscode \ No newline at end of file diff --git a/draco.py b/draco.py index 9b6897a..013daea 100644 --- a/draco.py +++ b/draco.py @@ -15,21 +15,19 @@ def main(manager) -> int: success = True processes = None pid = os.getpid() - GPIO.cleanup() # cleanup will be made in main application only + GPIO.cleanup() # cleanup will be made in main application only try: # Get static configuration for the program parser = argparse.ArgumentParser() parser.add_argument( - "-c", "--config", - default="config.json", - help="Path to static configuration" + "-c", "--config", default="config.json", help="Path to static configuration" ) - + with open(Path(parser.parse_args().config), "r") as jsonfile: config = json.load(jsonfile) # Creation of Manager proxy and Manager Lock: Manager Server Process with common data for multiprocessing. Not shared memory - system_status_proxy = manager.dict(Status()._asdict()) # create a proxy dict + system_status_proxy = manager.dict(Status()._asdict()) # create a proxy dict system_status_lock = manager.Lock() # shared memory queue q_telegram_log = Queue(maxsize=20) @@ -39,32 +37,46 @@ def main(manager) -> int: # Creation of processes processes = [] # Telegram bot - processes.append(TelegramBot(config=config, - memory_proxy=(system_status_proxy, system_status_lock), - telegram_queue=q_telegram_log, - name="telegram_bot")) + processes.append( + TelegramBot( + config=config, + memory_proxy=(system_status_proxy, system_status_lock), + telegram_queue=q_telegram_log, + name="telegram_bot", + ) + ) # Relay shield - processes.append(GPIOHandler(config=config, - memory_proxy=(system_status_proxy, system_status_lock), - telegram_queue=q_telegram_log, - name="relayshield")) - #System Scheduler - processes.append(SystemScheduler(config=config, - memory_proxy=(system_status_proxy, system_status_lock), - telegram_queue=q_telegram_log, - name="scheduler")) + processes.append( + GPIOHandler( + config=config, + memory_proxy=(system_status_proxy, system_status_lock), + telegram_queue=q_telegram_log, + name="relayshield", + ) + ) + # System Scheduler + processes.append( + SystemScheduler( + config=config, + memory_proxy=(system_status_proxy, system_status_lock), + telegram_queue=q_telegram_log, + name="scheduler", + ) + ) # Start processes for process in processes: process.start() - + # main loop for monitoring processes while success: for process in processes: if process.exitcode == 1: - raise Exception("A critical process exited with error, terminating all other processes") + raise Exception( + "A critical process exited with error, terminating all other processes" + ) sleep(1) - + except Exception as error: print(f"Process {pid} - " + repr(error)) success = False @@ -75,7 +87,8 @@ def main(manager) -> int: [p.kill() for p in processes if p.is_alive()] return int(not success) + if __name__ == "__main__": manager = Manager() exit_code = main(manager) - sys.exit(exit_code) \ No newline at end of file + sys.exit(exit_code) diff --git a/draco/interfaces/relay_shield_interface.py b/draco/interfaces/relay_shield_interface.py index d9d301c..1dc3ef8 100644 --- a/draco/interfaces/relay_shield_interface.py +++ b/draco/interfaces/relay_shield_interface.py @@ -4,11 +4,13 @@ import RPi.GPIO as GPIO from enum import IntEnum, unique + @unique class GPIO_Mode(IntEnum): BCM_MODE = 11 BOARD_MODE = 10 + class KS0212Interface(object): """ KS0212 keyestudio RPI 4-channel Relay Shield interface. @@ -17,10 +19,10 @@ class KS0212Interface(object): ---------- Control Signal: TTL Level Rated Load: - 10A 250VAC - 10A 125VAC - 10A 30VDC - 10A 28VDC + 10A 250VAC + 10A 125VAC + 10A 30VDC + 10A 28VDC Rated Current: 10A(NO) 5A(NC) Max Switching Voltage: 250VAC 30VDC Contact Time: under 10ms @@ -33,16 +35,17 @@ class KS0212Interface(object): J3 GPIO pin: 22 J4 GPIO pin: 6 J5 GPIO pin: 26 - + https://wiki.keyestudio.com/KS0212_keyestudio_RPI_4-channel_Relay_Shield """ + def __init__( self, config: Mapping[str, Any] = {}, memory_proxy: tuple = (), telegram_queue: Queue = None, - name: str = "relayshield" + name: str = "relayshield", ) -> None: """ KS0212 keyestudio RPI 4-channel Relay Shield interface constructor. @@ -89,18 +92,22 @@ def init( if GPIO.getmode() == None: GPIO.setmode(GPIO.BCM) self._gpio_setup(**self._config) - self.Channel = IntEnum('Channel', {"WATERPUMP" : self._config["WaterPump"], - "VALVE1" : self._config["Valve1"], - "VALVE2" : self._config["Valve2"], - "VALVE3" : self._config["Valve3"], - }) + self.Channel = IntEnum( + "Channel", + { + "WATERPUMP": self._config["WaterPump"], + "VALVE1": self._config["Valve1"], + "VALVE2": self._config["Valve2"], + "VALVE3": self._config["Valve3"], + }, + ) except Exception as error: print(f"Process {self._pid} - " + repr(error)) success = False return success - + def step( self, ) -> bool: @@ -124,7 +131,7 @@ def step( except: success = False return success - + def _gpio_setup(self, **kwargs) -> None: """ This method setup as outputs the GPIOs from the json config file. @@ -135,7 +142,7 @@ def _gpio_setup(self, **kwargs) -> None: """ for key in kwargs: GPIO.setup(kwargs[key], GPIO.OUT) - + def handle_relay(self, channel, value): """ Method to handle the relay. It only takes effect if the GPIO HW value has changed. @@ -151,7 +158,7 @@ def handle_relay(self, channel, value): if GPIO.input(channel) != value: GPIO.output(channel, value) self._log(f"{channel.name} set to {value}") - + def start_waterPump(self): """ Set to 1 GPIO waterpump @@ -165,7 +172,7 @@ def stop_waterPump(self): """ GPIO.output(self.Channel.WATERPUMP, 0) self._log(f"{self.Channel.WATERPUMP.name} set to 0") - + def start_valve1(self): """ Set to 1 GPIO valve1 @@ -179,7 +186,7 @@ def stop_valve1(self): """ GPIO.output(self.Channel.VALVE1, 0) self._log(f"{self.Channel.VALVE1.name} set to 0") - + def start_valve2(self): """ Set to 1 GPIO valve2 @@ -193,7 +200,7 @@ def stop_valve2(self): """ GPIO.output(self.Channel.VALVE2, 0) self._log(f"{self.Channel.VALVE2.name} set to 0") - + def start_valve3(self): """ Set to 1 GPIO valve3 @@ -207,12 +214,10 @@ def stop_valve3(self): """ GPIO.output(self.Channel.VALVE3, 0) self._log(f"{self.Channel.VALVE3.name} set to 0") - + def _log(self, msg): """ Logging function that queues message for telegram #TODO: will implement a python logger """ self.telegram_queue.put(f"{__name__.split('.')[-1]}: {msg}") - - \ No newline at end of file diff --git a/draco/interfaces/scheduler_interface.py b/draco/interfaces/scheduler_interface.py index 6db1d8c..711cfda 100644 --- a/draco/interfaces/scheduler_interface.py +++ b/draco/interfaces/scheduler_interface.py @@ -11,22 +11,22 @@ def __init__( config: Mapping[str, Any] = {}, memory_proxy: tuple = (), telegram_queue: Queue = None, - name: str = "scheduler" + name: str = "scheduler", ) -> None: """ - Telegram interface constructor. + Telegram interface constructor. - Parameters - ---------- - config : Mapping[str, Any] - Class configuration map. - memory_proxy: tuple - system_status_proxy - system_status_lock - telegram_queue : Queue - telegram queue to send logging to main user - name: str - name in json file + Parameters + ---------- + config : Mapping[str, Any] + Class configuration map. + memory_proxy: tuple + system_status_proxy + system_status_lock + telegram_queue : Queue + telegram queue to send logging to main user + name: str + name in json file """ config_draco = config.copy() if name in config: @@ -57,19 +57,19 @@ def init( print(f"Process {self._pid} - " + repr(error)) success = False return success - + def step(self) -> None: """ This methods will check status and stop / run schedulers """ print(schedule.get_jobs()) info = self._check_status() - #Only setup the scheduler if value change + # Only setup the scheduler if value change if self.initial_holidays_state != info["holidays"]: self.initial_holidays_state = info["holidays"] self.setup_scheduler(info) schedule.run_pending() - + def setup_scheduler(self, info) -> None: """ Function that is called in step, it setup the schedulers or remove them @@ -78,13 +78,19 @@ def setup_scheduler(self, info) -> None: self._log(f"Setting the holidays schedulers:") self._log(f"- Alive logging") self._log(f"- Summer watering") - schedule.every().day.at('22:00').do(self._alive_logging).tag('all', 'holidays', 'watchdog') - schedule.every().day.at('09:00').do(self._alive_logging).tag('all', 'holidays', 'watchdog') - schedule.every().day.at('20:00').do(self._summer_watering).tag('all', 'holidays', 'watchdog') + schedule.every().day.at("22:00").do(self._alive_logging).tag( + "all", "holidays", "watchdog" + ) + schedule.every().day.at("09:00").do(self._alive_logging).tag( + "all", "holidays", "watchdog" + ) + schedule.every().day.at("20:00").do(self._summer_watering).tag( + "all", "holidays", "watchdog" + ) else: self._log(f"Clearing the holidays schedulers") - schedule.clear('holidays') - + schedule.clear("holidays") + def _check_status(self): """ This method check the system status proxy @@ -93,11 +99,11 @@ def _check_status(self): info = self.system_status_proxy._getvalue() self.system_status_lock.release() return info - + def _alive_logging(self): self._log(f"Ey, I am alive.") - #TODO send a webcam photo through telegram - + # TODO send a webcam photo through telegram + def _summer_watering(self): """ Job that is triggered for watering during the summer @@ -105,10 +111,12 @@ def _summer_watering(self): self._log(f"Summer watering scheduler") self._command_waterPump(value=1) self._command_valve(valve_number=2, value=1) - #15 minutes of watering TODO: put inside configuration - schedule.every().day.at('20:15').do(self._command_waterPump, value=0) - schedule.every().day.at('20:25').do(self._command_valve, valve_number=2, value=0) - + # 15 minutes of watering TODO: put inside configuration + schedule.every().day.at("20:15").do(self._command_waterPump, value=0) + schedule.every().day.at("20:25").do( + self._command_valve, valve_number=2, value=0 + ) + def _command_waterPump(self, value): """ This method request the value of the pump to 'value' @@ -119,7 +127,7 @@ def _command_waterPump(self, value): self._log(f"Request Pump Status to {value}") if value == 0: return schedule.CancelJob - + def _command_valve(self, valve_number, value): """ This method request the value of the valves 1, 2, 3 to 'value' @@ -130,7 +138,7 @@ def _command_valve(self, valve_number, value): self._log(f"Request Valve {valve_number} Status to {value}") if value == 0: return schedule.CancelJob - + def _log(self, msg): """ Logging function that queues message for telegram diff --git a/draco/interfaces/telegram_interface.py b/draco/interfaces/telegram_interface.py index 646f2cc..df1166b 100644 --- a/draco/interfaces/telegram_interface.py +++ b/draco/interfaces/telegram_interface.py @@ -8,16 +8,17 @@ import telepot from telepot.loop import MessageLoop + class TelegramInterface(object): def __init__( self, config: Mapping[str, Any] = {}, memory_proxy: tuple = (), telegram_queue: Queue = None, - name: str = "telegram_bot" + name: str = "telegram_bot", ) -> None: """ - Telegram interface constructor. + Telegram interface constructor. Parameters ---------- @@ -39,12 +40,15 @@ def __init__( self.system_status_proxy = memory_proxy[0] self.system_status_lock = memory_proxy[1] self.telegram_queue = telegram_queue - self.logging_chat_id = int(keyring.get_password(self._config["namespace"], self._config["allowed_users"]["user1"])) + self.logging_chat_id = int( + keyring.get_password( + self._config["namespace"], self._config["allowed_users"]["user1"] + ) + ) self._pid = os.getpid() self._allowed_users = [] self._api_key = "" - def init( self, ) -> bool: @@ -58,15 +62,19 @@ def init( """ success = True try: - self._allowed_users = self._get_allowed_users(**self._config["allowed_users"]) - self._api_key = keyring.get_password(self._config["namespace"], self._config["api"]) + self._allowed_users = self._get_allowed_users( + **self._config["allowed_users"] + ) + self._api_key = keyring.get_password( + self._config["namespace"], self._config["api"] + ) self._bot = telepot.Bot(self._api_key) MessageLoop(self._bot, self._handle).run_as_thread() except Exception as error: print(f"Process {self._pid} - " + repr(error)) success = False return success - + def step_log(self) -> None: """ This methods will check the queue and log the messages from other processes to self.logging_chat_id @@ -76,32 +84,37 @@ def step_log(self) -> None: self._bot.sendMessage(self.logging_chat_id, f"{msg}") except queue.Empty: pass - + def _get_allowed_users(self, **kwargs): """ Parse the dictionary from config file to read and append the allowed users. """ allowed = [] for key in kwargs: - allowed.append(int(keyring.get_password(self._config["namespace"], kwargs[key]))) + allowed.append( + int(keyring.get_password(self._config["namespace"], kwargs[key])) + ) return allowed - + def _handle(self, msg): """ Function that handles the telegram telepot received messages """ chat_id = msg["chat"]["id"] command = msg["text"] - if "@" in command: # to fix messages inside groups + if "@" in command: # to fix messages inside groups command = command.split("@")[0] if chat_id in self._allowed_users: - print (f"Received command {command}") + print(f"Received command {command}") if command == "/random": - self._bot.sendMessage(chat_id, random.randint(1,6)) + self._bot.sendMessage(chat_id, random.randint(1, 6)) elif command == "/date": self._bot.sendMessage(chat_id, str(datetime.datetime.now())) elif command == "/photo": - self._bot.sendPhoto(chat_id, "https://sklad500.ru/wp-content/uploads/2019/09/teleport02-1000x526.jpeg") + self._bot.sendPhoto( + chat_id, + "https://sklad500.ru/wp-content/uploads/2019/09/teleport02-1000x526.jpeg", + ) elif command == "/status": self._check_status(chat_id) elif command == "/pump": @@ -114,7 +127,7 @@ def _handle(self, msg): self._toggle_valve(chat_id, 3) elif command == "/holidays": self._toggle_holidays(chat_id) - + def _check_status(self, chat_id): """ This method sends to the bot the system status data @@ -122,27 +135,36 @@ def _check_status(self, chat_id): self.system_status_lock.acquire() info = self.system_status_proxy._getvalue() self.system_status_lock.release() - self._bot.sendMessage(chat_id, "*__System Status__*", parse_mode= "MarkdownV2") + self._bot.sendMessage(chat_id, "*__System Status__*", parse_mode="MarkdownV2") for key in info: self._bot.sendMessage(chat_id, f"{key}: {info[key]}") - + def _toggle_pump(self, chat_id): """ This method toggle the value of the pump """ self.system_status_lock.acquire() - self.system_status_proxy["waterpump"] = int(not self.system_status_proxy["waterpump"]) - self._bot.sendMessage(chat_id, f"{__name__.split('.')[-1]}: Request Pump Status to {self.system_status_proxy['waterpump']}") + self.system_status_proxy["waterpump"] = int( + not self.system_status_proxy["waterpump"] + ) + self._bot.sendMessage( + chat_id, + f"{__name__.split('.')[-1]}: Request Pump Status to {self.system_status_proxy['waterpump']}", + ) self.system_status_lock.release() - - + def _toggle_valve(self, chat_id, valve_number): """ This method toggle the value of the valves 1, 2, 3 """ self.system_status_lock.acquire() - self.system_status_proxy[f"valve{valve_number}"] = int(not self.system_status_proxy[f"valve{valve_number}"]) - self._bot.sendMessage(chat_id, f"{__name__.split('.')[-1]}: Request Valve {valve_number} Status to {self.system_status_proxy[f'valve{valve_number}']}") + self.system_status_proxy[f"valve{valve_number}"] = int( + not self.system_status_proxy[f"valve{valve_number}"] + ) + self._bot.sendMessage( + chat_id, + f"{__name__.split('.')[-1]}: Request Valve {valve_number} Status to {self.system_status_proxy[f'valve{valve_number}']}", + ) self.system_status_lock.release() def _toggle_holidays(self, chat_id): @@ -150,6 +172,11 @@ def _toggle_holidays(self, chat_id): This method toggle the value of the holidays mode """ self.system_status_lock.acquire() - self.system_status_proxy["holidays"] = int(not self.system_status_proxy["holidays"]) - self._bot.sendMessage(chat_id, f"{__name__.split('.')[-1]}: Request Holidays Mode to {self.system_status_proxy['holidays']}") - self.system_status_lock.release() \ No newline at end of file + self.system_status_proxy["holidays"] = int( + not self.system_status_proxy["holidays"] + ) + self._bot.sendMessage( + chat_id, + f"{__name__.split('.')[-1]}: Request Holidays Mode to {self.system_status_proxy['holidays']}", + ) + self.system_status_lock.release() diff --git a/draco/processors/GPIO_handler.py b/draco/processors/GPIO_handler.py index e07b6c8..cc3997a 100644 --- a/draco/processors/GPIO_handler.py +++ b/draco/processors/GPIO_handler.py @@ -4,13 +4,14 @@ import sys, os from draco.interfaces.relay_shield_interface import KS0212Interface + class GPIOHandler(Process): def __init__( self, config: Mapping[str, Any], memory_proxy: tuple, telegram_queue: Queue, - name: str + name: str, ) -> None: """ Data consumer constructor. @@ -36,24 +37,26 @@ def __init__( self.telegram_queue = telegram_queue self._name = name - def run( - self - ) -> None: + def run(self) -> None: success = False relayit = None pid = os.getpid() try: - relayit = KS0212Interface(config=self._config, - memory_proxy=(self.system_status_proxy, self.system_status_lock), - telegram_queue=self.telegram_queue, - name=self._name) + relayit = KS0212Interface( + config=self._config, + memory_proxy=(self.system_status_proxy, self.system_status_lock), + telegram_queue=self.telegram_queue, + name=self._name, + ) while not success: success = relayit.init() sleep(0.1) print(f"'{self._name}' - {pid} successfully initialized") - self.telegram_queue.put(f"Process {pid} \- '{self._name}' successfully initialized") + self.telegram_queue.put( + f"Process {pid} \- '{self._name}' successfully initialized" + ) while True: - #Update GPIO values + # Update GPIO values relayit.step() sleep(1) diff --git a/draco/processors/system_scheduler.py b/draco/processors/system_scheduler.py index f226d62..61d2174 100644 --- a/draco/processors/system_scheduler.py +++ b/draco/processors/system_scheduler.py @@ -4,13 +4,14 @@ import sys, os from draco.interfaces.scheduler_interface import SchedulerInterface + class SystemScheduler(Process): def __init__( self, config: Mapping[str, Any], memory_proxy: tuple, telegram_queue: Queue, - name: str + name: str, ) -> None: """ Data consumer constructor. @@ -36,24 +37,26 @@ def __init__( self.telegram_queue = telegram_queue self._name = name - def run( - self - ) -> None: + def run(self) -> None: success = False schedit = None pid = os.getpid() try: - schedit = SchedulerInterface(config=self._config, - memory_proxy=(self.system_status_proxy, self.system_status_lock), - telegram_queue=self.telegram_queue, - name=self._name) + schedit = SchedulerInterface( + config=self._config, + memory_proxy=(self.system_status_proxy, self.system_status_lock), + telegram_queue=self.telegram_queue, + name=self._name, + ) while not success: success = schedit.init() sleep(0.2) print(f"'{self._name}' - {pid} successfully initialized") - self.telegram_queue.put(f"Process {pid} \- '{self._name}' successfully initialized") + self.telegram_queue.put( + f"Process {pid} \- '{self._name}' successfully initialized" + ) while True: - #Update scheduler + # Update scheduler schedit.step() sleep(1) diff --git a/draco/processors/telegram_bot.py b/draco/processors/telegram_bot.py index 65e6f16..5abd6d6 100644 --- a/draco/processors/telegram_bot.py +++ b/draco/processors/telegram_bot.py @@ -5,13 +5,14 @@ import sys, os from draco.interfaces.telegram_interface import TelegramInterface + class TelegramBot(Process): def __init__( self, config: Mapping[str, Any], memory_proxy: tuple, telegram_queue: Queue, - name: str + name: str, ) -> None: """ Data consumer constructor. @@ -36,22 +37,24 @@ def __init__( self.telegram_queue = telegram_queue self._name = name - def run( - self - ) -> None: + def run(self) -> None: success = False teleti = None pid = os.getpid() try: - teleti = TelegramInterface(config=self._config, - memory_proxy=(self.system_status_proxy, self.system_status_lock), - telegram_queue=self.telegram_queue, - name=self._name) + teleti = TelegramInterface( + config=self._config, + memory_proxy=(self.system_status_proxy, self.system_status_lock), + telegram_queue=self.telegram_queue, + name=self._name, + ) while not success: success = teleti.init() sleep(0.5) print(f"telegram bot '{self._name}' - {pid} successfully initialized") - self.telegram_queue.put(f"Process {pid} \- telegram bot '{self._name}' successfully initialized") + self.telegram_queue.put( + f"Process {pid} \- telegram bot '{self._name}' successfully initialized" + ) while True: # Log to telegram teleti.step_log() @@ -61,7 +64,7 @@ def run( print(f"Process {pid} - " + repr(error)) success = False finally: - pass # close telegram bot + pass # close telegram bot exit_code = int(not success) - sys.exit(exit_code) \ No newline at end of file + sys.exit(exit_code) diff --git a/draco/utils/types.py b/draco/utils/types.py index 9ee4918..a8028d2 100644 --- a/draco/utils/types.py +++ b/draco/utils/types.py @@ -1,14 +1,15 @@ from typing import NamedTuple + class Status(NamedTuple): """ Status object to be shared in multiprocessing manager. """ - #HW GPIOs status + + # HW GPIOs status waterpump: int = 0 valve1: int = 0 valve2: int = 0 valve3: int = 0 - #mode status + # mode status holidays: int = 0 - diff --git a/tests/test_GPIO_relay.py b/tests/test_GPIO_relay.py index 57ce0a5..afcb1bb 100644 --- a/tests/test_GPIO_relay.py +++ b/tests/test_GPIO_relay.py @@ -5,13 +5,14 @@ @unique class Channel(IntEnum): - VALVE1 = 4 #J2 - VALVE2 = 22 #J3 - VALVE3 = 6 #J4 - VALVE4 = 26 #J5 + VALVE1 = 4 # J2 + VALVE2 = 22 # J3 + VALVE3 = 6 # J4 + VALVE4 = 26 # J5 + class Times(IntEnum): - WATER_S = 5 # watering time [s] + WATER_S = 5 # watering time [s] # GPIO setup @@ -22,20 +23,19 @@ class Times(IntEnum): GPIO.setup(Channel.VALVE3, GPIO.OUT) GPIO.setup(Channel.VALVE4, GPIO.OUT) try: + GPIO.output(Channel.VALVE1, True) + GPIO.output(Channel.VALVE2, True) + GPIO.output(Channel.VALVE3, True) + GPIO.output(Channel.VALVE4, True) - GPIO.output(Channel.VALVE1,True) - GPIO.output(Channel.VALVE2,True) - GPIO.output(Channel.VALVE3,True) - GPIO.output(Channel.VALVE4,True) - sleep(Times.WATER_S) - GPIO.output(Channel.VALVE1,False) - GPIO.output(Channel.VALVE2,False) - GPIO.output(Channel.VALVE3,False) - GPIO.output(Channel.VALVE4,False) + GPIO.output(Channel.VALVE1, False) + GPIO.output(Channel.VALVE2, False) + GPIO.output(Channel.VALVE3, False) + GPIO.output(Channel.VALVE4, False) finally: - GPIO.output(Channel.VALVE1,False) - GPIO.output(Channel.VALVE2,False) - GPIO.output(Channel.VALVE3,False) - GPIO.output(Channel.VALVE4,False) - GPIO.cleanup() \ No newline at end of file + GPIO.output(Channel.VALVE1, False) + GPIO.output(Channel.VALVE2, False) + GPIO.output(Channel.VALVE3, False) + GPIO.output(Channel.VALVE4, False) + GPIO.cleanup() diff --git a/tests/test_corleone.py b/tests/test_corleone.py index 6d46d52..d7bf0de 100644 --- a/tests/test_corleone.py +++ b/tests/test_corleone.py @@ -2,72 +2,83 @@ import random import datetime import telepot -#import board -#import adafruit_dht + +# import board +# import adafruit_dht from telepot.loop import MessageLoop -#from pprint import pprint -#import pandas as pd + +# from pprint import pprint +# import pandas as pd import keyring API_KEY = keyring.get_password("draco", "TELEGRAM_API_KEY") USER_ID = int(keyring.get_password("draco", "CHAT_ID_USER1")) GROUP_ID = int(keyring.get_password("draco", "CHAT_ID_GROUP1")) + def allowed_users(*args): allowed = [] for item in args: allowed.append(item) return allowed + + allowed = allowed_users(USER_ID, GROUP_ID) -#DHT11 temperature and humidity sensor: -#https://github.com/adafruit/Adafruit_CircuitPython_DHT +# DHT11 temperature and humidity sensor: +# https://github.com/adafruit/Adafruit_CircuitPython_DHT # Initial the dht device, with data pin connected to: -#dhtDevice = adafruit_dht.DHT11(board.D4) -#df = pd.DataFrame(columns=['Date', 'Temperature', 'Humidity']) +# dhtDevice = adafruit_dht.DHT11(board.D4) +# df = pd.DataFrame(columns=['Date', 'Temperature', 'Humidity']) # ~ def get_DHT(): - # ~ temperature_c = None - # ~ while temperature_c == None: - # ~ try: - # ~ # Print the values to the serial port - # ~ temperature_c = dhtDevice.temperature - # ~ #emperature_f = temperature_c * (9 / 5) + 32 - # ~ humidity = dhtDevice.humidity - # ~ return temperature_c, humidity - # ~ except RuntimeError as error: - # ~ # Errors happen fairly often, DHT's are hard to read, just keep going - # ~ print(error.args[0]) - # ~ time.sleep(2.0) - # ~ continue - # ~ except Exception as error: - # ~ dhtDevice.exit() - # ~ raise error +# ~ temperature_c = None +# ~ while temperature_c == None: +# ~ try: +# ~ # Print the values to the serial port +# ~ temperature_c = dhtDevice.temperature +# ~ #emperature_f = temperature_c * (9 / 5) + 32 +# ~ humidity = dhtDevice.humidity +# ~ return temperature_c, humidity +# ~ except RuntimeError as error: +# ~ # Errors happen fairly often, DHT's are hard to read, just keep going +# ~ print(error.args[0]) +# ~ time.sleep(2.0) +# ~ continue +# ~ except Exception as error: +# ~ dhtDevice.exit() +# ~ raise error + def handle(msg): global df - chat_id = msg['chat']['id'] - command = msg['text'] - #pprint(msg) + chat_id = msg["chat"]["id"] + command = msg["text"] + # pprint(msg) if chat_id in allowed: - print ('Got command: %s' % command) - if command == '/random': - bot.sendMessage(chat_id, random.randint(1,6)) - elif command == '/date': + print("Got command: %s" % command) + if command == "/random": + bot.sendMessage(chat_id, random.randint(1, 6)) + elif command == "/date": bot.sendMessage(chat_id, str(datetime.datetime.now())) - elif command == '/photo': - bot.sendPhoto(chat_id, "https://sklad500.ru/wp-content/uploads/2019/09/teleport02-1000x526.jpeg") - #elif command == '/status': - #current_temperature, current_humidity=get_DHT() - #bot.sendMessage(chat_id, "Temperature: "+str(current_temperature)+" ºC") - #bot.sendMessage(chat_id, "Humedad: "+str(current_humidity)+" %") - #df = df.append({'Date': str(datetime.datetime.now()), 'Temperature': current_temperature, 'Humidity': current_humidity}, ignore_index=True) - #elif command == '/save': - #print("Logging content: ", df, sep='\n') - #df.to_csv("datos.csv",index = False) + elif command == "/photo": + bot.sendPhoto( + chat_id, + "https://sklad500.ru/wp-content/uploads/2019/09/teleport02-1000x526.jpeg", + ) + # elif command == '/status': + # current_temperature, current_humidity=get_DHT() + # bot.sendMessage(chat_id, "Temperature: "+str(current_temperature)+" ºC") + # bot.sendMessage(chat_id, "Humedad: "+str(current_humidity)+" %") + # df = df.append({'Date': str(datetime.datetime.now()), 'Temperature': current_temperature, 'Humidity': current_humidity}, ignore_index=True) + # elif command == '/save': + # print("Logging content: ", df, sep='\n') + # df.to_csv("datos.csv",index = False) + + bot = telepot.Bot(API_KEY) MessageLoop(bot, handle).run_as_thread() -print ('I am listening ...') +print("I am listening ...") while 1: time.sleep(10) diff --git a/tests/test_keyring.py b/tests/test_keyring.py index 377064e..a43415e 100644 --- a/tests/test_keyring.py +++ b/tests/test_keyring.py @@ -16,7 +16,9 @@ # a3491fb2-000f-4d9f-943e-127cfe29c39c cred = keyring.get_credential(NAMESPACE, ENTRY) -print(f"Password for username {cred.username} in namespace {NAMESPACE} is {cred.password}") +print( + f"Password for username {cred.username} in namespace {NAMESPACE} is {cred.password}" +) # Password for username API_KEY in namespace my-app is a3491fb2-000f-4d9f-943e-127cfe29c39c keyring.set_password(NAMESPACE, "TELEGRAM_API_KEY", "XXXXXXX") diff --git a/tests/test_main_examples_TobeDeleted.py b/tests/test_main_examples_TobeDeleted.py index fda1f66..f3cc50b 100644 --- a/tests/test_main_examples_TobeDeleted.py +++ b/tests/test_main_examples_TobeDeleted.py @@ -3,23 +3,27 @@ from enum import IntEnum, unique import adafruit_dht + @unique class Channel(IntEnum): MOISTURE = 1 VALVE = 2 - + + class Times(IntEnum): - WATER_S = 15 # watering time [s] - INTERVAL_H = 48*60*60 # water interval [h] + WATER_S = 15 # watering time [s] + INTERVAL_H = 48 * 60 * 60 # water interval [h] + # GPIO setup GPIO.setmode(GPIO.BOARD) GPIO.setup(Channel.MOISTURE, GPIO.IN) GPIO.setup(Channel.VALVE, GPIO.OUT) -#dht setup +# dht setup dhtDevice = adafruit_dht.DHT11(board.D18) + def get_temp(): try: temperature_c = dhtDevice.temperature @@ -32,6 +36,7 @@ def get_temp(): dhtDevice.exit() raise error + def get_humidity(): try: humidity = dhtDevice.humidity @@ -43,20 +48,22 @@ def get_humidity(): except Exception as error: dhtDevice.exit() raise error - + + def main(): try: while True: if GPIO.input(Channel.MOISTURE) == True: - GPIO.output(Channel.VALVE,True) + GPIO.output(Channel.VALVE, True) sleep(Times.WATER_S) - GPIO.output(Channel.VALVE,False) + GPIO.output(Channel.VALVE, False) sleep(Times.INTERVAL_H) else: sleep(1) finally: - #cleanup the GPIO pins before ending + # cleanup the GPIO pins before ending GPIO.cleanup() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/tests/test_manager.py b/tests/test_manager.py index 64737e0..155e894 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -2,15 +2,18 @@ from multiprocessing import Manager, Process from typing import NamedTuple + class Status(NamedTuple): """ Status object to be shared in multiprocessing manager. """ + waterpump: int = -1 valve1: int = -1 valve1: int = -1 valve3: int = -1 + def print_values(my_lock, status_proxy): while True: my_lock.acquire() @@ -19,7 +22,8 @@ def print_values(my_lock, status_proxy): for key in info: print(f"{key}: {status_proxy[key]}") sleep(0.1) - + + def modify_waterpump(my_lock, status_proxy): while True: my_lock.acquire() @@ -32,14 +36,15 @@ def modify_waterpump(my_lock, status_proxy): sleep(0.3) -if __name__ == '__main__': +if __name__ == "__main__": my_dict = Status() my_dict = my_dict._asdict() - - manager = Manager() # create a manager - status_proxy = manager.dict(my_dict) # create a proxy dict - my_lock = manager.Lock() #create lock object to be able to operate with the status proxy in multiprocess (read and write in multiple process without memory breaking) + manager = Manager() # create a manager + status_proxy = manager.dict(my_dict) # create a proxy dict + my_lock = ( + manager.Lock() + ) # create lock object to be able to operate with the status proxy in multiprocess (read and write in multiple process without memory breaking) # creating new processes p1 = Process(target=modify_waterpump, args=(my_lock, status_proxy)) diff --git a/tests/test_schedule.py b/tests/test_schedule.py index 1ea795b..f06b9be 100644 --- a/tests/test_schedule.py +++ b/tests/test_schedule.py @@ -4,9 +4,11 @@ import schedule import time + def job(): print("I'm working...") + # Run job every 3 second/minute/hour/day/week, # Starting 3 second/minute/hour/day/week from now schedule.every(3).seconds.do(job) @@ -40,17 +42,18 @@ def job(): time.sleep(1) - # Run a job once import schedule import time + def job_that_executes_once(): # Do some work that only needs to happen once... return schedule.CancelJob -schedule.every().day.at('22:30').do(job_that_executes_once) + +schedule.every().day.at("22:30").do(job_that_executes_once) while True: schedule.run_pending() - time.sleep(1) \ No newline at end of file + time.sleep(1)