161 lines
3.9 KiB
Python
161 lines
3.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# A polybar indicator for Project Michurin. Displays an icon along with
|
||
|
# a status message to indicate the status of a plant with a Michurin moisture
|
||
|
# sensor. This is just a proof of concept and may have to be completely
|
||
|
# rewritten soon.
|
||
|
#
|
||
|
# Requires MICHURIN_ENDPOINT and MICHURIN_TOKEN environmental variables to be
|
||
|
# set, or present in `michurin.env` file, for the purpose of communication
|
||
|
# with your Michurin server.
|
||
|
#
|
||
|
# Author: machaerus
|
||
|
# https://gitlab.com/machaerus
|
||
|
|
||
|
from dateutil import parser
|
||
|
from datetime import datetime as dt
|
||
|
from datetime import timedelta
|
||
|
import json
|
||
|
from pathlib import Path
|
||
|
import os
|
||
|
import sys
|
||
|
|
||
|
import pytz
|
||
|
import requests
|
||
|
from dotenv import load_dotenv
|
||
|
|
||
|
|
||
|
class MichurinPolybar:
|
||
|
|
||
|
def __init__(self):
|
||
|
|
||
|
try:
|
||
|
# Load environment
|
||
|
env_path = os.path.join(
|
||
|
os.path.dirname(sys.argv[0]), "michurin.env")
|
||
|
load_dotenv(dotenv_path=env_path)
|
||
|
colors_path = os.path.join(
|
||
|
os.path.dirname(sys.argv[0]), "colors.sh")
|
||
|
load_dotenv(dotenv_path=colors_path)
|
||
|
|
||
|
# server access
|
||
|
self.MICHURIN_TOKEN = os.getenv("MICHURIN_TOKEN")
|
||
|
self.MICHURIN_ENDPOINT = os.getenv("MICHURIN_ENDPOINT")
|
||
|
|
||
|
assert self.MICHURIN_TOKEN is not None
|
||
|
assert self.MICHURIN_ENDPOINT is not None
|
||
|
except AssertionError:
|
||
|
print("Michurin: Environment not loaded!")
|
||
|
sys.exit(1)
|
||
|
|
||
|
# color definitions
|
||
|
self.faded_green = os.getenv("faded_green")
|
||
|
self.faded_yellow = os.getenv("faded_yellow")
|
||
|
self.faded_red = os.getenv("faded_red")
|
||
|
self.RESET = os.getenv("RESET")
|
||
|
# self.RESET = "%{F#3e565e}"
|
||
|
|
||
|
def get_info(self, sensor_id: int):
|
||
|
"""
|
||
|
Fetch the sensor info from the server.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
sensor_id : int
|
||
|
ID of the sensor (plant).
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
name : str
|
||
|
Display name of the sensor (plant).
|
||
|
"""
|
||
|
endpoint = self.MICHURIN_ENDPOINT + f"sensors/{sensor_id}"
|
||
|
headers = {"Authorization": self.MICHURIN_TOKEN}
|
||
|
res = requests.get(endpoint, headers=headers)
|
||
|
|
||
|
assert res.status_code == 200
|
||
|
res_json = res.json()
|
||
|
name = res_json['display_name']
|
||
|
return name
|
||
|
|
||
|
def get_values(self, sensor_id: int):
|
||
|
"""
|
||
|
Fetch the sensor data from the server.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
sensor_id : int
|
||
|
ID of the sensor (plant).
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
moistureRelative : float
|
||
|
Moisture level (latest value).
|
||
|
timestamp : datetime.datetime
|
||
|
Measurement time.
|
||
|
"""
|
||
|
endpoint = self.MICHURIN_ENDPOINT + f"sensors/{sensor_id}/data/latest"
|
||
|
headers = {"Authorization": self.MICHURIN_TOKEN}
|
||
|
res = requests.get(endpoint, headers=headers)
|
||
|
|
||
|
assert res.status_code == 200
|
||
|
res_json = res.json()
|
||
|
# print(res_json)
|
||
|
|
||
|
# value = res_json['data']['value']
|
||
|
moistureRelative = res_json['data']['moistureRelative']
|
||
|
assert type(moistureRelative) == float
|
||
|
timestamp = parser.parse(res_json['data']['timestamp'])
|
||
|
|
||
|
return moistureRelative, timestamp
|
||
|
|
||
|
def show(self, sensor_id: int):
|
||
|
"""
|
||
|
Build and show the indicator.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
sensor_id : int
|
||
|
ID of the sensor (plant) to be displayed.
|
||
|
"""
|
||
|
try:
|
||
|
info = self.get_info(sensor_id)
|
||
|
except Exception as e:
|
||
|
info = None
|
||
|
|
||
|
try:
|
||
|
moistureRelative, timestamp = self.get_values(sensor_id)
|
||
|
except Exception as e:
|
||
|
moistureRelative = None
|
||
|
timestamp = None
|
||
|
|
||
|
if moistureRelative is not None:
|
||
|
value_f = f"{(moistureRelative * 100):.1f}%"
|
||
|
if moistureRelative > 0.5:
|
||
|
plant_indicator = f"{self.faded_green}{self.RESET}"
|
||
|
elif moistureRelative > 0.3:
|
||
|
plant_indicator = f"{self.faded_yellow}{self.RESET}"
|
||
|
else:
|
||
|
plant_indicator = f"{self.faded_red}{self.RESET}"
|
||
|
else:
|
||
|
value_f = "--"
|
||
|
plant_indicator = ""
|
||
|
|
||
|
if timestamp is not None:
|
||
|
now = dt.now(tz=pytz.timezone('utc'))
|
||
|
if now - timestamp > timedelta(hours=3):
|
||
|
plant_indicator += " (offline) "
|
||
|
|
||
|
# print(f"[ мичурин {plant_indicator} {value_f} ]")
|
||
|
return f"{self.RESET}[ {plant_indicator} {value_f} ]"
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
|
||
|
# ID of your plant
|
||
|
SENSOR = 5
|
||
|
|
||
|
mp = MichurinPolybar()
|
||
|
indicator = mp.show(SENSOR)
|
||
|
print(indicator)
|