diff --git a/appdaemon/docker/Dockerfile b/appdaemon/docker/Dockerfile index 77e7e39..1056435 100644 --- a/appdaemon/docker/Dockerfile +++ b/appdaemon/docker/Dockerfile @@ -1,10 +1,7 @@ ARG VERSION FROM acockburn/appdaemon:${VERSION} -#log encoding temporary fix -RUN sed -i -e 's/safe_enc = lambda s: str(s).encode("utf-8", "replace").decode("ascii", "replace")/safe_enc = lambda s: str(s)/g' /usr/src/app/appdaemon/utils.py - RUN pip3 install \ - emp_mos_api==0.9 \ + emp_mos_api \ sqlalchemy \ mysqlclient \ No newline at end of file diff --git a/appdaemon/settings/apps/light/kitchen_led_controller.py b/appdaemon/settings/apps/light/kitchen_led_controller.py index f1ac649..8d248a4 100644 --- a/appdaemon/settings/apps/light/kitchen_led_controller.py +++ b/appdaemon/settings/apps/light/kitchen_led_controller.py @@ -4,7 +4,7 @@ import globals class KitchenLedController(hass.Hass): listen_event_handle_list = [] def initialize(self): - if not globals.check_args(self,['watch_entity']): + if not globals.check_args(self,['watch_entity', 'topic']): return self.listen_event_handle_list.append(self.listen_state(self.entity_changed, self.args['watch_entity'])) @@ -15,4 +15,4 @@ class KitchenLedController(hass.Hass): stete = "off" if new == "on": stete = "on" - self.call_service("mqtt/publish", topic = "home/kitchen-led/kitchen-led/state/set", payload = stete) + self.call_service("mqtt/publish", topic = self.args['topic'], payload = stete) diff --git a/appdaemon/settings/apps/light/kitchen_led_controller.yaml b/appdaemon/settings/apps/light/kitchen_led_controller.yaml index 98614ef..e7dcfc1 100644 --- a/appdaemon/settings/apps/light/kitchen_led_controller.yaml +++ b/appdaemon/settings/apps/light/kitchen_led_controller.yaml @@ -5,5 +5,17 @@ kitchen_led_controller: module: kitchen_led_controller class: KitchenLedController watch_entity: light.kitchen + topic: home/kitchen-led/kitchen-led/state/set + global_dependencies: + - globals + +################################# +# LIGHT BUTTONS # +################################# +kitchen_mini_light_controller: + module: kitchen_led_controller + class: KitchenLedController + watch_entity: light.kitchen + topic: home/plug/kitchen-mini-light/status/set global_dependencies: - globals diff --git a/appdaemon/settings/apps/presence/home/report_action.py b/appdaemon/settings/apps/presence/home/report_action.py index 6e02ac7..17f5918 100644 --- a/appdaemon/settings/apps/presence/home/report_action.py +++ b/appdaemon/settings/apps/presence/home/report_action.py @@ -48,4 +48,4 @@ class ReportByPresence(hass.Hass): message = "В доме остались работать следующие устройства:\n" + message self.notify(message, name = self.args['notify']) else: - self.notify("Все устройства в доме выключены.", name = self.args['notify']) + self.notify("Все устройства в доме выключены.", name = self.args['notify']) diff --git a/appdaemon/settings/apps/presence/home/water_action.py b/appdaemon/settings/apps/presence/home/water_action.py index 623df40..da81131 100644 --- a/appdaemon/settings/apps/presence/home/water_action.py +++ b/appdaemon/settings/apps/presence/home/water_action.py @@ -17,12 +17,16 @@ import appdaemon.plugins.hass.hassapi as hass class WaterValveControl(hass.Hass): standby_power_limit = 15 timer = None + listen_event_handle_list = [] + timers = [] + def initialize(self): if 'water_valve' not in self.args or 'water_devices' not in self.args or 'notify' not in self.args: self.error("Please provide water_valve, water_devices and notify in config!") return - self.listen_event(self.away_mode, "away_mode") - self.listen_event(self.return_home_mode, "return_home_mode") + + self.listen_event_handle_list.append(self.listen_event(self.away_mode, "away_mode")) + self.listen_event_handle_list.append(self.listen_event(self.return_home_mode, "return_home_mode")) self.log(self.get_turned_on_devices()) def return_home_mode(self, event_id, event_args, kwargs): @@ -47,7 +51,7 @@ class WaterValveControl(hass.Hass): device_names += self.friendly_name(device) self.notify('В доме остались требующие воды устройства ({}), подача воды не будет отключена.'.format(device_names), name = self.args['notify']) self.cancel_current_timer() - self.timer = self.run_every(self.wait_when_device_is_done, self.datetime(), 5*60) + self.timers.append(self.run_every(self.wait_when_device_is_done, self.datetime(), 5*60)) else: self.turn_off(self.args['water_valve']) for device in self.args['water_devices']: @@ -83,7 +87,7 @@ class WaterValveControl(hass.Hass): return powered_on_devices def cancel_current_timer(self): - if self.timer != None: + for timer in self.timers: self.log('canceling timer') - self.cancel_timer(self.timer) - self.timer = None + self.cancel_timer(timer) + self.timers = [] diff --git a/appdaemon/settings/apps/security/lock.yaml b/appdaemon/settings/apps/security/lock.yaml index 166090d..2f9c009 100644 --- a/appdaemon/settings/apps/security/lock.yaml +++ b/appdaemon/settings/apps/security/lock.yaml @@ -6,6 +6,7 @@ lock: user_65537: Данил (сред) user_65538: Света (указ) user_65539: Мама (указ) + user_65540: Саня notify: telegram global_dependencies: - globals \ No newline at end of file diff --git a/appdaemon/settings/apps/sensors/mos_lib.py b/appdaemon/settings/apps/sensors/mos_lib.py deleted file mode 100644 index 1d85958..0000000 --- a/appdaemon/settings/apps/sensors/mos_lib.py +++ /dev/null @@ -1,628 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function -import requests -from datetime import datetime, tzinfo -from copy import deepcopy - - -class AuthException(Exception): - pass - -class EmpServerException(Exception): - pass - - -class Client(object): - """ - Клиента - """ - def __init__(self, **kwargs): - self.token = kwargs.get('token') - self.guid = kwargs.get('guid') - - #requests - self.verify = kwargs.get('verify', True) - self.timeout = kwargs.get('timeout', 3.0) - - self.session_id = None # emp mos ru - self.user_agent = kwargs.get('user_agent') - self.dev_app_version = kwargs.get('dev_app_version') - self.dev_user_agent = kwargs.get('dev_user_agent') - self.headers = {'Cache-Control': 'no-cache', - 'Host': 'emp.mos.ru', - 'Connection': 'Keep-Alive', - 'Accept-Encoding': 'gzip', - 'User-Agent': self.user_agent} - self.pheaders = deepcopy(self.headers) - self.pheaders.update({ - 'X-Cache-ov': '15552000', - 'X-Cache-ov-mode': 'FORCE_NETWORK' - }) - self.session = requests.Session() - - def raise_for_status(self, answer): - """ - :param answer: JSON - { - u'errorCode': 0, # 0-OK, - 401-Auth error - u'execTime': 0.138262, - u'errorMessage': u'', # error message - u'session_id': u'6c3333333c33333e44e21e7d43c46e03', - u'result': None or JSON - } - :return: - """ - # print('Exec time:', answer['execTime']) - if answer['errorCode'] == 401: - raise AuthException() - - if answer['errorCode'] != 0: - if answer['errorMessage']: - raise EmpServerException(u'{0} (code:{1})'.format(answer['errorMessage'], answer['errorCode'])) - raise EmpServerException(u'code:{0}'.format(answer['errorCode'])) - - def is_active(self): - """ - :return: True если уже залогинился - """ - return self.session_id is not None - - def login(self, telephone, pwd): - """ - :param telephone: Телефон. Вид: 7xxxxxxxxxx - :param pwd: Пароль из приложения Госуслуги Москвы - :return: JSON - { - u'is_filled': True, - u'surname': u'x', - u'name': u'x', - u'session_id': u'6c3333333c33333e44e21e7d43c46e03'}, - u'request_id': u'UN=PRO-12345678-1234-1234-1234-123456789123' - } - """ - login_data = { - 'device_info': { - 'guid': self.guid, - 'user_agent': self.dev_user_agent, - 'app_version': self.dev_app_version - }, - 'auth': { - 'login': telephone, - 'password': pwd, - 'guid': self.guid - } - } - - ret = self.session.post('https://emp.mos.ru/v1.0/auth/virtualLogin', - params={'token': self.token}, - headers={'Content-Type': 'application/json; charset=UTF-8', - 'Connection': 'Keep-Alive', - 'Accept-Encoding': 'gzip', - 'User-Agent': self.user_agent, - 'cache-control': 'no-cache', - 'Host': 'emp.mos.ru', - 'Accept': '*/*'}, - verify=self.verify, - timeout=self.timeout, - json=login_data) - - response = ret.json() - self.raise_for_status(response) - self.session_id = response['session_id'] - return response['result'] - - def get_profile(self): - """ - :return: JSON - { - u'drive_license': None, - u'firstname': u'x', - u'middlename': u'x', - u'lastname': u'x', - u'birthdate': u'dd.mm.YYYY', - u'msisdn': u'71234567890', # your telephone - u'email_confirmed': True, - u'email': u'x@x.ru' - } - """ - assert self.session_id - - ret = self.session.get('https://emp.mos.ru/v1.0/profile/get', - params={'token': self.token, - 'info[guid]': self.guid, - 'auth[session_id]': self.session_id - }, - headers=self.pheaders, - verify=self.verify, - timeout=self.timeout) - - response = ret.json() - self.raise_for_status(response) - return response['result']['profile'] - - def get_flats(self): - """ - :return: JSON array - [{ - u'name': u'x', - u'paycode': u'1234567890', - u'flat_id': u'1234567', - u'electro_account': None, - u'flat_number': u'0', - u'unad': u'0', - u'address': u'x', - u'electro_device': None, - u'unom': u'1234567' # Идентификатор дома - },{...} - ] - """ - assert self.session_id - - ret = self.session.get('https://emp.mos.ru/v1.0/flat/get', - params={'token': self.token, - 'info[guid]': self.guid, - 'auth[session_id]': self.session_id - }, - headers=self.pheaders, - verify=self.verify, - timeout = self.timeout) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def get_watercounters(self, flat_id): - """ - :param flat_id: unicode string from flat_response - response[0]['flat_id'] - :return: - { - u'stat_title': u'x', - u'archive': [ - { - u'cold_indication': 1.55, - u'hot_indication': 1.2, - u'period': u'2017-08-31+03:00' - }, { - u'cold_indication': 1.3, - u'hot_indication': 0.39999999999998, - u'period': u'2017-09-30+03:00' - }, { - +10 эл-тов. Всего 12. - }], - u'counters': [ - { - u'checkup': u'2021-08-08+03:00', # дата поверки - u'counterId': 123456, # id счетчика в emp.mos.ru - u'num': u'123456', # серийный номер счетчика - u'type': 1, # ХВС - u'indications': [ - { - u'indication': u'200.24', - u'period': u'2018-07-31+03:00' - }, { - u'indication': u'200.6', - u'period': u'2018-06-30+03:00' - }, { - u'indication': u'200.5', - u'period': u'2018-05-31+03:00' - }, { - u'indication': u'200.5', - u'period': u'2018-04-30+03:00' - }] - }, - {..} - ] - } - """ - assert self.session_id - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'WATER_COUNTERS', - 'X-Cache-ov-mode': 'DEFAULT', - 'Content-Type': 'application/json; charset=UTF-8' }) - - wcrequest = { - 'flat_id': flat_id, - 'is_widget': False, - 'info': { - 'guid': self.guid - }, - 'auth': { - 'session_id': self.session_id - } - } - - ret = self.session.post('https://emp.mos.ru/v1.0/watercounters/get', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def send_watercounters(self, flat_id, counters_data): - """ - :param flat_id: flat_response['flat_id'] - :param counters_data: array of - [{ - 'counter_id': u'123456', # ['counters'][0]['counterId'] - 'period': datetime.now().strftime("%Y-%m-%d"), - 'indication': 'xxx,xx' - }, { - ... - }] - :return: - """ - assert self.session_id - - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'WATER_COUNTERS', - 'Content-Type': 'application/json; charset=UTF-8' - }) - - wcrequest = {'flat_id': flat_id, - 'counters_data': counters_data, - 'info': { - 'guid': self.guid - }, - 'auth': { - 'session_id': self.session_id - }} - - ret = self.session.post('https://emp.mos.ru/v1.0/watercounters/addValues', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def get_electrocounters(self, flat_id): - """ - :param flat_id: unicode string from flat_response - response[0]['flat_id'] - :return: - { - "address": "город ...", - "electro_account": "851xxxx475", - "electro_device": "04xxx17", - "balance": 0, - "is_debt": false, - "description": "Внимание! Передача показаний возможна с 15 по 26 число месяца. При вводе текущих показаний обращайте внимание на значность счетчиков. В случае если показания введены с неверной значностью (большей, чем значность счетчика), они не будут загружены.", - "sh_znk": 5, - "zones": [{ - "name": "T1", - "value": "6887" - }], - "intervals": [{ - "name": "T1", - "value": "00:00-23:59" - }] - } - """ - assert self.session_id - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'ELECTRO_COUNTERS', - 'X-Cache-ov-mode': 'DEFAULT', - 'Content-Type': 'application/json; charset=UTF-8' }) - - wcrequest = { - 'flat_id': flat_id, - 'is_widget': False, - 'info': { - 'guid': self.guid, - 'user_agent': self.dev_user_agent, - 'app_version': self.dev_app_version - }, - 'auth': { - 'session_id': self.session_id - } - } - - ret = self.session.post('https://emp.mos.ru/v1.0/electrocounters/get', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def send_electrocounters(self, flat_id, counters_data): - """ - :param flat_id: flat_response['flat_id'] - :param counters_data: array of - [{ - 'counter_id': u'123456', # ['counters'][0]['counterId'] - 'period': datetime.now().strftime("%Y-%m-%d"), - 'indication': 'xxx,xx' - }, { - ... - }] - :return: - """ - assert self.session_id - - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'ELECTRO_COUNTERS', - 'Content-Type': 'application/json; charset=UTF-8' - }) - - wcrequest = {'flat_id': flat_id, - 'counters_data': counters_data, - 'info': { - 'guid': self.guid, - 'user_agent': self.dev_user_agent, - 'app_version': self.dev_app_version - }, - 'auth': { - 'session_id': self.session_id - }} - ret = self.session.post('https://emp.mos.ru/v1.0/electrocounters/addValues', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def get_epd(self, flat_id, period, is_debt=True): - """ - :param flat_id: unicode string from flat_response - response[0]['flat_id'] - :param period: unicode string represents date in 27.09.2018 format - :param is_debt: True/False - :return: - { - "is_debt": true, - "is_paid": true, - "amount": 2982.77, - "service_code": "emp.zkh", - "insurance": 61.93, - "ammount_insurance": 3044.7 - } - """ - assert self.session_id - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'EPD', - 'X-Cache-ov-mode': 'DEFAULT', - 'Content-Type': 'application/json; charset=UTF-8' }) - - wcrequest = { - 'flat_id': flat_id, - 'period': period, - 'is_debt': is_debt, - 'info': { - 'guid': self.guid - }, - 'auth': { - 'session_id': self.session_id - } - } - - ret = self.session.post('https://emp.mos.ru/v1.1/epd/get', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def get_car_fines(self, sts_number): - """ - :param sts_number: unicode string contains car sts_numer - :return: - { - "paid": [{ - "seriesAndNumber": "xxxxx", - "date": "2018-08-30+03:00", - "offence_place": "МОСКВА Г. МКАД,xxxx, ВНЕШНЯЯ СТОРОНА", - "offenceType": "12.9ч.2 - Превышение установленной скорости движения транспортного средства на величину от 20 до 40 километров в час ", - "cost": "500", - "is_discount": false, - "drive_license": null, - "sts_number": "xxxxxxx", - "executionState": "Исполнено", - "is_fssp": false - }], - "unpaid": [] - } - """ - assert self.session_id - wheaders = deepcopy(self.headers) - wheaders.update({ - 'X-Clears-tags': 'FORCE_NETWORK', - 'X-Cache-ov-mode': 'DEFAULT', - 'Content-Type': 'application/json; charset=UTF-8' }) - - wcrequest = { - 'sts_number': sts_number, - 'info': { - 'guid': self.guid - }, - 'auth': { - 'session_id': self.session_id - } - } - - ret = self.session.post('https://emp.mos.ru/v1.0/offence/getOffence', - params={'token': self.token}, - headers=wheaders, - verify=self.verify, - timeout=self.timeout, - json=wcrequest) - - response = ret.json() - self.raise_for_status(response) - return response['result'] - - def logout(self, timeout=None): - """ - Почему то очень долго выполняется (5 сек) - """ - if self.session_id: - logout_data = { - 'info': { - 'guid': self.guid - }, - 'auth': { - 'session_id': self.session_id - } - } - ret = self.session.post('https://emp.mos.ru/v1.0/auth/logout', - params={'token': self.token}, - headers=self.headers, - timeout=timeout or self.timeout, - json=logout_data) - - response = ret.json() - self.raise_for_status(response) - self.session_id = None - return response['result'] - - -class MosAPI(object): - """ - kwargs: - - token: уникальный ключ приложения - guid: некий уникальный ключ - https_verify: ключ verify в GET, POST запросах, по умолчанию 'False' - timeout: ключ timeout в GET, POST запросах - user_agent: версия веб клиента - dev_user_agent: 'Android' для ОС Android - dev_app_version: версия ОС - """ - - def __init__(self, **kwargs): - self.kwargs = kwargs - self._clients = {'default': Client(**kwargs)} - - def client(self, client_id='default', **kwargs): - if client_id and \ - client_id not in self._clients: - k = self.kwargs - k.update(kwargs) - self._clients[client_id] = Client(**k) - return self._clients[client_id] - - # if only one client - def is_active(self): - return self.client().is_active() - def login(self, *args): - return self.client().login(*args) - def logout(self, *args): - return self.client().logout(*args) - - def get_profile(self): - return self.client().get_profile() - def get_flats(self): - return self.client().get_flats() - def get_watercounters(self, *args): - return self.client().get_watercounters(*args) - def send_watercounters(self, *args): - return self.client().send_watercounters(*args) - def get_electrocounters(self, *args): - return self.client().get_electrocounters(*args) - def send_electrocounters(self, *args): - return self.client().send_electrocounters(*args) - def get_epd(self, *args): - return self.client().get_epd(*args) - def get_car_fines(self, *args): - return self.client().get_car_fines(*args) - -class Water(): - COLD = 1 - HOT = 2 - - @staticmethod - def water_abbr(water): - if water == Water.COLD: return u'ХВС' - elif water == Water.HOT: return u'ГВС' - - @staticmethod - def name(water): - if water == Water.COLD: return u'холодная вода' - elif water == Water.HOT: return u'горячая вода' - - -class Watercounter(): - """ - [{ 'counterId': 1437373, # внутреннее id счетчика - 'type': 1, # тип воды - 'num': '417944', # серийный номер счетчика - 'checkup': '2023-09-25+03:00', # дата следующей поверки - 'indications': - [{'period': '2018-08-31+03:00', 'indication': '21.38'}, - {'period': '2018-07-31+03:00', 'indication': '20.7'}, - {'period': '2018-06-30+03:00', 'indication': '19'}] - }, - {...} - ] - - list(filter(lambda x: x['num'] == num, response['counters'])) - list(filter(lambda x: x['counterId'] == id, response['counters'])) - list(filter(lambda x: x['type'] == water_type_id, response['counters'])) - """ - - @staticmethod - def last_value(counter): - """ - alphabetical sort data =) - :param counter: counter JSON - :return: float or None - """ - indications = counter['indications'] - indications.sort(key=lambda x: x['period']) - if indications: - return float(indications[-1]['indication']) - - @staticmethod - def water_title(counter): - return Watercounter.humanreadable_name(counter['type']) - - @staticmethod - def checkup(counter): - """ - https://docs.python.org/3/library/datetime.html#datetime.timezone - Если нужно UTC d.replace(tzinfo=None) - - :param counter: counter JSON - :return: datetime с временной зоной - """ - checkup = counter['checkup'] - d = datetime.strptime(checkup, '%Y-%m-%d%z') #'checkup': '2023-09-25+03:00' - return d - - @staticmethod - def serialize_for_send(counter, value): - """ - :param counterId: id счетчика. не номер из приложения! - :param value: значение float - :return: dict - """ - return { - 'counter_id': int(counter['counterId']), - 'period': datetime.now().strftime("%Y-%m-%d"), - 'indication': '{:.2f}'.format(value).replace('.', ',') - } diff --git a/appdaemon/settings/apps/sensors/mosru.py b/appdaemon/settings/apps/sensors/mosru.py index 68a4646..20c03b1 100644 --- a/appdaemon/settings/apps/sensors/mosru.py +++ b/appdaemon/settings/apps/sensors/mosru.py @@ -2,7 +2,8 @@ import globals import datetime as dt from automation import Automation, Base from datetime import datetime, tzinfo, timedelta -from mos_lib import MosAPI, Watercounter, Water, EmpServerException +from emp_mos_api.mos import MosAPI, Watercounter, Water, EmpServerException +#from mos_lib import MosAPI, Watercounter, Water, EmpServerException class WaterCounterSender(Automation): timezone = '+03:00' diff --git a/appdaemon/settings/apps/test/db.yaml b/appdaemon/settings/apps/test/db.yaml index 2f4bfff..2cef4c5 100644 --- a/appdaemon/settings/apps/test/db.yaml +++ b/appdaemon/settings/apps/test/db.yaml @@ -1,9 +1,31 @@ -db: +power_history: module: db class: Db connectionstring: !secret mysql_url entity_id: sensor.power_total + debug: true + global_dependencies: + - automation + + + +counter_hot_history: + module: db + class: Db + connectionstring: !secret mysql_url + entity_id: sensor.counter_hot + + debug: true + global_dependencies: + - automation + +counter_cold_history: + module: db + class: Db + connectionstring: !secret mysql_url + entity_id: sensor.counter_cold + debug: true global_dependencies: - automation \ No newline at end of file diff --git a/appdaemon/settings/apps/timers/airfresher.py b/appdaemon/settings/apps/timers/airfresher.py new file mode 100644 index 0000000..e6aa65a --- /dev/null +++ b/appdaemon/settings/apps/timers/airfresher.py @@ -0,0 +1,50 @@ +import appdaemon.plugins.hass.hassapi as hass +import globals +import datetime +# +# Entity timer +# Turn on and off this entities +# +# Args: +# +# None +# +# Release Notes +# +# Version 1.0: +# Initial Version + +class AirFresher(hass.Hass): + timers = [] + fresh_times = 0 + + def initialize(self): + self.listen_state(self.light_change, 'group.bath_light') + + def light_change(self, entity, attribute, old, new, kwargs): + if (old == "on" and new == "off"): + self.log('Stop freshing timer.') + self.cancel_timers() + + if (old == "off" and new == "on"): + self.log('Start freshing timer.') + self.fresh_times = 0 + newtime = self.datetime()+datetime.timedelta(minutes=3) + self.timers.append(self.run_every(self.timer_tick, newtime, 5*60)) + + def timer_tick(self, args) -> None: + self.fresh() + + def cancel_timers(self) -> None: + for timer in self.timers: + self.cancel_timer(timer) + timers = [] + + def fresh(self): + self.fresh_times = self.fresh_times + 1 + if (self.fresh_times > 5): + self.log('Fresh max times is reached. Canceling timer.') + self.cancel_timers() + return + self.log('freshing in {}'.format(self.fresh_times)) + self.call_service("mqtt/publish", topic = "home/airfresher/airfresher/fresh/set", payload = "1") diff --git a/appdaemon/settings/apps/timers/airfresher.yaml b/appdaemon/settings/apps/timers/airfresher.yaml new file mode 100644 index 0000000..a8bfd6c --- /dev/null +++ b/appdaemon/settings/apps/timers/airfresher.yaml @@ -0,0 +1,8 @@ +airfresher: + module: airfresher + class: AirFresher + # on_time: "09:00:00" + # off_time: "22:00:00" + # entity: switch.ledstrip + global_dependencies: + - globals diff --git a/docker-compose.yml b/docker-compose.yml index 7a88919..2bb45ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,6 @@ services: depends_on: - mysql - mosquitto - - influxdb - broadlink network_mode: "host" restart: always @@ -45,7 +44,7 @@ services: appdaemon: image: monster1025/appdaemon build: - context: ./appdaemon + context: ./appdaemon/docker args: VERSION: 3.0.2 container_name: appdaemon @@ -135,7 +134,7 @@ services: environment: - GF_SECURITY_ADMIN_PASSWORD=grafanapass links: - - "influxdb:influxdb" + - "mysql:mysql" ports: - "3000:3000" restart: always @@ -143,19 +142,3 @@ services: driver: json-file options: max-size: 50m - - influxdb: - image: influxdb - container_name: influxdb - volumes: - - "/etc/localtime:/etc/localtime:ro" - - "/srv/influxdb/db:/var/lib/influxdb" - - "/srv/influxdb/log:/var/log/influxdb" - ports: - - "8083:8083" - - "8086:8086" - restart: always - logging: - driver: json-file - options: - max-size: 50m diff --git a/hass/settings/configuration.yaml b/hass/settings/configuration.yaml index c7a0b25..4fe6c10 100644 --- a/hass/settings/configuration.yaml +++ b/hass/settings/configuration.yaml @@ -50,7 +50,6 @@ switch: !include_dir_merge_list configuration/switch tts: !include_dir_merge_list configuration/tts #one item -influxdb: !include configuration/influxdb.yaml mqtt: !include configuration/mqtt/mqtt.yaml telegram_bot: !include configuration/bot/telegram_bot.yaml vacuum: !include configuration/xiaomi_aqara/vacuum.yaml diff --git a/hass/settings/configuration/group/hidden/light/kitchen_light.yaml b/hass/settings/configuration/group/hidden/light/kitchen_light.yaml index 412c9f7..b9eeca5 100644 --- a/hass/settings/configuration/group/hidden/light/kitchen_light.yaml +++ b/hass/settings/configuration/group/hidden/light/kitchen_light.yaml @@ -3,4 +3,4 @@ kitchen_light: control: hidden entities: - light.kitchen - - switch.kitchen_mini_light \ No newline at end of file + #- switch.kitchen_mini_light \ No newline at end of file diff --git a/hass/settings/configuration/group/home/bath.yaml b/hass/settings/configuration/group/home/bath.yaml index 2d86b08..1be21c1 100644 --- a/hass/settings/configuration/group/home/bath.yaml +++ b/hass/settings/configuration/group/home/bath.yaml @@ -4,4 +4,5 @@ entities: - sensor.temperature_158d00012d9ad7 - sensor.humidity_158d00012d9ad7 - - light.bath \ No newline at end of file + - light.bath + - sensor.airfresher_remain \ No newline at end of file diff --git a/hass/settings/configuration/influxdb.yaml b/hass/settings/configuration/influxdb.yaml deleted file mode 100644 index 5582d56..0000000 --- a/hass/settings/configuration/influxdb.yaml +++ /dev/null @@ -1,12 +0,0 @@ -host: !secret base_localname -port: 8086 -database: hass -max_retries: 3 -default_measurement: state - -include: - domains: - - sensor - - binary_sensor - - switch - diff --git a/hass/settings/configuration/sensor/airfresher.yaml b/hass/settings/configuration/sensor/airfresher.yaml new file mode 100644 index 0000000..3ac6ec8 --- /dev/null +++ b/hass/settings/configuration/sensor/airfresher.yaml @@ -0,0 +1,13 @@ +################################################# +# AirFresher # +################################################# +- platform: mqtt + state_topic: 'home/airfresher/airfresher/remain' + name: 'airfresher_remain' + # value_template: '{{ (value | float ) / 1000 }}' + unit_of_measurement: 'times' + +- platform: template + sensors: + airfresher_remain_percent: + value_template: "{{ ((100 / 1400) * (states('sensor.airfresher_remain') | int )) | round(2) }}" \ No newline at end of file diff --git a/hass/settings/configuration/sensor/lux_outside.yaml b/hass/settings/configuration/sensor/lux_outside.yaml deleted file mode 100644 index b26af2d..0000000 --- a/hass/settings/configuration/sensor/lux_outside.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# - platform: template -# sensors: -# lux_outside: -# value_template: '{{0}}' \ No newline at end of file diff --git a/hass/settings/configuration/sensor/power-modbus.yaml b/hass/settings/configuration/sensor/power-modbus.yaml index 44a0bae..b26bf5d 100644 --- a/hass/settings/configuration/sensor/power-modbus.yaml +++ b/hass/settings/configuration/sensor/power-modbus.yaml @@ -2,7 +2,7 @@ sensors: power_main_total: unit_of_measurement: 'kWh' - value_template: "{{ states('sensor.power_total') | float + 4733.6 | round(2) }}" + value_template: "{{ (states('sensor.power_total') | float + 4733.6) | round(2) }}" #4733.6 - разница показаний. - platform: modbus