updating configuration
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import datetime
|
||||
from automation import Automation, Base # type: ignore
|
||||
|
||||
#
|
||||
# App to send email report for low balance
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
# threshold = value below which battery levels are reported and email is sent
|
||||
# always_send = set to 1 to override threshold and force send
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class Balance(Base):
|
||||
def initialize(self):
|
||||
super().initialize()
|
||||
self.check_balance({"force": 1})
|
||||
self.run_daily(self.check_balance, datetime.time(10, 0, 0))
|
||||
|
||||
def check_balance(self, kwargs):
|
||||
# if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']):
|
||||
# return
|
||||
|
||||
values = {}
|
||||
low = []
|
||||
for key in self.entity_ids:
|
||||
device = self.entity_ids[key]
|
||||
balance = self.get_state(device)
|
||||
if balance != None and self.is_number(balance):
|
||||
if float(balance) < self.args["threshold"]:
|
||||
low.append(device)
|
||||
values[device] = balance
|
||||
|
||||
message=""
|
||||
name = "Алтуфьево"
|
||||
if 'name' in self.args:
|
||||
name = self.args['name']
|
||||
if low:
|
||||
message += "Пожалуйста пополните баланс ({}): (< {}) \n".format(name, self.args["threshold"])
|
||||
for device in low:
|
||||
message += " - {}: {}\n".format(self.friendly_name(device), values[device])
|
||||
message += "\n\n"
|
||||
|
||||
if low or ("always_send" in self.args and self.args["always_send"] == 1) or ("force" in kwargs and kwargs["force"] == 1):
|
||||
if message != "":
|
||||
self.log(message)
|
||||
self.notify_tg(message) #name = "telegram_monster"
|
||||
|
||||
def is_number(self, s):
|
||||
try:
|
||||
float(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def notify_tg(self, msg):
|
||||
if not 'notify' in self.args:
|
||||
return
|
||||
extra_data = {'parse_mode': 'html'}
|
||||
self.notify(msg, name = self.args['notify'], data=extra_data)
|
||||
@@ -0,0 +1,61 @@
|
||||
#################################
|
||||
# SENSOR BATTERY LOW REMINDER #
|
||||
#################################
|
||||
balance_low_report:
|
||||
class: Balance
|
||||
module: balance
|
||||
constraint: input_boolean.balance_report
|
||||
name: Алтуфьево
|
||||
entity_ids:
|
||||
mosenergosbyt_balance: sensor.power_mosenergosbyt_balance
|
||||
epd_balance: sensor.epd_balance
|
||||
always_send: 0
|
||||
threshold: -100
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
|
||||
balance_low_report_belorechenskaya:
|
||||
class: Balance
|
||||
module: balance
|
||||
constraint: input_boolean.balance_report
|
||||
name: Белореченская
|
||||
entity_ids:
|
||||
mosenergosbyt_balance: sensor.power_mosenergosbyt_belorechenskaya_balance
|
||||
epd_balance: sensor.epd_balance_belorechenskaya
|
||||
always_send: 0
|
||||
threshold: -100
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
|
||||
balance_low_report_fonvizina:
|
||||
class: Balance
|
||||
module: balance
|
||||
constraint: input_boolean.balance_report
|
||||
name: Фонвизина
|
||||
entity_ids:
|
||||
fonvizina_epd_balance: sensor.fonvizina_epd_balance
|
||||
fonvizina_trash_balance: sensor.fonvizina_trash_balance
|
||||
fonvizina_kladovka_balance: sensor.fonvizina_kladovka_balance
|
||||
fonvizina_epd_mosru_balance: sensor.epd_balance_mosru_fonvizina
|
||||
always_send: 0
|
||||
threshold: -100
|
||||
inverted: true
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
|
||||
balance_low_report_milashenkova:
|
||||
class: Balance
|
||||
module: balance
|
||||
constraint: input_boolean.balance_report
|
||||
name: Милашенкова
|
||||
entity_ids:
|
||||
mosenergosbyt_balance: sensor.power_mosenergosbyt_milashenkova_balance
|
||||
epd_balance: sensor.epd_balance_milashenkova
|
||||
always_send: 0
|
||||
threshold: -100
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
@@ -0,0 +1,65 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
import datetime
|
||||
|
||||
#
|
||||
# App to send email report for devices running low on battery
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
# threshold = value below which battery levels are reported and email is sent
|
||||
# always_send = set to 1 to override threshold and force send
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class Battery(hass.Hass):
|
||||
|
||||
def initialize(self):
|
||||
#self.check_batteries({"force": 1})
|
||||
time = datetime.time(10, 0, 0)
|
||||
self.run_daily(self.check_batteries, time)
|
||||
self.check_batteries(None)
|
||||
|
||||
def check_batteries(self, kwargs):
|
||||
if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']):
|
||||
return
|
||||
|
||||
devices = self.get_state()
|
||||
values = {}
|
||||
low = []
|
||||
for deviceName in devices:
|
||||
device = devices[deviceName]
|
||||
if device == None:
|
||||
continue
|
||||
battery = None
|
||||
if "attributes" in device:
|
||||
if "battery" in device["attributes"]:
|
||||
battery = device["attributes"]["battery"]
|
||||
elif "battery_level" in device["attributes"]:
|
||||
battery = device["attributes"]["battery_level"]
|
||||
elif '_battery' in device['entity_id']:
|
||||
battery = self.get_state(device['entity_id'])
|
||||
if not battery.isdigit():
|
||||
continue
|
||||
battery = int(battery)
|
||||
print('{} = {}'.format(device['entity_id'], battery))
|
||||
if battery != None:
|
||||
print('{} = {}'.format(device['entity_id'], battery))
|
||||
if battery < self.args["threshold"] and battery != 0:
|
||||
low.append(deviceName)
|
||||
values[deviceName] = battery
|
||||
|
||||
message=""
|
||||
if low:
|
||||
message += "У следующих датчиков садится батарея: (< {}) \n".format(self.args["threshold"])
|
||||
for device in low:
|
||||
message += "{}: {}%\n".format(self.friendly_name(device), values[device])
|
||||
message += "\n\n"
|
||||
|
||||
if low: # or ("always_send" in self.args and self.args["always_send"] == 1) or ("force" in kwargs and kwargs["force"] == 1):
|
||||
if message != "":
|
||||
self.notify(message, name = "telegram")
|
||||
@@ -0,0 +1,12 @@
|
||||
#################################
|
||||
# SENSOR BATTERY LOW REMINDER #
|
||||
#################################
|
||||
sensor_battery_low_report:
|
||||
class: Battery
|
||||
module: battery
|
||||
constraint: input_boolean.battery_report
|
||||
always_send: 0
|
||||
threshold: 10
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
@@ -0,0 +1,165 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
import datetime
|
||||
|
||||
class BMSMonitor(hass.Hass):
|
||||
def initialize(self):
|
||||
# Получаем настройки из apps.yaml
|
||||
self.chat_id = self.args.get("chat_id")
|
||||
self.power_entity = self.args.get("power_sensor")
|
||||
self.soc_entity = self.args.get("soc_sensor")
|
||||
self.current_entity = self.args.get("current_sensor")
|
||||
self.voltage_entity = self.args.get("voltage_sensor")
|
||||
self.threshold = float(self.args.get("power_threshold", 5))
|
||||
self.interval = int(self.args.get("discharge_interval", 300))
|
||||
self.notification_target = self.args.get("notification_target", 'telegram')
|
||||
|
||||
# Внутренние переменные состояния
|
||||
self.current_state = "IDLE" # IDLE, CHARGING, DISCHARGING
|
||||
self.timer_handle = None
|
||||
self.was_charging = False
|
||||
|
||||
# Переменная для хранения времени начала отключения
|
||||
self.outage_start_time = None
|
||||
|
||||
# Слушаем изменения мощности
|
||||
self.listen_state(self.on_power_change, self.power_entity)
|
||||
# Слушаем SOC для детектирования 100%
|
||||
self.listen_state(self.on_soc_change, self.soc_entity)
|
||||
|
||||
self.log("BMS Monitor запущен. Ожидание изменений...")
|
||||
|
||||
def on_power_change(self, entity, attribute, old, new, kwargs):
|
||||
try:
|
||||
power = float(new)
|
||||
except (ValueError, TypeError):
|
||||
return
|
||||
|
||||
# Определяем новый режим
|
||||
if power > self.threshold:
|
||||
new_state = "CHARGING"
|
||||
elif power < -self.threshold:
|
||||
new_state = "DISCHARGING"
|
||||
else:
|
||||
new_state = "IDLE"
|
||||
|
||||
# Логика переходов состояний
|
||||
if self.current_state != new_state:
|
||||
self.log(f"Смена состояния BMS: {self.current_state} -> {new_state}")
|
||||
|
||||
# 1. Переход в разрядку (Отключили питание)
|
||||
if new_state == "DISCHARGING" and self.current_state != "DISCHARGING":
|
||||
# Фиксируем время начала отключения
|
||||
self.outage_start_time = datetime.datetime.now()
|
||||
|
||||
self.send_telegram("⚠️ <b>ВНИМАНИЕ: Отключено питание!</b>\nБатарея перешла в режим разрядки.", "html")
|
||||
self.start_discharge_timer()
|
||||
|
||||
# 2. Выход из разрядки (Питание восстановлено)
|
||||
# Срабатывает при переходе DISCHARGING -> IDLE или DISCHARGING -> CHARGING
|
||||
elif self.current_state == "DISCHARGING" and new_state in ("IDLE", "CHARGING"):
|
||||
duration_msg = ""
|
||||
if self.outage_start_time:
|
||||
duration = datetime.datetime.now() - self.outage_start_time
|
||||
duration_msg = f"\n⏱ <b>Время без электричества: {self._format_timedelta(duration)}</b>"
|
||||
self.outage_start_time = None # Сбрасываем таймер
|
||||
|
||||
self.send_telegram(f"✅ <b>Питание восстановлено!</b>\nБатарея вышла из режима разрядки.{duration_msg}", "html")
|
||||
self.stop_discharge_timer()
|
||||
|
||||
# 3. Переход в простой после зарядки (Зарядка окончена)
|
||||
elif new_state == "IDLE" and self.current_state == "CHARGING":
|
||||
self.send_telegram("🔋 <b>Зарядка завершена!</b>\nБатарея полностью заряжена или нагрузка отключена.", "html")
|
||||
|
||||
# 4. Переход IDLE -> CHARGING (если не было разрядки перед этим)
|
||||
elif new_state == "CHARGING" and self.current_state == "IDLE":
|
||||
# Можно добавить отдельное уведомление, если нужно
|
||||
pass
|
||||
|
||||
self.current_state = new_state
|
||||
self.was_charging = (new_state == "CHARGING")
|
||||
|
||||
def on_soc_change(self, entity, attribute, old, new, kwargs):
|
||||
# Дополнительная проверка на 100% SOC, если мощность еще не упала в ноль
|
||||
try:
|
||||
soc = float(new)
|
||||
if soc >= 100 and self.current_state == "CHARGING" and not self.was_charging:
|
||||
# Небольшая защита от повторных уведомлений
|
||||
self.was_charging = True
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
def start_discharge_timer(self):
|
||||
if self.timer_handle is None:
|
||||
# Запускаем таймер: сразу первое сообщение, потом каждые N секунд
|
||||
self.send_status_report()
|
||||
self.timer_handle = self.run_every(self.periodic_status, datetime.datetime.now(), self.interval)
|
||||
self.log("Таймер статуса разрядки запущен")
|
||||
|
||||
def stop_discharge_timer(self):
|
||||
if self.timer_handle is not None:
|
||||
self.cancel_timer(self.timer_handle)
|
||||
self.timer_handle = None
|
||||
self.log("Таймер статуса разрядки остановлен")
|
||||
|
||||
def periodic_status(self, kwargs):
|
||||
# Проверяем, все ли еще в режиме разрядки перед отправкой
|
||||
if self.current_state == "DISCHARGING":
|
||||
self.send_status_report()
|
||||
else:
|
||||
self.stop_discharge_timer()
|
||||
|
||||
def _format_timedelta(self, td):
|
||||
"""Преобразует timedelta в строку вида 'X ч Y мин'"""
|
||||
total_seconds = int(td.total_seconds())
|
||||
hours, remainder = divmod(total_seconds, 3600)
|
||||
minutes, _ = divmod(remainder, 60)
|
||||
|
||||
parts = []
|
||||
if hours > 0:
|
||||
parts.append(f"{hours} ч")
|
||||
parts.append(f"{minutes} мин")
|
||||
|
||||
return " ".join(parts)
|
||||
|
||||
def send_status_report(self):
|
||||
try:
|
||||
# Получаем значения напрямую
|
||||
power_raw = self.get_state(self.power_entity)
|
||||
soc_raw = self.get_state(self.soc_entity)
|
||||
current_raw = self.get_state(self.current_entity)
|
||||
voltage_raw = self.get_state(self.voltage_entity)
|
||||
|
||||
# Преобразуем и округляем средствами Python
|
||||
power = round(float(power_raw), 1) if power_raw not in (None, 'unknown', 'unavailable') else 0
|
||||
soc = round(float(soc_raw), 1) if soc_raw not in (None, 'unknown', 'unavailable') else 0
|
||||
current = round(float(current_raw), 2) if current_raw not in (None, 'unknown', 'unavailable') else 0
|
||||
voltage = round(float(voltage_raw), 2) if voltage_raw not in (None, 'unknown', 'unavailable') else 0
|
||||
|
||||
# Определяем цвет/иконку для направления тока
|
||||
direction = "🔻 Разряд" if power < 0 else "🔺 Заряд"
|
||||
|
||||
# Добавляем время без электричества, если известно
|
||||
outage_info = ""
|
||||
if self.outage_start_time:
|
||||
duration = datetime.datetime.now() - self.outage_start_time
|
||||
outage_info = f"\n⏳ Без света: <b>{self._format_timedelta(duration)}</b>"
|
||||
|
||||
msg = (
|
||||
f"📊 <b>Статус батареи (JK BMS)</b>\n\n"
|
||||
f"🔋 SOC: <b>{soc}%</b>\n"
|
||||
f"⚡ Мощность: <b>{power} Вт</b> ({direction})\n"
|
||||
f"🔌 Ток: <b>{current} А</b>\n"
|
||||
f"🔋 Напряжение: <b>{voltage} В</b>"
|
||||
f"{outage_info}\n"
|
||||
f"🕒 Время: {datetime.datetime.now().strftime('%H:%M:%S')}"
|
||||
)
|
||||
self.send_telegram(msg, "html")
|
||||
except Exception as e:
|
||||
self.log(f"Ошибка при отправке статуса: {e}")
|
||||
|
||||
def send_telegram(self, message, parse_mode="html"):
|
||||
try:
|
||||
extra_data = {'parse_mode': parse_mode}
|
||||
self.notify(message, name = self.notification_target, data=extra_data)
|
||||
except Exception as e:
|
||||
self.log(f"Ошибка отправки Telegram: {e}")
|
||||
@@ -0,0 +1,14 @@
|
||||
bms_monitor:
|
||||
module: bms_monitor
|
||||
class: BMSMonitor
|
||||
# Telegram настройки
|
||||
notification_target: telegram_monster
|
||||
# Сущности из вашей карточки
|
||||
power_sensor: sensor.jk_bms_jk_bms_power
|
||||
soc_sensor: sensor.jk_bms_jk_bms_state_of_charge
|
||||
current_sensor: sensor.jk_bms_jk_bms_current
|
||||
voltage_sensor: sensor.jk_bms_jk_bms_total_voltage
|
||||
# Порог мощности (Вт) для определения режима (чтобы избежать шумов)
|
||||
power_threshold: 5
|
||||
# Интервал оповещений при разрядке (секунды)
|
||||
discharge_interval: 300
|
||||
@@ -0,0 +1,75 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
from datetime import timedelta
|
||||
import datetime
|
||||
|
||||
#
|
||||
# App to show dishwasher state
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
# door_sensor = dishwasher door sensor
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class Dishwasher(hass.Hass):
|
||||
listen_event_handle_list = []
|
||||
run_state = ""
|
||||
|
||||
def initialize(self):
|
||||
if 'run_state' not in self.args or 'door_sensor' not in self.args or 'counter' not in self.args:
|
||||
self.log("Please define all args")
|
||||
return
|
||||
|
||||
self.listen_event_handle_list.append(self.listen_state(self.door_state_change, self.args['door_sensor'])) #, attribute='action'
|
||||
self.listen_event_handle_list.append(self.listen_state(self.run_state_change, self.args['run_state'])) #, attribute='action'
|
||||
|
||||
|
||||
def door_state_change(self, entity, attribute, old, new, kwargs):
|
||||
if (new == "on"):
|
||||
self.call_service("counter/increment", entity_id = self.args['counter'])
|
||||
if (self.run_state == "finished"):
|
||||
self.run_state = "collecting"
|
||||
self.set_light_state_by_counter()
|
||||
|
||||
def run_state_change(self, entity, attribute, old, new, kwargs):
|
||||
self.log("event: {}: {}->{}".format(attribute,old,new))
|
||||
if (new == "Run"):
|
||||
self.log('resetting counter')
|
||||
self.call_service("counter/reset", entity_id = self.args['counter'])
|
||||
self.run_state = "washing"
|
||||
if (new == "Finished"):
|
||||
self.log('resetting counter')
|
||||
self.call_service("counter/reset", entity_id = self.args['counter'])
|
||||
self.run_state = "finished"
|
||||
|
||||
self.log('Current state is: {}'.format(self.run_state))
|
||||
self.set_light_state_by_counter()
|
||||
|
||||
def set_light_state_by_counter(self):
|
||||
state = self.get_state(self.args['counter'])
|
||||
open_count = int(state)
|
||||
self.log('counter state={}, machine state = {}'.format(state, self.run_state))
|
||||
|
||||
if (self.run_state == "finished"):
|
||||
# Есть посуда, которую нужно разобрать
|
||||
# red color
|
||||
self.log('Time to take dishes out.')
|
||||
if 'lamp' in self.args:
|
||||
self.call_service("light/turn_on", entity_id = self.args['lamp'], rgb_color=[255, 0, 0]) #red
|
||||
|
||||
elif (self.run_state == "washing"):
|
||||
self.log('washing in progress')
|
||||
self.call_service("light/turn_off", entity_id = self.args['lamp'])
|
||||
|
||||
elif (open_count > 10):
|
||||
self.log('Time to run dishwasher.')
|
||||
if 'lamp' in self.args:
|
||||
self.call_service("light/turn_on", entity_id = self.args['lamp'], rgb_color=[245, 236, 0]) #yellow
|
||||
|
||||
else:
|
||||
self.call_service("light/turn_off", entity_id = self.args['lamp'])
|
||||
@@ -0,0 +1,13 @@
|
||||
#################################
|
||||
# TEETH BRUSHING REMINDER #
|
||||
#################################
|
||||
dishwasher:
|
||||
class: Dishwasher
|
||||
module: dishwasher
|
||||
constraint: input_boolean.dishwasher_state_light
|
||||
counter: counter.dishwasher_door_open_count
|
||||
door_sensor: binary_sensor.hc_dishwasher_door
|
||||
run_state: sensor.hc_dishwasher_state
|
||||
lamp: light.espresense_kitchen_led_1
|
||||
global_dependencies:
|
||||
- globals
|
||||
@@ -0,0 +1,28 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
import datetime
|
||||
|
||||
#
|
||||
# App to send reports about unsuccessfull auths
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
# threshold = value below which battery levels are reported and email is sent
|
||||
# always_send = set to 1 to override threshold and force send
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class Security(hass.Hass):
|
||||
def initialize(self):
|
||||
self.log('---------------------')
|
||||
self.listen_event_handle_list = []
|
||||
self.listen_event_handle_list.append(self.listen_state(self.state_change, 'persistent_notification.http_login', attribute='message'))
|
||||
|
||||
def state_change(self, entity, attribute, old, new, kwargs):
|
||||
notifier = self.args.get('notify', 'telegram')
|
||||
self.notify(new, name = notifier)
|
||||
self.log(new)
|
||||
@@ -0,0 +1,10 @@
|
||||
#################################
|
||||
# INVALID AUTH REPORT #
|
||||
#################################
|
||||
invalid_auth_report:
|
||||
class: Security
|
||||
module: security
|
||||
alice: media_player.yandex_station_m00r46000m53db
|
||||
notify: telegram
|
||||
global_dependencies:
|
||||
- globals
|
||||
@@ -0,0 +1,82 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
from datetime import timedelta
|
||||
import datetime
|
||||
|
||||
#
|
||||
# App to send email report for devices running low on battery
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
# threshold = value below which battery levels are reported and email is sent
|
||||
# always_send = set to 1 to override threshold and force send
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class Teeth(hass.Hass):
|
||||
listen_event_handle_list = []
|
||||
|
||||
def initialize(self):
|
||||
# Устанавливаем сброс в 10 утра
|
||||
reset_time = datetime.time(10, 0, 0)
|
||||
self.run_daily(self.reset_action, reset_time)
|
||||
|
||||
# Запускаем проверку напоминаний
|
||||
self.timer = self.run_every(self.remind_if_needed, self.datetime()+timedelta(seconds=1), self.args['remind_interval'])
|
||||
|
||||
# Слушаем изменения состояния кнопки
|
||||
self.listen_event_handle_list.append(self.listen_state(self.button_state_change, self.args['mute_button']))
|
||||
|
||||
def button_state_change(self, entity, attribute, old, new, kwargs):
|
||||
self.log("event: {}={}".format(attribute,new))
|
||||
if attribute == 'state' and new == 'single':
|
||||
self.log('Muting notifications')
|
||||
self.set_state(self.args['mute_boolean'], state='on')
|
||||
self.say("Принято")
|
||||
|
||||
def say(self, command):
|
||||
self.log('command = {}'.format(command))
|
||||
self.call_service('media_player/play_media', entity_id=self.args['alice'], media_content_type='text', media_content_id=command)
|
||||
|
||||
def reset_action(self, kwargs):
|
||||
self.log('Resetting mute state')
|
||||
self.set_state(self.args['mute_boolean'], state='off')
|
||||
|
||||
def remind_if_needed(self, kwargs):
|
||||
# Проверяем состояние input_boolean
|
||||
if self.get_state(self.args['mute_boolean']) == 'on':
|
||||
return
|
||||
|
||||
if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']):
|
||||
self.log('disabled by constraint')
|
||||
return
|
||||
|
||||
home_sensors = []
|
||||
if 'home_sensors' in self.args:
|
||||
home_sensors = self.args['home_sensors']
|
||||
|
||||
at_home = False
|
||||
is_any = False
|
||||
for sensor in home_sensors:
|
||||
is_any = True
|
||||
state = self.get_state(sensor)
|
||||
if state != 'not_home':
|
||||
at_home = True
|
||||
if not is_any:
|
||||
at_home = True
|
||||
|
||||
if not at_home:
|
||||
self.log('User is away, skipping notification')
|
||||
return
|
||||
|
||||
notify = "telegram"
|
||||
if "notify" in self.args:
|
||||
notify = self.args["notify"]
|
||||
now = datetime.datetime.now()+timedelta(hours=3)
|
||||
message="Уже {}, а ты еще не почистил зубы. Самое время =)".format(now.strftime("%H:%M"))
|
||||
self.notify(message, name = notify)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#################################
|
||||
# TEETH BRUSHING REMINDER #
|
||||
#################################
|
||||
teeth_danil_brush_reminder:
|
||||
class: Teeth
|
||||
module: teeth_brushing
|
||||
constraint: input_boolean.teeth_brushing_reminder
|
||||
mute_boolean: input_boolean.teeth_muted
|
||||
notify: telegram_monster
|
||||
remind_interval: 1800
|
||||
mute_button: sensor.button_shower_sink_action
|
||||
home_sensors: [person.danil]
|
||||
alice: media_player.yandex_station_ff98f029ab48468d28c557be
|
||||
global_dependencies:
|
||||
- globals
|
||||
@@ -0,0 +1,109 @@
|
||||
import appdaemon.plugins.hass.hassapi as hass
|
||||
import datetime
|
||||
import globals
|
||||
|
||||
#
|
||||
# App to send notifications when counters state needs attention
|
||||
#
|
||||
# Args:
|
||||
#
|
||||
#
|
||||
# None
|
||||
#
|
||||
# Release Notes
|
||||
#
|
||||
# Version 1.0:
|
||||
# Initial Version
|
||||
|
||||
class WaterCountersAlarm(hass.Hass):
|
||||
checkup_threasold = 31
|
||||
send_threasold = 31
|
||||
|
||||
def initialize(self):
|
||||
if not globals.check_args(self, ['checkup_threasold', 'send_threasold']):
|
||||
return
|
||||
self.checkup_threasold = self.args['checkup_threasold']
|
||||
self.send_threasold = self.args['send_threasold']
|
||||
|
||||
self.check_send_dates({"force": 1})
|
||||
self.run_daily(self.check_checkup_dates, datetime.time(10, 0, 0))
|
||||
self.run_daily(self.check_send_dates, datetime.time(10, 1, 0))
|
||||
#self.check_checkup_dates([])
|
||||
|
||||
def check_send_dates(self, kwargs):
|
||||
if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']):
|
||||
return
|
||||
devices = self.get_state()
|
||||
if devices == None:
|
||||
return
|
||||
values = {}
|
||||
send_devices = []
|
||||
for deviceName in devices:
|
||||
device = devices[deviceName]
|
||||
if (device == None):
|
||||
continue
|
||||
send_date = None
|
||||
if "attributes" in device:
|
||||
if "date" in device["attributes"]:
|
||||
send_date = device["attributes"]["date"]
|
||||
if send_date != None:
|
||||
self.log('found device {} with send date {}'.format(deviceName, send_date))
|
||||
if send_date == "":
|
||||
send_devices.append(deviceName)
|
||||
else:
|
||||
send_date_date = None
|
||||
if '-' in send_date:
|
||||
send_date_date = datetime.datetime.strptime(send_date, '%Y-%m-%d')
|
||||
else:
|
||||
send_date_date = datetime.datetime.strptime(send_date, '%d.%m.%Y')
|
||||
if send_date_date < (datetime.datetime.now() - datetime.timedelta(days=self.send_threasold)):
|
||||
send_devices.append(deviceName)
|
||||
values[deviceName] = send_date
|
||||
|
||||
message=""
|
||||
if send_devices:
|
||||
message += "ВНИМАНИЕ! У вас есть счетчики, показания по которым давно не передавались:\n"
|
||||
for device in send_devices:
|
||||
message += "- {}: {}\n".format(self.friendly_name(device), values[device])
|
||||
message += "\n\n"
|
||||
|
||||
if send_devices or ("always_send" in self.args and self.args["always_send"] == 1) or ("force" in kwargs and kwargs["force"] == 1):
|
||||
if message != "":
|
||||
self.notify(message, name = "telegram")
|
||||
|
||||
def check_checkup_dates(self, kwargs):
|
||||
if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']):
|
||||
return
|
||||
|
||||
entities = self.get_state()
|
||||
if entities == None:
|
||||
return
|
||||
|
||||
values = {}
|
||||
checkup_devices = []
|
||||
for entity_id in entities:
|
||||
entity = entities[entity_id]
|
||||
if entity == None:
|
||||
# self.log('not found {} in entities list.'.format(entity_id))
|
||||
continue
|
||||
checkup = None
|
||||
if "attributes" in entity:
|
||||
if "checkup" in entity["attributes"]:
|
||||
checkup = entity["attributes"]["checkup"]
|
||||
if checkup != None:
|
||||
self.log('found device {} with checkup date {}'.format(entity_id, checkup))
|
||||
checkup_date = datetime.datetime.strptime(checkup, '%Y-%m-%d')
|
||||
if (checkup_date - datetime.timedelta(days=self.checkup_threasold)) < datetime.datetime.now():
|
||||
checkup_devices.append(entity_id)
|
||||
values[entity_id] = checkup
|
||||
|
||||
message=""
|
||||
if checkup_devices:
|
||||
message += "ВНИМАНИЕ! У вас есть счетчик, нуждающийся в поверке:\n"
|
||||
for device in checkup_devices:
|
||||
message += "- {}: {}\n".format(self.friendly_name(device), values[device])
|
||||
message += "\n\n"
|
||||
|
||||
if checkup_devices or ("always_send" in self.args and self.args["always_send"] == 1) or ("force" in kwargs and kwargs["force"] == 1):
|
||||
if message != "":
|
||||
self.notify(message, name = "telegram")
|
||||
@@ -0,0 +1,12 @@
|
||||
#################################
|
||||
# WATER COUNTERS ALARM #
|
||||
#################################
|
||||
water_counters:
|
||||
class: WaterCountersAlarm
|
||||
module: water_counters
|
||||
constraint: input_boolean.water_counters_report
|
||||
notify: telegram
|
||||
checkup_threasold: 31
|
||||
send_threasold: 31
|
||||
global_dependencies:
|
||||
- globals
|
||||
Reference in New Issue
Block a user