diff --git a/appdaemon/docker/Dockerfile b/appdaemon/docker/Dockerfile index 1bd411d..106d2bf 100644 --- a/appdaemon/docker/Dockerfile +++ b/appdaemon/docker/Dockerfile @@ -1,5 +1,6 @@ -ARG VERSION -FROM acockburn/appdaemon:${VERSION} +ARG BASE_IMAGE +#FROM acockburn/appdaemon:${BASE_IMAGE} +FROM ${BASE_IMAGE} RUN pip3 install \ attrs==19.1.0 \ @@ -7,4 +8,6 @@ RUN pip3 install \ packaging==16.8 \ python-Levenshtein==0.12.0 \ voluptuous==0.11.5 \ - noolite==1.4.0 && apt-get update -y && apt-get install -y libusb-1.0-0-dev && apt-get clean + noolite==1.4.0 && \ + apk add --no-cache libusb-dev + #apt-get update && apt-get install -y libusb-1.0.0-dev && apt-get clean diff --git a/appdaemon/settings/appdaemon.yaml b/appdaemon/settings/appdaemon.yaml index 683d474..75f4609 100644 --- a/appdaemon/settings/appdaemon.yaml +++ b/appdaemon/settings/appdaemon.yaml @@ -1,17 +1,23 @@ -log: - logfile: STDOUT - errorfile: STDERR +logs: + main_log: + filename: STDOUT appdaemon: threads: 10 + latitude: !secret home_latitude + longitude: !secret home_longitude + elevation: 0 + timezone: !secret timezone api_port: 5000 api_key: !secret ha_password time_zone: !secret timezone narodmon_key: !secret narodmon_key - + + api: + plugins: HASS: type: hass ha_url: !secret ha_url #ha_key: !secret ha_password - token: !secret ha_token \ No newline at end of file + token: !secret ha_token diff --git a/appdaemon/settings/apps/alarm_clock/alarm_clock_action.py b/appdaemon/settings/apps/alarm_clock/alarm_clock_action.py index b7ad124..9087ac6 100644 --- a/appdaemon/settings/apps/alarm_clock/alarm_clock_action.py +++ b/appdaemon/settings/apps/alarm_clock/alarm_clock_action.py @@ -1,5 +1,6 @@ import appdaemon.plugins.hass.hassapi as hass import globals +import datetime # # Very special case, this will run sound 'alarm' on xiaomi gateway based on alarm clock event (alarmclock_trigger) # and stop it when sensor became active. @@ -41,7 +42,7 @@ class AlarmclockAction(hass.Hass): def alarmclock_alarm(self, event_id, event_args, kwargs): self.log('Alarm triggered!') self.volume = 10 - self.timer = self.run_every(self.timer_tick, self.datetime(), 17) + self.timer = self.run_every(self.timer_tick, self.datetime()+datetime.timedelta(seconds=15), 17) def timer_tick(self, kwargs): self.call_service("xiaomi_aqara/play_ringtone", gw_mac=self.gw_mac, ringtone_id=self.ringtone, ringtone_vol=self.volume) diff --git a/appdaemon/settings/apps/alarm_clock/alarm_clock_prealarm.py b/appdaemon/settings/apps/alarm_clock/alarm_clock_prealarm.py index 15ebb69..fc86aca 100644 --- a/appdaemon/settings/apps/alarm_clock/alarm_clock_prealarm.py +++ b/appdaemon/settings/apps/alarm_clock/alarm_clock_prealarm.py @@ -1,4 +1,5 @@ import appdaemon.plugins.hass.hassapi as hass +import datetime # # Pre alarm event. @@ -51,7 +52,7 @@ class AlarmclockPrealarm(hass.Hass): self.log('Prealarm triggered!') self.brightness = 1 self.color_temp = 588 - self.timer = self.run_every(self.timer_tick, self.datetime(), 10) + self.timer = self.run_every(self.timer_tick, self.datetime()+datetime.timedelta(seconds=15), 10) def timer_tick(self, kwargs): attributes = self.get_state(self.args['entity'], attribute = "all") diff --git a/appdaemon/settings/apps/doorbells/doorbell.py b/appdaemon/settings/apps/doorbells/doorbell.py index dbc6fbf..f454da9 100644 --- a/appdaemon/settings/apps/doorbells/doorbell.py +++ b/appdaemon/settings/apps/doorbells/doorbell.py @@ -39,11 +39,11 @@ class Doorbell(hass.Hass): self.notify("Звонок в дверь!!!", name = self.args['notify']) result = self.send_video() - def send_video(self): + def send_video(self): file = '/config/www/doorbell_video.mp4' if os.path.exists(file): os.remove(file) - + self.call_service("stream/record", stream_source=self.args['rtsp'], filename=file, duration=10) self.timer = self.run_in(self.run_in_send_video, 20, file=file) diff --git a/appdaemon/settings/apps/light/lightcontrol.py b/appdaemon/settings/apps/light/lightcontrol.py index 44232c3..77021dd 100644 --- a/appdaemon/settings/apps/light/lightcontrol.py +++ b/appdaemon/settings/apps/light/lightcontrol.py @@ -34,6 +34,17 @@ class LightControl(hass.Hass): # self.log('Sun is up, and we will turn it only after sundown.') return + vacuum_state = "docked" + ha_panel_state = "" + if "vacuum" in self.args: + vacuum_state = self.get_state(self.args["vacuum"]) + if "ha_panel" in self.args: + ha_panel_state = self.get_state(self.args["ha_panel"]) + #Если работает пылесос и сработал датчик движения + if ha_panel_state == "armed_away" and vacuum_state == 'cleaning': + self.log('Motion sensor is triggered, but vacuum is cleaning.') + return + #sensor is off if new == 'off' and old == 'on': self.log('Sensor is off - running timer for {}s.'.format(self.args['timeout'])) diff --git a/appdaemon/settings/apps/light/lightcontrol.yaml b/appdaemon/settings/apps/light/lightcontrol.yaml index d0a586e..b4d4568 100644 --- a/appdaemon/settings/apps/light/lightcontrol.yaml +++ b/appdaemon/settings/apps/light/lightcontrol.yaml @@ -8,6 +8,8 @@ light_control_mainroom: control_entity: group.mainroom_light timeout: 60 constraint: input_boolean.mainroom_light_control + ha_panel: alarm_control_panel.ha_alarm + vacuum: vacuum.xiaomi_vacuum_cleaner global_dependencies: - globals @@ -18,6 +20,8 @@ light_control_kitchen: control_entity: group.kitchen_light timeout: 300 constraint: input_boolean.kitchen_light_control + ha_panel: alarm_control_panel.ha_alarm + vacuum: vacuum.xiaomi_vacuum_cleaner global_dependencies: - globals @@ -28,6 +32,8 @@ light_control_hall: control_entity: group.hall_light timeout: 120 constraint: input_boolean.hall_light_control + ha_panel: alarm_control_panel.ha_alarm + vacuum: vacuum.xiaomi_vacuum_cleaner global_dependencies: - globals diff --git a/appdaemon/settings/apps/notifiers/watchdog.py b/appdaemon/settings/apps/notifiers/watchdog.py index 7243027..564e51b 100644 --- a/appdaemon/settings/apps/notifiers/watchdog.py +++ b/appdaemon/settings/apps/notifiers/watchdog.py @@ -23,7 +23,7 @@ class Watchdog(hass.Hass): def initialize(self): if not globals.check_args(self, ['entity','update_interval', 'timeout']): return - self.timer = self.run_every(self.watchdog_check, self.datetime(), self.args['update_interval']) + self.timer = self.run_every(self.watchdog_check, self.datetime()+timedelta(seconds=10), self.args['update_interval']) def watchdog_check(self, kwargs): if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']): diff --git a/appdaemon/settings/apps/presence/home/water_action.py b/appdaemon/settings/apps/presence/home/water_action.py index da81131..e69088d 100644 --- a/appdaemon/settings/apps/presence/home/water_action.py +++ b/appdaemon/settings/apps/presence/home/water_action.py @@ -1,4 +1,5 @@ import appdaemon.plugins.hass.hassapi as hass +import datetime # # Water control application. @@ -16,6 +17,8 @@ import appdaemon.plugins.hass.hassapi as hass class WaterValveControl(hass.Hass): standby_power_limit = 15 + current_wait_cycle = 0 + wait_cycles = 3 timer = None listen_event_handle_list = [] timers = [] @@ -51,7 +54,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.timers.append(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()+datetime.timedelta(seconds=5), 5*60)) else: self.turn_off(self.args['water_valve']) for device in self.args['water_devices']: @@ -62,13 +65,23 @@ class WaterValveControl(hass.Hass): return powered_on = self.get_turned_on_devices() self.log('still powered on: {}'.format(powered_on)) - if (len(powered_on) == 0): - # self.turn_off(self.args['water_valve']) - # for device in self.args['water_devices']: - # self.turn_off(device) - self.notify('Устройства требующие воды закончили свою работу.', name = self.args['notify']) + if len(powered_on) == 1: + self.current_wait_cycle = 0 + elif len(powered_on) == 0 and self.current_wait_cycle=self.wait_cycles: + self.current_wait_cycle = 0 + self.notify('Устройства требующие воды закончили свою работу, отключаю подачу воды.', name = self.args['notify']) + self.turn_off_water_devices() self.cancel_current_timer() + def turn_off_water_devices(): + self.log("Turning off water devices.") + self.turn_off(self.args['water_valve']) + for device in self.args['water_devices']: + self.turn_off(device) + def get_turned_on_devices(self): powered_on_devices = [] devices = self.args['water_devices'] @@ -80,8 +93,8 @@ class WaterValveControl(hass.Hass): if 'load_power' not in attributes: continue load_power = attributes['load_power'] - self.log('{} load power is: {}'.format(device, load_power)) - if state == 'on' and load_power < self.standby_power_limit: + self.log('{} state: {}, load power is: {}'.format(device, state, load_power)) + if state == 'off' or load_power < self.standby_power_limit: continue powered_on_devices.append(device) return powered_on_devices diff --git a/appdaemon/settings/apps/sensors/mosenergosbyt.py b/appdaemon/settings/apps/sensors/mosenergosbyt.py index 0692dac..dbae8df 100644 --- a/appdaemon/settings/apps/sensors/mosenergosbyt.py +++ b/appdaemon/settings/apps/sensors/mosenergosbyt.py @@ -24,7 +24,7 @@ class MosEnergoSbytSender(Automation): class MosEnergoSbytUpdater(Automation): def initialize(self): super().initialize() - self.timer = self.run_every(self.update_sensors, self.datetime(), 1*60*60) + self.timer = self.run_every(self.update_sensors, self.datetime()+timedelta(seconds=10), 1*60*60) # self.send_mosenergosbyt_counters({}) def update_sensors(self, args) -> None: diff --git a/appdaemon/settings/apps/sensors/mosru.py b/appdaemon/settings/apps/sensors/mosru.py index 32c1e8a..7d67c2c 100644 --- a/appdaemon/settings/apps/sensors/mosru.py +++ b/appdaemon/settings/apps/sensors/mosru.py @@ -161,7 +161,7 @@ class PowerCounterSensor(Automation): class EpdBalanceSensor(Automation): def initialize(self): super().initialize() - self.timer = self.run_every(self.update_sensors, self.datetime(), 1*60*60) + self.timer = self.run_every(self.update_sensors, self.datetime()+timedelta(seconds=40), 1*60*60) def update_sensors(self, args) -> None: if 'epd_balance' in self.entity_ids: diff --git a/appdaemon/settings/apps/sensors/toilet_presence.py b/appdaemon/settings/apps/sensors/toilet_presence.py index be4d709..fd08267 100644 --- a/appdaemon/settings/apps/sensors/toilet_presence.py +++ b/appdaemon/settings/apps/sensors/toilet_presence.py @@ -32,7 +32,7 @@ class ToiletPresence(hass.Hass): def distance_changed(self, entity, attribute, old, new, kwargs): distance = int(new) state = "" - if (distance > 1000 or distance < 850): + if (distance > 1100 or distance < 750): self.timers_off() self.timers.append(self.run_in(self.run_in_presense_off, self.presence_timeout)) self.log('re-setting timer for {}s.'.format(self.presence_timeout)) diff --git a/appdaemon/settings/apps/test.py b/appdaemon/settings/apps/test.py index 207a745..c37ebd3 100644 --- a/appdaemon/settings/apps/test.py +++ b/appdaemon/settings/apps/test.py @@ -1,6 +1,10 @@ +# -*- coding: utf-8 -*- import appdaemon.plugins.hass.hassapi as hass from notification import send_notification import datetime +import sys, locale +import logging, codecs +#import unicode # from automation import Automation # type: ignore """ @@ -10,7 +14,8 @@ Arguments: """ class Test(hass.Hass): def initialize(self) -> None: - self.log('test') + self.log(u"Привет") + self.log(str(u'ô')) # id = send_notification( # self, targets="telegram_monster", message="Repeated message", title="title", when=datetime.datetime.now(), interval=10, iterations=2 # ) @@ -23,4 +28,4 @@ class Test(hass.Hass): self.log('terminate') # for listen_event_handle in self.listen_event_handle_list: # self.cancel_listen_event(listen_event_handle) - \ No newline at end of file + diff --git a/appdaemon/settings/apps/test.py.save b/appdaemon/settings/apps/test.py.save new file mode 100644 index 0000000..6df9d26 --- /dev/null +++ b/appdaemon/settings/apps/test.py.save @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +import appdaemon.plugins.hass.hassapi as hass +from notification import send_notification +import datetime +import sys, locale +import logging +#import unicode +# from automation import Automation # type: ignore + +""" +Monitor events and output changes to the verbose_log. Nice for debugging purposes. +Arguments: + - events: List of events to monitor +""" +class Test(hass.Hass): + def initialize(self) -> None: + root_logger= logging.getLogger() + root_logger.setLevel(logging.DEBUG) # or whatever + handler = logging.FileHandler('test.log', 'w', 'utf-8') # or whatever + formatter = logging.Formatter('%(name)s %(message)s') # or whatever + handler.setFormatter(formatter) # Pass handler as a parameter, not assign + root_logger.addHandler(handler) + UTF8Writer = codecs.getwriter('utf8') + sys.stdout = UTF8Writer(sys.stdout.buffer) + self.log(u"Привет") + self.log(str(u'ô')) + # id = send_notification( + # self, targets="telegram_monster", message="Repeated message", title="title", when=datetime.datetime.now(), interval=10, iterations=2 + # ) + # self.log("id:{}".format(id)) + # id = send_notification( + # self, targets="telegram_monster", message="Dummy", title="title" + # ) + + def terminate(self): + self.log('terminate') + # for listen_event_handle in self.listen_event_handle_list: + # self.cancel_listen_event(listen_event_handle) + diff --git a/docker-compose.yml b/docker-compose.yml index ff82df7..e61d9c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: - /srv/mysql:/var/lib/mysql hass: - image: homeassistant/home-assistant + image: homeassistant/home-assistant:0.100.2 container_name: hass volumes: - ./hass/settings:/config @@ -46,7 +46,7 @@ services: build: context: ./appdaemon/docker args: - VERSION: 3.0.5 + BASE_IMAGE: dlashua/appdaemon:dev #acockburn/appdaemon:3.0.5 container_name: appdaemon depends_on: - hass diff --git a/hass/settings/configuration.yaml b/hass/settings/configuration.yaml index 9d29b1f..6ab1c1b 100644 --- a/hass/settings/configuration.yaml +++ b/hass/settings/configuration.yaml @@ -23,6 +23,8 @@ http: trusted_proxies: - 172.19.0.0/24 +# noolite: + # Enables the frontend frontend: lovelace: @@ -44,11 +46,12 @@ logbook: # Components automation: !include_dir_merge_list configuration/automation -apple_tv: !include_dir_merge_list configuration/appletv -alexa: !include configuration/amazon/alexa.yaml +#apple_tv: !include_dir_merge_list configuration/appletv +#alexa: !include configuration/amazon/alexa.yaml alarm_control_panel: !include_dir_merge_list configuration/alarm_control_panel binary_sensor: !include_dir_merge_list configuration/binary_sensor camera: !include_dir_merge_list configuration/camera +fan: !include_dir_merge_list configuration/fan #climate: !include_dir_merge_list configuration/climate group: !include_dir_merge_named configuration/group input_boolean: !include_dir_merge_named configuration/input/input_boolean @@ -78,10 +81,4 @@ modbus: !include configuration/modbus.yaml utility_meter: !include_dir_merge_named configuration/utility_meter speedtestdotnet: !include configuration/speedtest.yaml -yandex_smart_home: !include configuration/alice.yaml - -# fan: -# - platform: xiaomi_miio -# host: 192.168.1.14 -# token: '411cf70bb69052c0121813388f30e193' -# model: zhimi.airpurifier.mc1 +yandex_smart_home: !include configuration/alice.yaml \ No newline at end of file diff --git a/hass/settings/configuration/fan/purifier.yaml b/hass/settings/configuration/fan/purifier.yaml new file mode 100644 index 0000000..1b49963 --- /dev/null +++ b/hass/settings/configuration/fan/purifier.yaml @@ -0,0 +1,4 @@ +- platform: xiaomi_miio + model: zhimi.airpurifier.mc1 + host: !secret purifier_ip + token: !secret purifier_key diff --git a/hass/settings/configuration/light/livingroom.yaml b/hass/settings/configuration/light/livingroom.yaml index ee8becc..6ef0c69 100644 --- a/hass/settings/configuration/light/livingroom.yaml +++ b/hass/settings/configuration/light/livingroom.yaml @@ -1,6 +1,4 @@ -# - platform: noolite -# channel: 0 -# name: "salt_lamp" -# scan_interval: 60 -# type: dimmer -# mode: noolite \ No newline at end of file +# - platform: noolite +# tx_channel: 1 +# friendly_name: "salt_lamp" +# dimmable: true \ No newline at end of file diff --git a/hass/settings/configuration/sensor/purifier.yaml b/hass/settings/configuration/sensor/purifier.yaml new file mode 100644 index 0000000..4488159 --- /dev/null +++ b/hass/settings/configuration/sensor/purifier.yaml @@ -0,0 +1,7 @@ + - platform: template + sensors: + xiaomi_airpurifier_air_quality_pm25: + friendly_name: "Air quality" + value_template: "{{ state_attr('fan.xiaomi_miio_device', 'aqi') }}" + unit_of_measurement: "μg/m³" + icon_template: "mdi:weather-fog" \ No newline at end of file diff --git a/hass/settings/configuration/sensor/template/xiaomi_socket_money_cost.yaml b/hass/settings/configuration/sensor/template/xiaomi_socket_money_cost.yaml index e5ffa90..09ad36a 100644 --- a/hass/settings/configuration/sensor/template/xiaomi_socket_money_cost.yaml +++ b/hass/settings/configuration/sensor/template/xiaomi_socket_money_cost.yaml @@ -40,6 +40,10 @@ friendly_name: "Фитолампа в мес." unit_of_measurement: '₽' value_template: "{{ (states('sensor.fitolamp_monthly') | float * (states('sensor.power_cost') | float)) | round(2) }}" + server_money_monthly: + friendly_name: "Сервер в мес." + unit_of_measurement: '₽' + value_template: "{{ (states('sensor.server_monthly') | float * (states('sensor.power_cost') | float)) | round(2) }}" power_money_t1_monthly: friendly_name: "Т1 в мес." unit_of_measurement: '₽' @@ -98,6 +102,10 @@ friendly_name: "Фитолампа в день" unit_of_measurement: '₽' value_template: "{{ (states('sensor.fitolamp_daily') | float * (states('sensor.power_cost') | float)) | round(2) }}" + server_money_daily: + friendly_name: "Сервер в день" + unit_of_measurement: '₽' + value_template: "{{ (states('sensor.server_daily') | float * (states('sensor.power_cost') | float)) | round(2) }}" counter_hot_money_hourly: friendly_name: "Горячая в час" @@ -139,3 +147,7 @@ friendly_name: "Фитолампа в час" unit_of_measurement: '₽' value_template: "{{ (states('sensor.fitolamp_hourly') | float * (states('sensor.power_cost') | float)) | round(2) }}" + server_money_hourly: + friendly_name: "Сервер в час" + unit_of_measurement: '₽' + value_template: "{{ (states('sensor.server_hourly') | float * (states('sensor.power_cost') | float)) | round(2) }}" diff --git a/hass/settings/configuration/sensor/template/xiaomi_socket_power_consumed.yaml b/hass/settings/configuration/sensor/template/xiaomi_socket_power_consumed.yaml index 8e06ac2..14de93e 100644 --- a/hass/settings/configuration/sensor/template/xiaomi_socket_power_consumed.yaml +++ b/hass/settings/configuration/sensor/template/xiaomi_socket_power_consumed.yaml @@ -28,4 +28,7 @@ friendly_name: "Фитолампа потреблено" unit_of_measurement: 'kWh' value_template: "{{ state_attr('switch.plug_158d0002924779', 'power_consumed') / 1000 }}" - \ No newline at end of file + server_consumed: + friendly_name: "Сервер потреблено" + unit_of_measurement: 'kWh' + value_template: "{{ state_attr('switch.plug_158d00026ea017', 'power_consumed') / 1000 }}" \ No newline at end of file diff --git a/hass/settings/configuration/utility_meter/server.yaml b/hass/settings/configuration/utility_meter/server.yaml new file mode 100644 index 0000000..5370849 --- /dev/null +++ b/hass/settings/configuration/utility_meter/server.yaml @@ -0,0 +1,9 @@ +server_hourly: + source: sensor.server_consumed + cycle: hourly +server_daily: + source: sensor.server_consumed + cycle: daily +server_monthly: + source: sensor.server_consumed + cycle: monthly diff --git a/hass/settings/custom_components/noolite.tgz b/hass/settings/custom_components/noolite.tgz new file mode 100644 index 0000000..a27fde1 Binary files /dev/null and b/hass/settings/custom_components/noolite.tgz differ diff --git a/hass/settings/ui-lovelace.yaml b/hass/settings/ui-lovelace.yaml index d39888e..46f7c99 100644 --- a/hass/settings/ui-lovelace.yaml +++ b/hass/settings/ui-lovelace.yaml @@ -22,14 +22,17 @@ views: entities: - sensor.temperature_158d0001149b3c - sensor.humidity_158d0001149b3c + - sensor.xiaomi_airpurifier_air_quality_pm25 - light.yeelight_color1_34ce008fe328 - light.yeelight_color1_286c071065c7 - light.yeelight_mono1_34ce0087ff33 - light.yeelight_mono1_f0b4290e5444 - light.yeelight_mono1_f0b429a8f6df + # - light.salt_lamp - cover.curtain_158d0002b0c46a - switch.plug_158d0001104a0c - switch.plug_158d00010dd98d + - fan.xiaomi_miio_device - climate.living_room - type: entities title: "Кухня" @@ -285,6 +288,16 @@ views: - sensor.fitolamp_money_hourly - sensor.fitolamp_money_daily - sensor.fitolamp_money_monthly + - type: entities + title: "Сервер" + show_header_toggle: false + entities: + - sensor.server_hourly + - sensor.server_daily + - sensor.server_monthly + - sensor.server_money_hourly + - sensor.server_money_daily + - sensor.server_money_monthly - type: entities title: "Теория трехтарифника" show_header_toggle: false @@ -296,6 +309,12 @@ views: - sensor.power_money_t2_monthly - sensor.power_money_t3_monthly - sensor.power_theory_money_monthly + - type: history-graph + title: Качество воздуха + hours_to_show: 80 + refresh_interval: 60 + entities: + - sensor.xiaomi_airpurifier_air_quality_pm25 - title: "Настройки" icon: mdi:settings cards: