├── .gitignore ├── .pylintrc ├── LICENSE ├── README.md ├── ad-ench └── ench.py ├── alarmClock ├── alarmClock.py └── alarmClock.yaml ├── alexa ├── README.md ├── alexa.yaml ├── alexa_api.py ├── custom_skill.json ├── lightState │ ├── lightStateIntent-utterances_DE.csv │ ├── lightStateIntent-utterances_EN.csv │ ├── lightStateIntent.py │ └── lightStateIntent.yaml ├── listService │ ├── listService.py │ └── listService.yaml ├── nextBus │ ├── nextBusIntent.py │ └── nextBusIntent.yaml ├── remindMeOfXWhenZone │ ├── remindMeOfXWhenZoneIntent.py │ └── remindMeOfXWhenZoneIntent.yaml ├── temperatureState │ ├── temperatureStateIntent-utterances_DE.csv │ ├── temperatureStateIntent-utterances_EN.csv │ ├── temperatureStateIntent.py │ └── temperatureStateIntent.yaml ├── turnEntityOffInX │ ├── requirements.txt │ ├── turnEntityOffInXIntent-utterances_DE.csv │ ├── turnEntityOffInXIntent-utterances_EN.csv │ ├── turnEntityOffInXIntent.py │ └── turnEntityOffInXIntent.yaml └── windowsOpen │ ├── windowsOpenIntent-utterances_DE.csv │ ├── windowsOpenIntent-utterances_EN.csv │ ├── windowsOpenIntent.py │ └── windowsOpenIntent.yaml ├── alexaSpeakerConnector ├── alexaSpeakerConnector.py └── alexaSpeakerConnector.yaml ├── appWatcher ├── appWatcher.py └── appWatcher.yaml ├── apps.yaml ├── buttonClicked ├── buttonClicked.py └── buttonClicked.yaml ├── comingHome ├── comingHome.py └── comingHome.yaml ├── deconz_xiaomi_button.yaml ├── deconz_xiaomi_button └── deconz_xiaomi_button.py ├── detectWrongState ├── detectWrongState.py └── detectWrongState.yaml ├── ench.yaml ├── eventMonitor ├── eventMonitor.py └── eventMonitor.yaml ├── faceRecognitionBot ├── faceRecognitionBot.py └── faceRecognitionBot.yaml ├── globals.py ├── heartbeat ├── heartbeat.py └── heartbeat.yaml ├── homeArrivalNotifier ├── homeArrivalNotifier.py └── homeArrivalNotifier.yaml ├── images ├── alarmClock.PNG ├── dishWasherNotify.PNG ├── failedLogin.PNG ├── googleTravelTimes.PNG ├── logo-pretty.png ├── logo-round-192x192.png ├── next_appoint_leave_modifier.PNG ├── next_appoint_leave_modifier_notification.PNG ├── notifyOfActionWhenAway.PNG ├── plantWateringReminder.PNG ├── plantWateringReminderAcknowledged.PNG ├── roggenNotify.PNG ├── ventilatorAutomation.PNG └── washingMachineStart.PNG ├── isHomeDeterminer ├── isHomeDeterminer.py └── isHomeDeterminer.yaml ├── isUserHomeDeterminer ├── isUserHomeDeterminer.py └── isUserHomeDeterminer.yaml ├── leavingZoneNotifier ├── leavingZoneNotifier.py └── leavingZoneNotifier.yaml ├── motionTrigger ├── motionTrigger.py └── motionTrigger.yaml ├── newWifiDeviceNotify ├── newWifiDeviceNotify.py ├── newWifiDeviceNotify.yaml └── requirements.txt ├── nextAppointmentLeaveNotifier ├── nextAppointmentLeaveNotifier.py └── nextAppointmentLeaveNotifier.yaml ├── notifier ├── notifier.py └── notifier.yaml ├── notifyOfActionWhenAway ├── notifyOfActionWhenAway.py └── notifyOfActionWhenAway.yaml ├── plantWateringNotifier ├── plantWateringNotifier.py └── plantWateringNotifier.yaml ├── pollenNotifier ├── pollenNotifier.py └── pollenNotifier.yaml ├── powerUsageNotification ├── powerUsageNotification.py └── powerUsageNotification.yaml ├── reminder ├── reminder.py └── reminder.yaml ├── requirements.txt ├── seqSink ├── requirements.txt ├── seqSink.py └── seqSink.yaml ├── setThermostat ├── setThermostat.py └── setThermostat.yaml ├── setThermostatOnStateChange ├── setThermostatOnStateChange.py └── setThermostatOnStateChange.yaml ├── sleepModeHandler ├── sleepModeHandler.py ├── sleepModeHandler.yaml ├── userSleepModeHandler.py └── userSleepModeHandler.yaml ├── travelTimeNotifier ├── travelTimeNotifier.py └── travelTimeNotifier.yaml ├── turnFanOnWhenHot ├── turnFanOnWhenHot.py └── turnFanOnWhenHot.yaml └── turnOffBarAfterRestart ├── turnOffBarAfterRestart.py └── turnOffBarAfterRestart.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | #appdaemon 107 | secrets.py 108 | 109 | #VS Code 110 | .vscode 111 | 112 | #PyCharm 113 | .idea 114 | 115 | #AppDaemon 116 | secrets.yaml 117 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/.pylintrc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kevin Eifinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /alarmClock/alarmClock.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | import math 4 | 5 | 6 | # 7 | # Alarm Clock App 8 | # 9 | # 10 | # Args: 11 | # alarm_time: entity which holds the alarm time. example: sensor.alarm_time 12 | # wakemeup: entity which enables the alarm. example: input_boolean.wakemeup 13 | # naturalwakeup: entity which enables the natural wake up fade in. example: input_number.alarm_natural_wakeup_fade_in 14 | # alarmweekday: entity which enables alarm only on weekdays. example: input_boolean.alarmweekday 15 | # radiowakeup: entity which enables radio wake up. example: input_boolean.radiowakeup 16 | # TODO radioplayer: entity which holds the information which radio player to select. example: input_select.wakeup_radioplayer 17 | # wakeup_light: light to fade in. example: light.bedroom_yeelight 18 | # isweekday: entity which holds the information whether today is a week day. example: binary_sensor.workday_today 19 | # notify_name: Who to notify. example: group_notifications 20 | # message: localized message to use in notification. e.g. "You left open {} Dummy." 21 | # 22 | # Release Notes 23 | # 24 | # Version 1.4: 25 | # Catch unknown 26 | # 27 | # Version 1.3.1: 28 | # Use consistent message variable 29 | # 30 | # Version 1.3: 31 | # Use new formatted alarm_time 32 | # 33 | # Version 1.2: 34 | # use Notify App 35 | # 36 | # Version 1.1: 37 | # message now directly in own yaml instead of message module 38 | # 39 | # Version 1.0: 40 | # Initial Version 41 | 42 | 43 | class AlarmClock(hass.Hass): 44 | def initialize(self): 45 | 46 | self.timer_handle_list = [] 47 | self.listen_event_handle_list = [] 48 | self.listen_state_handle_list = [] 49 | 50 | self.alarm_time = self.args["alarm_time"] 51 | self.wakemeup = self.args["wakemeup"] 52 | self.naturalwakeup = self.args["naturalwakeup"] 53 | self.alarmweekday = self.args["alarmweekday"] 54 | self.radiowakeup = self.args["radiowakeup"] 55 | self.isweekday = self.args["isweekday"] 56 | self.notify_name = self.args["notify_name"] 57 | self.wakeup_light = self.args["wakeup_light"] 58 | self.fade_in_time_multiplicator = self.args["fade_in_time_multiplicator"] 59 | self.message = self.args["message"] 60 | self.button = self.args["button"] 61 | 62 | self.notifier = self.get_app("Notifier") 63 | 64 | self.brightness = 100 65 | self.rgb_color = [255, 120, 0] 66 | self.alarm_timer = None 67 | 68 | self.cached_alarm_time = self.get_state(self.alarm_time) 69 | self.cached_fade_in_time = self.get_state(self.naturalwakeup) 70 | self.add_timer() 71 | 72 | self.listen_state_handle_list.append( 73 | self.listen_state(self.alarm_change, self.alarm_time) 74 | ) 75 | self.listen_state_handle_list.append( 76 | self.listen_state(self.naturalwakeup_change, self.naturalwakeup) 77 | ) 78 | 79 | self.listen_event_handle_list.append( 80 | self.listen_event(self.button_clicked, "xiaomi_aqara.click") 81 | ) 82 | 83 | def alarm_change(self, entity, attributes, old, new, kwargs): 84 | if new is not None and new != old and new != self.cached_alarm_time: 85 | if self.alarm_timer is not None: 86 | if self.alarm_timer in self.timer_handle_list: 87 | self.timer_handle_list.remove(self.alarm_timer) 88 | self.cancel_timer(self.alarm_timer) 89 | self.log("Alarm time change: {}".format(new)) 90 | self.cached_alarm_time = new 91 | self.add_timer() 92 | 93 | def naturalwakeup_change(self, entity, attributes, old, new, kwargs): 94 | if new is not None and new != old and new != self.cached_fade_in_time: 95 | if self.alarm_timer is not None: 96 | if self.alarm_timer in self.timer_handle_list: 97 | self.timer_handle_list.remove(self.alarm_timer) 98 | self.cancel_timer(self.alarm_timer) 99 | self.log("Fade-In time change: {}".format(new)) 100 | self.cached_fade_in_time = new 101 | self.add_timer() 102 | 103 | def add_timer(self): 104 | self.log("cached_alarm_time: {}".format(self.cached_alarm_time)) 105 | self.log("cached_fade_in_time: {}".format(self.cached_fade_in_time)) 106 | offset = self.cached_fade_in_time.split(".", 1)[0] 107 | 108 | if ( 109 | self.cached_alarm_time is not None 110 | and self.cached_alarm_time != "" 111 | and self.cached_alarm_time != "unknown" 112 | ): 113 | run_datetime = datetime.datetime.strptime( 114 | self.cached_alarm_time, "%Y-%m-%d %H:%M:%S" 115 | ) 116 | event_time = run_datetime - datetime.timedelta(minutes=int(offset)) 117 | try: 118 | self.alarm_timer = self.run_at(self.trigger_alarm, event_time) 119 | self.timer_handle_list.append(self.alarm_timer) 120 | self.log("Alarm will trigger at {}".format(event_time)) 121 | except ValueError: 122 | self.log("New trigger time would be in the past: {}".format(event_time)) 123 | 124 | def trigger_alarm(self, kwargs): 125 | if self.get_state(self.wakemeup) == "on": 126 | if self.get_state(self.alarmweekday) == "off" or ( 127 | self.get_state(self.alarmweekday) == "on" 128 | and self.get_state(self.isweekday) == "on" 129 | ): 130 | if float(self.cached_fade_in_time) > 0: 131 | self.log( 132 | "Turning on {}".format(self.friendly_name(self.wakeup_light)) 133 | ) 134 | self.call_service( 135 | "light/turn_on", entity_id=self.wakeup_light, brightness_pct=1 136 | ) 137 | transition = int( 138 | float(self.cached_fade_in_time) 139 | * int(self.fade_in_time_multiplicator) 140 | ) 141 | self.log( 142 | "Transitioning light in over {} seconds".format(transition) 143 | ) 144 | self.timer_handle_list.append( 145 | self.run_in( 146 | self.run_fade_in, 1, transition=transition, brightness_pct=1 147 | ) 148 | ) 149 | self.timer_handle_list.append( 150 | self.run_in(self.run_alarm, float(self.cached_fade_in_time)) 151 | ) 152 | 153 | def button_clicked(self, event_name, data, kwargs): 154 | """Extra callback method to trigger the wakeup light on demand by pressing a Xiaomi Button""" 155 | if data["entity_id"] == self.button: 156 | if data["click_type"] == "single": 157 | if float(self.cached_fade_in_time) > 0: 158 | self.log( 159 | "Turning on {}".format(self.friendly_name(self.wakeup_light)) 160 | ) 161 | self.call_service( 162 | "light/turn_on", entity_id=self.wakeup_light, brightness_pct=1 163 | ) 164 | transition = int( 165 | float(self.cached_fade_in_time) 166 | * int(self.fade_in_time_multiplicator) 167 | ) 168 | self.log( 169 | "Transitioning light in over {} seconds".format(transition) 170 | ) 171 | self.timer_handle_list.append( 172 | self.run_in( 173 | self.run_fade_in, 1, transition=transition, brightness_pct=1 174 | ) 175 | ) 176 | 177 | def run_fade_in(self, kwargs): 178 | """ 179 | Callback / recursion style because the transition feature does not seem to work well with Yeelight for 180 | transition values greater than 10s. 181 | :param kwargs: 182 | :return: 183 | """ 184 | wait_factor = 1 185 | transition = kwargs["transition"] 186 | brightness_pct = kwargs["brightness_pct"] 187 | pct_increase = 1 / transition 188 | self.log("pct_increase: {}".format(pct_increase), level="DEBUG") 189 | if pct_increase < 0.01: 190 | wait_factor = math.ceil(0.01 / pct_increase) 191 | pct_increase = 0.01 192 | self.log( 193 | "pct_increase smaller than 1% next run_in in {} seconds".format( 194 | wait_factor 195 | ), 196 | level="DEBUG", 197 | ) 198 | brightness_pct_old = brightness_pct 199 | self.log("brightness_pct_old: {}".format(brightness_pct_old), level="DEBUG") 200 | brightness_pct_new = int((brightness_pct_old + pct_increase * 100)) 201 | self.log("brightness_pct_new: {}".format(brightness_pct_new), level="DEBUG") 202 | if brightness_pct_new < 100: 203 | self.call_service( 204 | "light/turn_on", 205 | entity_id=self.wakeup_light, 206 | rgb_color=self.rgb_color, 207 | brightness_pct=brightness_pct_new, 208 | ) 209 | self.timer_handle_list.append( 210 | self.run_in( 211 | self.run_fade_in, 212 | wait_factor, 213 | transition=transition, 214 | brightness_pct=brightness_pct_new, 215 | ) 216 | ) 217 | 218 | def run_alarm(self, kwargs): 219 | self.notifier.notify(self.notify_name, self.message) 220 | # TODO radio 221 | 222 | def terminate(self): 223 | for timer_handle in self.timer_handle_list: 224 | self.cancel_timer(timer_handle) 225 | 226 | for listen_event_handle in self.listen_event_handle_list: 227 | self.cancel_listen_event(listen_event_handle) 228 | 229 | for listen_state_handle in self.listen_state_handle_list: 230 | self.cancel_listen_state(listen_state_handle) 231 | -------------------------------------------------------------------------------- /alarmClock/alarmClock.yaml: -------------------------------------------------------------------------------- 1 | # Alarm Clock App 2 | # alarmClock: 3 | # module: alarmClock 4 | # class: AlarmClock 5 | # alarm_time: sensor.alarm_time 6 | # wakemeup: input_boolean.wakemeup 7 | # naturalwakeup: input_number.alarm_natural_wakeup_fade_in 8 | # alarmweekday: input_boolean.alarmweekday 9 | # radiowakeup: input_boolean.radiowakeup 10 | # #TODO radioplayer: input_select.wakeup_radioplayer 11 | # wakeup_light: light.bedroom_yeelight 12 | # fade_in_time_multiplicator: 60 13 | # isweekday: binary_sensor.workday_today 14 | # notify_name: group_notifications 15 | # message: "Guten Morgen!" 16 | # #message: "Good Morning!" 17 | # button: binary_sensor.switch_158d000215aa28 18 | # dependencies: 19 | # - Notifier 20 | -------------------------------------------------------------------------------- /alexa/README.md: -------------------------------------------------------------------------------- 1 | # Alexa Intents 2 | 3 | Intents for [Alexa-Appdaemon-App](https://github.com/ReneTode/Alexa-Appdaemon-App) from [Rene Tode](https://github.com/ReneTode). 4 | 5 | To set it up for yourself follow [this](https://github.com/ReneTode/Alexa-Appdaemon-App/blob/master/alexa%20skill%20tutorial.md) tutorial 6 | 7 | ## listService 8 | 9 | Supply friendly names and known entities for other alexa skills. 10 | This is needed as this is a custom Alexa App and has nothing to do with HA Cloud / Alexa integration 11 | 12 | ## turnEntityOffInX 13 | 14 | Ask Alexa to turn something off in a set amount of minutes. 15 | 16 | Only works with entities defined under *switchable* in [listService.yaml](https://github.com/eifinger/appdaemon-scripts/blob/master/alexa/listService/listService.yaml) 17 | 18 | ``Alexa tell Home Assistant to turn off Ventilator in 10 Minutes`` 19 | 20 | ```yaml 21 | turnEntityOffInXIntent: 22 | module: turnEntityOffInXIntent 23 | class: TurnEntityOffInXIntent 24 | language: DE 25 | textLine: "Okay Homeassistant schaltet {{device}} in" 26 | Error:

Ich habe nicht richtig verstanden welches geraet soll ich ausschalten?

27 | unreadableState: "unlesbar fuer mich" 28 | dependencies: 29 | - listService 30 | ``` 31 | 32 | ## windowsOpen 33 | 34 | Will tell you if any windows / doors are open and/or tilted 35 | 36 | Only works with entities defined under *window*/*door*/*door_tilted* in [listService.yaml](https://github.com/eifinger/appdaemon-scripts/blob/master/alexa/listService/listService.yaml) 37 | 38 | ``Alexa ask Home Assistant whether all windows are closed`` 39 | 40 | ```yaml 41 | windowsOpenIntent: 42 | module: windowsOpenIntent 43 | class: WindowsOpenIntent 44 | language: DE 45 | textLineClosed: "Alle Fenster und Türen sind zu" 46 | #textLineClosed: "All windows and doors are closed" 47 | textLineWindowOpen: "Folgende Fenster sind noch offen" 48 | #textLineWindowOpen: "The following windows are stil open..." 49 | textLineDoorOpen: "Folgende Türen sind noch offen" 50 | #textLineDoorOpen: "The following doors are still open" 51 | textLineDoorTilted: "Die folgenden Türen sind noch gekippt" 52 | #textLineDoorTilted: "The following doors are tilted" 53 | Error:

Ich habe dich nicht richtig verstanden

54 | unreadableState: "unlesbar fuer mich" 55 | dependencies: 56 | - listService 57 | ``` 58 | 59 | ## nextBusIntent 60 | 61 | Will tell you the next departure of a bus/train of a [RMV](https://www.home-assistant.io/components/sensor.rmvtransport/) sensor 62 | 63 | ``Alexa ask Home Assistant when the next bus departs`` 64 | 65 | ```yaml 66 | nextBusIntent: 67 | module: nextBusIntent 68 | class: nextBusIntent 69 | textLine: "Linie {} fährt in {} Minuten" 70 | #textLine: "Line {} departs in {} minutes" 71 | Error:

Ich habe nicht richtig verstanden was du meinst

72 | sensor: sensor.nach_bruckenkopf 73 | global_dependencies: 74 | - globals 75 | ``` 76 | 77 | ## remindMeOfXWhenZoneIntent 78 | 79 | CURRENTLY DOES NOT WORK BECAUSE ALEXA DOES NOT ALLOW INTENTS CONTAINING REMINDERS 80 | 81 | Will send you a reminder over the notification service when you leave/enter a zone. 82 | 83 | ``Alexa tell Home Assistant to remind me of <> when entering work`` 84 | 85 | ```yaml 86 | remindMeOfXWhenZoneIntent: 87 | module: remindMeOfXWhenZoneIntent 88 | class: RemindMeOfXWhenZoneIntent 89 | device_tracker: person.kevin 90 | notify_name: group_notifications 91 | Error:

Es ist etwas schief gegangen

92 | textLine: "Okay ich erinnere dich an {{reminder}} wenn du {{zone}} " 93 | textEnter: "erreichst" 94 | textLeave: "verlässt" 95 | remindMessageSkeleton: "Ich sollte dich erinnern an " 96 | zoneMapping: 97 | arbeit: work 98 | hause: home 99 | elmo: elmo 100 | dependencies: 101 | - Notifier 102 | global_dependencies: 103 | - globals 104 | ``` 105 | -------------------------------------------------------------------------------- /alexa/alexa.yaml: -------------------------------------------------------------------------------- 1 | alexa_api: # appdaemon skill 2 | module: alexa_api 3 | class: alexa_api 4 | cardTitle: Your Card Title 5 | devices: 6 | unknownDevice: ein unbekannte platze 7 | logfile: /conf/logs/alexaAD.log 8 | responselogfile: /conf/logs/alexaResponse.log 9 | language: DE 10 | temperatureUnit: "Grad" 11 | logging: "True" 12 | launchRequest: 13 | -

bonjour, bist du wieder in {{device}}?

wie kann ich dir helfen?

14 | -

aloha, wie gehts es in {{device}}?

kann ich etwas fuer dich tun?

15 | -

hey, du bist wieder in {{device}}.

was kan ich machen?

16 | -

moin. Schoen das du wieder im {{device}} bist,

was ist dein wunsch?

17 | - mazzel tov.

Ich sage hallo zu alle in {{device}}

Kann ich etwas fuer dich tun?

18 | nextConversationQuestion: 19 | -

Was kann ich als naechstes fuer dich tun?

20 | -

Mit was willst du das ich dir helfe?

21 | intentEnd: 22 | -

Kann ich noch etwas fuer dich machen?

23 | -

Willst du das ich noch etwas anderes mache?

24 | -

Kann ich dir mit noch etwas helfen?

25 | -

Gibt es noch etwas was ich fuer dich tun kann?

26 | conversationEnd: 27 | - Bis zum naechsten mal 28 | - arrivederci 29 | - bis bald 30 | - bis dann 31 | - mach's gut 32 | - tschoe 33 | - viel glueck 34 | - Schoen mit dir gesprochen zu haben 35 | responseError: 36 | - Es tut mir leit aber etwas ist falsch gegangen 37 | - Leider meldet home assistant einen Fehler -------------------------------------------------------------------------------- /alexa/lightState/lightStateIntent-utterances_DE.csv: -------------------------------------------------------------------------------- 1 | Ist {device} aus 2 | Ist {device} an 3 | Was ist der Status von {device} -------------------------------------------------------------------------------- /alexa/lightState/lightStateIntent-utterances_EN.csv: -------------------------------------------------------------------------------- 1 | Is {device} off 2 | Is {device} on 3 | What is the state of {device} 4 | -------------------------------------------------------------------------------- /alexa/lightState/lightStateIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import random 3 | 4 | 5 | class lightStateIntent(hass.Hass): 6 | def initialize(self): 7 | return 8 | 9 | def getIntentResponse(self, slots, devicename): 10 | ############################################ 11 | # an Intent to give back the state from a light. 12 | # but it also can be any other kind of entity 13 | ############################################ 14 | try: 15 | entityname = self.args["entities"][slots["device"]] 16 | state = self.get_state(entityname) 17 | if isinstance(state, float): 18 | if self.args["language"] == "DE": 19 | state = self.floatToStr(state) 20 | else: 21 | state = str(state) 22 | elif isinstance(state, str): 23 | state = state 24 | else: 25 | state = self.args["unreadableState"] 26 | text = self.random_arg(self.args["textLine"]) + state 27 | except: 28 | text = self.random_arg(self.args["Error"]) 29 | return text 30 | 31 | def floatToStr(self, myfloat): 32 | ############################################ 33 | # replace . with , for better speech 34 | ############################################ 35 | floatstr = str(myfloat) 36 | floatstr = floatstr.replace(".", ",") 37 | return floatstr 38 | -------------------------------------------------------------------------------- /alexa/lightState/lightStateIntent.yaml: -------------------------------------------------------------------------------- 1 | lightStateIntent: 2 | module: lightStateIntent 3 | class: lightStateIntent 4 | language: DE 5 | temperatureUnit: "Grad" 6 | textLine: 7 | - "Der Status von {{device}} ist " 8 | - "{{device}} ist in diesem Moment " 9 | - "{{device}} ist " 10 | Error:

Ich habe nicht richtig verstanden welches geraet oder sensor du wissen wolltest

11 | unreadableState: "unlesbar fuer mich" 12 | entities: 13 | wohnzimmer temperatur: sensor.large_lamp_temperature 14 | deckenlampe: light.livingroom_yeelight 15 | ventilator: switch.ventilator -------------------------------------------------------------------------------- /alexa/listService/listService.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # Provide the list of HA entities for Alexa Apps 5 | # 6 | # 7 | # Args: 8 | # 9 | # switchable: dict of switchable entities 10 | # temperature: dict of temperature sensors 11 | # door: dict of reed sensors showing if the door is completely open 12 | # door_tilted: dict of reed sensors showing if a door is partially/leaning open 13 | # window: dict of reed sensors showing if a window is open 14 | # 15 | # 16 | # Release Notes 17 | # 18 | # Version 1.0: 19 | # Initial Version 20 | 21 | 22 | class ListService(hass.Hass): 23 | def initialize(self): 24 | return 25 | 26 | def getSwitchable(self): 27 | return self.args["switchable"] 28 | 29 | def getTemperature(self): 30 | return self.args["temperature"] 31 | 32 | def getDoor(self): 33 | return self.args["door"] 34 | 35 | def getWindow(self): 36 | return self.args["window"] 37 | 38 | def getDoorTilted(self): 39 | return self.args["door_tilted"] 40 | -------------------------------------------------------------------------------- /alexa/listService/listService.yaml: -------------------------------------------------------------------------------- 1 | listService: 2 | module: listService 3 | class: ListService 4 | switchable: 5 | große lampe: switch.large_lamp 6 | kleine lampe: switch.small_lamp 7 | deckenlampe: light.livingroom_yeelight 8 | ventilator: switch.ventilator 9 | großer ventilator: switch.large_ventilator 10 | markise: switch.markise 11 | fernseher: switch.tv 12 | schlafzimmer receiver: switch.bedroom_receiver 13 | snowboard: switch.snowboard 14 | receiver: media_player.denon_avr_x1300w 15 | wohnzimmer: group.livingroom 16 | wohnung: group.all 17 | schlafzimmer lampe: light.bedroom_yeelight 18 | schlafzimmer: light.bedroom_yeelight 19 | bar: light.bar_table 20 | bar licht: light.bar_table 21 | treppe unten: light.stairs_lower_yeelight 22 | treppe oben: light.treppe_oben 23 | leselampe: light.reading_lamp_yeelight 24 | lese lampe: light.reading_lamp_yeelight 25 | runde lampe: light.reading_lamp_yeelight 26 | garderobe: light.lobby_yeelight 27 | garderoben licht: light.lobby_yeelight 28 | garderobenlicht: light.lobby_yeelight 29 | flur: switch.lobby 30 | flur licht: switch.lobby 31 | bad: light.lower_bathroom_yeelight 32 | bad licht: light.lower_bathroom_yeelight 33 | treppe: group.stair_lights 34 | treppen licht: group.stair_lights 35 | treppenlicht: group.stair_lights 36 | 37 | temperature: 38 | große lampe thermostat: sensor.large_lamp_temperature 39 | kleine lampe thermostat: sensor.small_lamp_temperature 40 | ventilator thermostat: sensor.ventilator_temperature 41 | großer ventilator thermostat: sensor.large_ventilator_temperature 42 | door: 43 | wohnungstuer: binary_sensor.contact_door 44 | terassentuer schlafzimmer: binary_sensor.contact_bedroom_door 45 | terassentuer: binary_sensor.contact_terrace_door 46 | terassentuer arbeitszimmer: binary_sensor.contact_studyroom_door 47 | window: 48 | kuechenfenster: binary_sensor.contact_kitchen_window 49 | badfenster oben: binary_sensor.contact_upper_bathroom_window 50 | badfenster unten: binary_sensor.contact_lower_bathroom_window 51 | gaestezimmerfenster: binary_sensor.contact_guestroom_window 52 | door_tilted: 53 | kuechenfenster gekippt: binary_sensor.contact_kitchen_window_tilted 54 | terassentuer arbeitszimmer gekippt: binary_sensor.contact_studyroom_door_tilted 55 | terassentuer schlafzimmer gekippt: binary_sensor.contact_bedroom_door_tilted 56 | terrassentuer gekippt: binary_sensor.contact_terrace_door_tilted 57 | badfenster unten gekippt: binary_sensor.contact_lower_bathroom_window_tilted 58 | badfenster oben gekippt: binary_sensor.contact_upper_bathroom_window_tilted 59 | gaestezimmerfenster gekippt: binary_sensor.contact_guestroom_window_tilted 60 | 61 | -------------------------------------------------------------------------------- /alexa/nextBus/nextBusIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import random 3 | 4 | 5 | class nextBusIntent(hass.Hass): 6 | def initialize(self): 7 | self.sensor = self.args["sensor"] 8 | self.textLine = self.args["textLine"] 9 | self.error = self.args["error"] 10 | 11 | def getIntentResponse(self, slots, devicename): 12 | ############################################ 13 | # give next bus departure 14 | ############################################ 15 | try: 16 | state = self.get_state(self.sensor, attribute="all") 17 | line = state["attributes"]["line"] 18 | minutes = state["attributes"]["minutes"] 19 | text = self.textLine.format(line, minutes) 20 | except: 21 | text = self.error 22 | return text 23 | -------------------------------------------------------------------------------- /alexa/nextBus/nextBusIntent.yaml: -------------------------------------------------------------------------------- 1 | nextBusIntent: 2 | module: nextBusIntent 3 | class: nextBusIntent 4 | textLine: "Linie {} fährt in {} Minuten" 5 | #textLine: "Line {} departs in {} minutes" 6 | error:

Ich habe nicht richtig verstanden was du meinst

7 | sensor: sensor.nach_bruckenkopf -------------------------------------------------------------------------------- /alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | __ZONE_ACTION_ENTER__ = "kommen" 5 | __ZONE_ACTION_LEAVE__ = "verlassen" 6 | 7 | 8 | class RemindMeOfXWhenZoneIntent(hass.Hass): 9 | def initialize(self): 10 | self.timer_handle_list = [] 11 | self.listen_state_handle_list = [] 12 | 13 | self.device_tracker = self.args["device_tracker"] 14 | self.notify_name = self.args["notify_name"] 15 | self.remindMessageSkeleton = self.args["remindMessageSkeleton"] 16 | 17 | self.notifier = self.get_app("Notifier") 18 | return 19 | 20 | def getIntentResponse(self, slots, devicename): 21 | ############################################ 22 | # an Intent to give back the state from a light. 23 | # but it also can be any other kind of entity 24 | ############################################ 25 | try: 26 | # get zone_name for friendly name used when talking to alexa 27 | zone_name = None 28 | for key, value in self.args["zoneMapping"].items(): 29 | if key == slots["zone"].lower(): 30 | zone_name = value 31 | # listen to a state change of the zone 32 | if zone_name == None: 33 | raise Exception( 34 | "Could not find zonemapping for: {}".format(slots["zone"].lower()) 35 | ) 36 | else: 37 | self.listen_state_handle_list.append( 38 | self.listen_state( 39 | self.remind_callback, 40 | self.device_tracker, 41 | zone=slots["zone"], 42 | zoneAction=slots["zoneAction"], 43 | reminder=slots["reminder"], 44 | ) 45 | ) 46 | # set correct zoneAction response 47 | if slots["zoneAction"] == __ZONE_ACTION_ENTER__: 48 | text = self.args["textLine"] + self.args["textEnter"] 49 | else: 50 | text = self.args["textLine"] + self.args["textLeave"] 51 | except Exception as e: 52 | self.log("Exception: {}".format(e)) 53 | self.log("slots: {}".format(slots)) 54 | text = self.random_arg(self.args["Error"]) 55 | return text 56 | 57 | def remind_callback(self, entity, attribute, old, new, kwargs): 58 | if kwargs["zoneAction"] == __ZONE_ACTION_ENTER__: 59 | if new != old and new == kwargs["zone"]: 60 | self.log("Notifying") 61 | self.notifier.notify( 62 | self.notify_name, 63 | self.remindMessageSkeleton + kwargs["reminder"], 64 | useAlexa=False, 65 | ) 66 | elif kwargs["zoneAction"] == __ZONE_ACTION_LEAVE__: 67 | if new != old and old == kwargs["zone"]: 68 | self.log("Notifying") 69 | self.notifier.notify( 70 | self.notify_name, 71 | self.remindMessageSkeleton + kwargs["reminder"], 72 | useAlexa=False, 73 | ) 74 | 75 | def terminate(self): 76 | for timer_handle in self.timer_handle_list: 77 | self.cancel_timer(timer_handle) 78 | 79 | for listen_state_handle in self.listen_state_handle_list: 80 | self.cancel_listen_state(listen_state_handle) 81 | -------------------------------------------------------------------------------- /alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.yaml: -------------------------------------------------------------------------------- 1 | remindMeOfXWhenZoneIntent: 2 | module: remindMeOfXWhenZoneIntent 3 | class: RemindMeOfXWhenZoneIntent 4 | device_tracker: person.kevin 5 | notify_name: group_notifications 6 | Error:

Es ist etwas schief gegangen

7 | textLine: "Okay ich erinnere dich an {{reminder}} wenn du {{zone}} " 8 | textEnter: "erreichst" 9 | textLeave: "verlässt" 10 | remindMessageSkeleton: "Ich sollte dich erinnern an " 11 | zoneMapping: 12 | arbeit: work 13 | hause: home 14 | elmo: elmo 15 | dependencies: 16 | - Notifier -------------------------------------------------------------------------------- /alexa/temperatureState/temperatureStateIntent-utterances_DE.csv: -------------------------------------------------------------------------------- 1 | wie viel grad ist es in {location} 2 | was ist die temperatur in {location} -------------------------------------------------------------------------------- /alexa/temperatureState/temperatureStateIntent-utterances_EN.csv: -------------------------------------------------------------------------------- 1 | how many degree is it in {location} 2 | what is the temperture in {location} 3 | -------------------------------------------------------------------------------- /alexa/temperatureState/temperatureStateIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import random 3 | 4 | 5 | class temperatureStateIntent(hass.Hass): 6 | def initialize(self): 7 | return 8 | 9 | def getIntentResponse(self, slots, devicename): 10 | ############################################ 11 | # give temperature for a list of temperature sensors 12 | ############################################ 13 | try: 14 | if self.args["language"] == "DE": 15 | temp = ( 16 | self.floatToStr( 17 | self.get_state(self.args["sensors"][slots["location"]]) 18 | ) 19 | + self.args["temperatureUnit"] 20 | ) 21 | else: 22 | temp = ( 23 | str(self.get_state(self.args["sensors"][slots["location"]])) 24 | + self.args["temperatureUnit"] 25 | ) 26 | text = self.random_arg(self.args["textLine"]) + temp 27 | except: 28 | text = self.random_arg(self.args["Error"]) 29 | return text 30 | 31 | def floatToStr(self, myfloat): 32 | ############################################ 33 | # replace . with , for better speech 34 | ############################################ 35 | floatstr = str(myfloat) 36 | floatstr = floatstr.replace(".", ",") 37 | return floatstr 38 | 39 | def random_arg(self, argName): 40 | ############################################ 41 | # pick a random text from a list 42 | ############################################ 43 | if isinstance(argName, list): 44 | text = random.choice(argName) 45 | else: 46 | text = argName 47 | return text 48 | -------------------------------------------------------------------------------- /alexa/temperatureState/temperatureStateIntent.yaml: -------------------------------------------------------------------------------- 1 | temperatureStateIntent: 2 | module: temperatureStateIntent 3 | class: temperatureStateIntent 4 | language: DE 5 | temperatureUnit: "Grad" 6 | textLine: 7 | - "Die Temperatur im {{location}} ist " 8 | - "In diesem Moment ist es im {{location}} " 9 | - "Im Moment ist die Temperatur " 10 | Error:

Ich habe nicht richtig verstanden welche Temperatur du wissen wolltest

11 | sensors: 12 | wohnzimmer: sensor.large_lamp_temperature -------------------------------------------------------------------------------- /alexa/turnEntityOffInX/requirements.txt: -------------------------------------------------------------------------------- 1 | isodate -------------------------------------------------------------------------------- /alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_DE.csv: -------------------------------------------------------------------------------- 1 | In {duration} {device} ausschalten 2 | {device} in {duration} ausschalten 3 | er soll {device} in {duration} ausschalten 4 | es soll {device} in {duration} ausschalten 5 | Schalte {device} in {duration} aus -------------------------------------------------------------------------------- /alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_EN.csv: -------------------------------------------------------------------------------- 1 | In {duration} turn off {device} 2 | turn off {device} in {duration} 3 | it should turn off {device} in {duration} 4 | -------------------------------------------------------------------------------- /alexa/turnEntityOffInX/turnEntityOffInXIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import random 3 | import isodate 4 | import datetime 5 | 6 | 7 | class TurnEntityOffInXIntent(hass.Hass): 8 | def initialize(self): 9 | self.timer_handle_list = [] 10 | self.listService = self.get_app("listService") 11 | return 12 | 13 | def getIntentResponse(self, slots, devicename): 14 | ############################################ 15 | # an Intent to give back the state from a light. 16 | # but it also can be any other kind of entity 17 | ############################################ 18 | try: 19 | 20 | entityname = self.listService.getSwitchable()[slots["device"]] 21 | # to upper because of https://github.com/gweis/isodate/issues/52 22 | duration = isodate.parse_duration(slots["duration"].upper()) 23 | self.timer_handle_list.append( 24 | self.run_in( 25 | self.turn_off_callback, 26 | duration.total_seconds(), 27 | entityname=entityname, 28 | ) 29 | ) 30 | minutes, seconds = divmod(duration.total_seconds(), 60) 31 | minutes = int(minutes) 32 | seconds = int(seconds) 33 | if minutes == 0: 34 | if seconds == 1: 35 | timeText = " einer Sekunde" 36 | else: 37 | timeText = " {} Sekunden".format(seconds) 38 | elif minutes == 1: 39 | if seconds == 1: 40 | timeText = " einer Minute und einer Sekunde" 41 | elif seconds == 0: 42 | timeText = " einer Minute" 43 | else: 44 | timeText = " einer Minute und {} Sekunden".format(seconds) 45 | else: 46 | if seconds == 1: 47 | timeText = " {} Minuten und einer Sekunde".format(minutes) 48 | elif seconds == 0: 49 | timeText = " {} Minuten".format(minutes) 50 | else: 51 | timeText = " {} Minuten und {} Sekunden".format(minutes, seconds) 52 | text = self.args["textLine"] + timeText + " ab." 53 | except Exception as e: 54 | self.log("Exception: {}".format(e)) 55 | self.log("slots: {}".format(slots)) 56 | text = self.random_arg(self.args["Error"]) 57 | return text 58 | 59 | def turn_off_callback(self, kwargs): 60 | entityname = kwargs["entityname"] 61 | self.log("Turning off {}".format(entityname)) 62 | self.turn_off(entityname) 63 | 64 | def random_arg(self, argName): 65 | ############################################ 66 | # pick a random text from a list 67 | ############################################ 68 | if isinstance(argName, list): 69 | text = random.choice(argName) 70 | else: 71 | text = argName 72 | return text 73 | 74 | def terminate(self): 75 | for timer_handle in self.timer_handle_list: 76 | self.cancel_timer(timer_handle) 77 | -------------------------------------------------------------------------------- /alexa/turnEntityOffInX/turnEntityOffInXIntent.yaml: -------------------------------------------------------------------------------- 1 | turnEntityOffInXIntent: 2 | module: turnEntityOffInXIntent 3 | class: TurnEntityOffInXIntent 4 | language: DE 5 | textLine: "Okay Homeassistant schaltet {{device}} in" 6 | Error:

Ich habe nicht richtig verstanden welches geraet soll ich ausschalten?

7 | unreadableState: "unlesbar fuer mich" 8 | dependencies: 9 | - listService -------------------------------------------------------------------------------- /alexa/windowsOpen/windowsOpenIntent-utterances_DE.csv: -------------------------------------------------------------------------------- 1 | ob alles zu ist 2 | ob noch etwas offen ist 3 | ist noch etwas offen 4 | ist alles zu 5 | sind alle türen zu 6 | ob noch türen offen sind 7 | ob alle türen zu sind 8 | ob alle fenster zu sind 9 | ob noch Fenster offen sind 10 | ob die Fenster zu sind 11 | Sind die Fenster zu 12 | Sind alle Fenster zu 13 | Welche Fenster sind offen -------------------------------------------------------------------------------- /alexa/windowsOpen/windowsOpenIntent-utterances_EN.csv: -------------------------------------------------------------------------------- 1 | whether everything is closed 2 | whether something is still open 3 | is something still open 4 | is everthing closed 5 | are all doors closed 6 | whether some doors are still open 7 | whether all doors are closed 8 | whether all windows are closed 9 | whether some windows are open 10 | whether the windows are closed 11 | Are the Windows closed 12 | Are all windows closed 13 | Which Windows are open 14 | -------------------------------------------------------------------------------- /alexa/windowsOpen/windowsOpenIntent.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import random 3 | import isodate 4 | import datetime 5 | 6 | 7 | class WindowsOpenIntent(hass.Hass): 8 | def initialize(self): 9 | self.listService = self.get_app("listService") 10 | return 11 | 12 | def getIntentResponse(self, slots, devicename): 13 | ############################################ 14 | # an Intent to give back the state of windows 15 | ############################################ 16 | try: 17 | windows_dict = self.listService.getWindow() 18 | doors_dict = self.listService.getDoor() 19 | doors_tilted_dict = self.listService.getDoorTilted() 20 | window_open_list = [] 21 | door_open_list = [] 22 | door_tilted_list = [] 23 | # iterate over all window entities 24 | for key, value in windows_dict.items(): 25 | # if a window is open ("on") add it to the window_open_list 26 | if self.get_state(value) == "on": 27 | window_open_list.append(value) 28 | # iterate over all door entities 29 | for key, value in doors_dict.items(): 30 | # if a door is open ("on") add it to the door_open_list 31 | if self.get_state(value) == "on": 32 | door_open_list.append(value) 33 | # iterate over all door_tilted entities 34 | for key, value in doors_tilted_dict.items(): 35 | # if a door is tilted ("on") add it to the door_tilted_list 36 | if self.get_state(value) == "on": 37 | door_tilted_list.append(value) 38 | 39 | text = "" 40 | # add open windows to response 41 | if len(window_open_list) > 0: 42 | if text != "": 43 | text = text + ' ' 44 | text = text + self.args["textLineWindowOpen"] 45 | for entity in window_open_list: 46 | text = ( 47 | text + ' ' + self.friendly_name(entity) 48 | ) 49 | # add open doors to response 50 | if len(door_open_list) > 0: 51 | if text != "": 52 | text = text + ' ' 53 | text = text + self.args["textLineDoorOpen"] 54 | for entity in door_open_list: 55 | text = ( 56 | text + ' ' + self.friendly_name(entity) 57 | ) 58 | # add tilted doors to reponse 59 | if len(door_tilted_list) > 0: 60 | if text != "": 61 | text = text + ' ' 62 | text = text + self.args["textLineDoorTilted"] 63 | for entity in door_tilted_list: 64 | friendly_name = self.friendly_name(entity) 65 | # remove "gekippt" (german for tilted) from the friendly name 66 | friendly_name = friendly_name.replace(" gekippt", "") 67 | friendly_name = friendly_name.replace(" Gekippt", "") 68 | text = text + ' ' + friendly_name 69 | # if all closed response 70 | if text == "": 71 | text = self.args["textLineClosed"] 72 | except Exception as e: 73 | self.log("Exception: {}".format(e)) 74 | self.log("slots: {}".format(slots)) 75 | text = self.random_arg(self.args["Error"]) 76 | return text 77 | 78 | def random_arg(self, argName): 79 | ############################################ 80 | # pick a random text from a list 81 | ############################################ 82 | if isinstance(argName, list): 83 | text = random.choice(argName) 84 | else: 85 | text = argName 86 | return text 87 | -------------------------------------------------------------------------------- /alexa/windowsOpen/windowsOpenIntent.yaml: -------------------------------------------------------------------------------- 1 | windowsOpenIntent: 2 | module: windowsOpenIntent 3 | class: WindowsOpenIntent 4 | language: DE 5 | textLineClosed: "Alle Fenster und Türen sind zu" 6 | #textLineClosed: "All windows and doors are closed" 7 | textLineWindowOpen: "Folgende Fenster sind noch offen" 8 | #textLineWindowOpen: "The following windows are stil open..." 9 | textLineDoorOpen: "Folgende Türen sind noch offen" 10 | #textLineDoorOpen: "The following doors are still open" 11 | textLineDoorTilted: "Die folgenden Türen sind noch gekippt" 12 | #textLineDoorTilted: "The following doors are tilted" 13 | Error:

Ich habe dich nicht richtig verstanden

14 | unreadableState: "unlesbar fuer mich" 15 | dependencies: 16 | - listService -------------------------------------------------------------------------------- /alexaSpeakerConnector/alexaSpeakerConnector.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers 5 | # 6 | # Args: 7 | # 8 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 9 | # alexa_entity: the alexa media player entity. example: media_player.kevins_echo_dot_oben 10 | # alexa_entity_source: source to set alexa to. example: Denon AVR-X1300W 11 | # receiver: Receiver to turn on. example: media_player.denon_avr_x1300w 12 | # receiver_source: source to set receiver to. example: Bluetooth 13 | # 14 | # Release Notes 15 | # 16 | # Version 1.2.0: 17 | # Introduce INITIAL_VOLUME 18 | # 19 | # Version 1.1.1: 20 | # Fix WAITING_TIME 21 | # 22 | # Version 1.1: 23 | # Introduce WAITING_TIME 24 | # 25 | # Version 1.0: 26 | # Initial Version 27 | 28 | WAITING_TIME = 10 29 | INITIAL_VOLUME = 30 30 | 31 | 32 | class AlexaSpeakerConnector(hass.Hass): 33 | def initialize(self): 34 | self.listen_state_handle_list = [] 35 | self.timer_handle_list = [] 36 | 37 | self.app_switch = self.args["app_switch"] 38 | self.alexa_entity = self.args["alexa_entity"] 39 | self.alexa_entity_source = self.args["alexa_entity_source"] 40 | self.receiver = self.args["receiver"] 41 | self.receiver_source = self.args["receiver_source"] 42 | 43 | self.listen_state_handle_list.append( 44 | self.listen_state(self.state_change, self.alexa_entity) 45 | ) 46 | 47 | def state_change(self, entity, attribute, old, new, kwargs): 48 | if self.get_state(self.app_switch) == "on": 49 | if new.lower() == "playing" and old.lower() != "playing": 50 | self.log("{} changed to {}".format(self.alexa_entity, new)) 51 | # Only trigger when the receiver is off. Otherwise its probably playing something 52 | if self.get_state(self.receiver) == "off": 53 | self.log( 54 | "Setting source of {} to: {}".format( 55 | self.receiver, self.receiver_source 56 | ) 57 | ) 58 | self.call_service( 59 | "media_player/select_source", 60 | entity_id=self.receiver, 61 | source=self.receiver_source, 62 | ) 63 | self.log(f"Setting volume of {self.receiver} to: {INITIAL_VOLUME}") 64 | self.call_service( 65 | "media_player/volume_set", 66 | entity_id=self.receiver, 67 | volume_level=INITIAL_VOLUME, 68 | ) 69 | self.timer_handle_list.append( 70 | self.run_in(self.run_in_callback, WAITING_TIME) 71 | ) 72 | 73 | def run_in_callback(self, kwargs): 74 | """ 75 | Callback method to introduce a waiting time for the receiver to come 'online' 76 | :return: 77 | """ 78 | self.log( 79 | "Setting source of {} to: {}".format( 80 | self.alexa_entity, self.alexa_entity_source 81 | ) 82 | ) 83 | self.call_service( 84 | "media_player/select_source", 85 | entity_id=self.alexa_entity, 86 | source=self.alexa_entity_source, 87 | ) 88 | 89 | def terminate(self): 90 | for listen_state_handle in self.listen_state_handle_list: 91 | self.cancel_listen_state(listen_state_handle) 92 | 93 | for timer_handle in self.timer_handle_list: 94 | self.cancel_timer(timer_handle) 95 | -------------------------------------------------------------------------------- /alexaSpeakerConnector/alexaSpeakerConnector.yaml: -------------------------------------------------------------------------------- 1 | #App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers 2 | # alexaSpeakerConnector: 3 | # module: alexaSpeakerConnector 4 | # class: AlexaSpeakerConnector 5 | # app_switch: input_boolean.alexa_speaker_connector 6 | # alexa_entity: media_player.kevins_echo_dot_oben 7 | # alexa_entity_source: Denon AVR-X1300W 8 | # receiver: media_player.denon_avr_x1300w 9 | # receiver_source: Bluetooth -------------------------------------------------------------------------------- /appWatcher/appWatcher.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App which listens on the log for App crashes and notifies via telegram 5 | # 6 | # Args: 7 | # 8 | # Release Notes 9 | # 10 | # Version 2.0: 11 | # Updates for Appdaemon Version 4.0.3 12 | # 13 | # Version 1.0: 14 | # Initial Version 15 | 16 | 17 | class AppWatcher(hass.Hass): 18 | def initialize(self): 19 | self.notify_name = self.args["notify_name"] 20 | self.notify_message = self.args["notify_message"] 21 | try: 22 | self.exclude_apps = self.args["exclude_apps"].split(",") 23 | except KeyError: 24 | self.exclude_apps = None 25 | 26 | # App dependencies 27 | self.notifier = self.get_app("Notifier") 28 | 29 | self.handle = self.listen_log(self.log_message_callback) 30 | 31 | def log_message_callback(self, app_name, ts, level, log_type, message, kwargs): 32 | if level == "WARNING" or level == "ERROR" or level == "CRITICAL": 33 | if app_name == "AppDaemon": 34 | if "Unexpected error" in message: 35 | self.notifier.notify( 36 | self.notify_name, 37 | self.notify_message.format(message), 38 | useAlexa=False, 39 | ) 40 | 41 | def terminate(self): 42 | self.cancel_listen_log(self.handle) 43 | -------------------------------------------------------------------------------- /appWatcher/appWatcher.yaml: -------------------------------------------------------------------------------- 1 | appWatcher: 2 | module: appWatcher 3 | class: AppWatcher 4 | notify_name: kevin 5 | notify_message: "AppDaemon error: {}" 6 | #notify_message: "Appdaemon reported an error: {}" 7 | dependencies: 8 | - Notifier -------------------------------------------------------------------------------- /apps.yaml: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | ## Global 3 | ################################################################# 4 | global_modules: 5 | - globals -------------------------------------------------------------------------------- /buttonClicked/buttonClicked.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which toggles entities for single/double presses of xiaomi buttons 6 | # 7 | # Args: 8 | # 9 | # sensor: sensor to monitor e.g. sensor.upstairs_smoke 10 | # actor_single: actor to toggle on single click 11 | # actor_double: actor to toggle on double click 12 | # actor_hold: actor to dim on hold 13 | # Release Notes 14 | # 15 | # Version 1.2: 16 | # All actors optional 17 | # 18 | # Version 1.1: 19 | # added actor_hold 20 | # 21 | # Version 1.0: 22 | # Initial Version 23 | 24 | 25 | class ButtonClicked(hass.Hass): 26 | def initialize(self): 27 | self.listen_event_handle_list = [] 28 | self.timer_handle_list = [] 29 | 30 | self.actor_single = self.args.get("actor_single") 31 | self.actor_double = self.args.get("actor_double") 32 | self.actor_hold = self.args.get("actor_hold") 33 | 34 | self.dimmer_timer_handle = None 35 | 36 | self.listen_event_handle_list.append( 37 | self.listen_event(self.event_detected, "xiaomi_aqara.click") 38 | ) 39 | 40 | def event_detected(self, event_name, data, kwargs): 41 | if data["entity_id"] == self.args["sensor"]: 42 | if data["click_type"] == "single" and self.actor_single != None: 43 | self.log("ButtonClicked: {}".format(data["entity_id"])) 44 | # Is on 45 | if self.get_state(self.actor_single) == "on": 46 | self.log("Turning {} off".format(self.actor_single)) 47 | # Workaround for Yeelight see https://community.home-assistant.io/t/transition-for-turn-off-service-doesnt-work-for-yeelight-lightstrip/25333/4 48 | if self.actor_single.startswith("light"): 49 | self.call_service( 50 | "light/turn_on", 51 | entity_id=self.actor_single, 52 | transition=1, 53 | brightness_pct=1, 54 | ) 55 | self.timer_handle_list.append( 56 | self.run_in(self.turn_off_workaround, 2) 57 | ) 58 | else: 59 | self.turn_off(self.actor_single) 60 | # Is off 61 | if self.get_state(self.actor_single) == "off": 62 | self.log("Turning {} on".format(self.actor_single)) 63 | if self.actor_single.startswith("light"): 64 | self.call_service( 65 | "light/turn_on", 66 | entity_id=self.actor_single, 67 | transition=1, 68 | brightness_pct=100, 69 | ) 70 | else: 71 | self.turn_on(self.actor_single) 72 | 73 | if data["click_type"] == "double" and self.actor_double != None: 74 | self.log("Double Button Click: {}".format(data["entity_id"])) 75 | self.log("Toggling {}".format(self.actor_double)) 76 | # Is on 77 | if self.get_state(self.actor_double) == "on": 78 | # Workaround for Yeelight see https://community.home-assistant.io/t/transition-for-turn-off-service-doesnt-work-for-yeelight-lightstrip/25333/4 79 | if self.actor_single.startswith("light"): 80 | self.call_service( 81 | "light/turn_on", 82 | entity_id=self.actor_single, 83 | transition=1, 84 | brightness_pct=1, 85 | ) 86 | self.timer_handle_list.append( 87 | self.run_in(self.turn_off_workaround, 2) 88 | ) 89 | else: 90 | self.turn_off(self.actor_single) 91 | # Is off 92 | if self.get_state(self.actor_double) == "off": 93 | self.log("Turning {} on".format(self.actor_single)) 94 | if self.actor_single.startswith("light"): 95 | self.call_service( 96 | "light/turn_on", 97 | entity_id=self.actor_single, 98 | transition=1, 99 | brightness_pct=100, 100 | ) 101 | else: 102 | self.turn_on(self.actor_single) 103 | 104 | if data["click_type"] == "long_click_press" and self.actor_hold != None: 105 | self.log("Long Button Click: {}".format(data["entity_id"])) 106 | self.log("Starting Dimmer") 107 | self.dimmer_timer_handle = self.run_every( 108 | self.dimmer_callback, 109 | datetime.datetime.now(), 110 | 0.5, 111 | entity_id=self.actor_hold, 112 | ) 113 | self.timer_handle_list.append(self.dimmer_timer_handle) 114 | 115 | if data["click_type"] == "hold" and self.actor_hold != None: 116 | self.log("Button Release: {}".format(data["entity_id"])) 117 | self.log("Stopping Dimmer") 118 | if self.dimmer_timer_handle != None: 119 | self.cancel_timer(self.dimmer_timer_handle) 120 | 121 | def dimmer_callback(self, kwargs): 122 | """Dimm the by 10% light. If it would dim above 100% start again at 10%""" 123 | brightness_pct_old = ( 124 | int( 125 | self.get_state(self.actor_hold, attribute="all")["attributes"][ 126 | "brightness" 127 | ] 128 | ) 129 | / 255 130 | ) 131 | brightness_pct_new = brightness_pct_old + 0.1 132 | if brightness_pct_new > 1: 133 | brightness_pct_new = 0.1 134 | self.call_service( 135 | "light/turn_on", 136 | entity_id=kwargs["entity_id"], 137 | brightness_pct=brightness_pct_new * 100, 138 | ) 139 | 140 | def turn_off_workaround(self, *kwargs): 141 | self.call_service("light/turn_off", entity_id=self.actor_single) 142 | 143 | def terminate(self): 144 | for listen_event_handle in self.listen_event_handle_list: 145 | self.cancel_listen_event(listen_event_handle) 146 | 147 | for timer_handle in self.timer_handle_list: 148 | self.cancel_timer(timer_handle) 149 | -------------------------------------------------------------------------------- /buttonClicked/buttonClicked.yaml: -------------------------------------------------------------------------------- 1 | # App which toggles entities for single/double presses of xiaomi buttons 2 | # xiaomiroundButtonBedroomClicked: 3 | # module: buttonClicked 4 | # class: ButtonClicked 5 | # sensor: binary_sensor.switch_158d0001b12a12 6 | # actor_single: light.bedroom_yeelight 7 | # actor_double: group.all 8 | # actor_hold: light.bedroom_yeelight 9 | # dependencies: 10 | # - Notifier 11 | 12 | # xiaomisquareButtonLobbyClicked: 13 | # module: buttonClicked 14 | # class: ButtonClicked 15 | # sensor: binary_sensor.switch_158d00021329bc 16 | # actor_single: switch.lobby 17 | # actor_double: switch.lobby 18 | # dependencies: 19 | # - Notifier 20 | 21 | # xiaomiroundButtonBathroomClicked: 22 | # module: buttonClicked 23 | # class: ButtonClicked 24 | # sensor: sensor.0x00158d00012db9e5_click 25 | # actor_single: light.lower_bathroom_yeelight 26 | # actor_double: light.lower_bathroom_yeelight 27 | # actor_hold: light.lower_bathroom_yeelight 28 | # dependencies: 29 | # - Notifier 30 | -------------------------------------------------------------------------------- /comingHome/comingHome.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App to Turn on Lobby Lamp when Door openes and no one is Home 6 | # 7 | # Args: 8 | # 9 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 10 | # sensor: door sensor 11 | # isHome: input_boolean which shows if someone is home eg input_boolean.isHome 12 | # actor (optional): actor to turn on. example: script.receiver_set_source_bluetooth 13 | # service (optional): service to call. example: media_player.volume_set 14 | # service_data (optional): dictionary of attributes for the service call. 15 | # after_sundown (optional): whether to only trigger after sundown. example: True 16 | # Release Notes 17 | # 18 | # Version 1.4.2: 19 | # unwrap service_data 20 | # 21 | # Version 1.4.1: 22 | # fix duplicate line for self.actor 23 | # 24 | # Version 1.4: 25 | # Add service and service_data and make actor optional 26 | # 27 | # Version 1.3.2: 28 | # Check for new != old 29 | # 30 | # Version 1.3.1: 31 | # Actually implement isHome 32 | # 33 | # Version 1.3: 34 | # Added app_switch 35 | # 36 | # Version 1.2: 37 | # Added after_sundown 38 | # 39 | # Version 1.1: 40 | # Using globals 41 | # 42 | # Version 1.0: 43 | # Initial Version 44 | 45 | 46 | class ComingHome(hass.Hass): 47 | def initialize(self): 48 | self.listen_state_handle_list = [] 49 | 50 | self.app_switch = self.args["app_switch"] 51 | self.sensor = self.args["sensor"] 52 | self.isHome = self.args["isHome"] 53 | self.actor = self.args.get("actor") 54 | self.service = self.args.get("service") 55 | self.service_data = self.args.get("service_data") 56 | self.after_sundown = self.args.get("after_sundown") 57 | 58 | self.delay = 2 59 | 60 | self.listen_state_handle_list.append( 61 | self.listen_state(self.state_change, self.sensor) 62 | ) 63 | 64 | def state_change(self, entity, attribute, old, new, kwargs): 65 | if self.get_state(self.app_switch) == "on": 66 | if new != "" and new != old: 67 | isHome_attributes = self.get_state(self.isHome, attribute="all") 68 | isHome_state = isHome_attributes["state"] 69 | last_changed = self.convert_utc(isHome_attributes["last_changed"]) 70 | if isHome_state == "off" or ( 71 | datetime.datetime.now(datetime.timezone.utc) - last_changed 72 | <= datetime.timedelta(seconds=self.delay) 73 | ): 74 | if self.after_sundown is not None and self.after_sundown: 75 | if self.sun_down(): 76 | self.turn_on_actor(self.actor, entity, new) 77 | self.my_call_service( 78 | self.service, self.service_data, entity, new 79 | ) 80 | else: 81 | self.turn_on_actor(self.actor, entity, new) 82 | self.my_call_service( 83 | self.service, self.service_data, entity, new 84 | ) 85 | 86 | def turn_on_actor(self, actor, entity, new): 87 | if self.actor is not None: 88 | self.log("{} changed to {}".format(self.friendly_name(entity), new)) 89 | self.turn_on(actor) 90 | 91 | def my_call_service(self, service, service_data, entity, new): 92 | if self.service is not None: 93 | if self.service_data is not None: 94 | self.log("{} changed to {}".format(self.friendly_name(entity), new)) 95 | self.call_service(service, **service_data) 96 | 97 | def terminate(self): 98 | for listen_state_handle in self.listen_state_handle_list: 99 | self.cancel_listen_state(listen_state_handle) 100 | -------------------------------------------------------------------------------- /comingHome/comingHome.yaml: -------------------------------------------------------------------------------- 1 | #Switch on Lobby lamp when the first person is coming home and the sun is down 2 | # comingHomeYeelight: 3 | # module: comingHome 4 | # class: ComingHome 5 | # app_switch: input_boolean.coming_home_yeelight 6 | # sensor: binary_sensor.contact_door 7 | # isHome: input_boolean.is_home 8 | # actor: switch.large_lamp 9 | # after_sundown: True 10 | 11 | # comingHomeSetVolume: 12 | # module: comingHome 13 | # class: ComingHome 14 | # app_switch: input_boolean.coming_home_set_volume 15 | # sensor: binary_sensor.contact_door 16 | # isHome: input_boolean.is_home 17 | # service: media_player/volume_set 18 | # service_data: 19 | # entity_id: media_player.denon_avr_x1300w 20 | # volume_level: 0.3 -------------------------------------------------------------------------------- /deconz_xiaomi_button.yaml: -------------------------------------------------------------------------------- 1 | DeconzXiaomiButtonBedroom: 2 | module: deconz_xiaomi_button 3 | class: DeconzXiaomiButton 4 | id: round_button_schlafzimmer 5 | actor_single: light.bedroom_yeelight 6 | actor_double: group.all 7 | actor_hold: light.bedroom_yeelight 8 | 9 | DeconzXiaomiButtonLobby: 10 | module: deconz_xiaomi_button 11 | class: DeconzXiaomiButton 12 | id: flur_switch 13 | actor_single: switch.lobby 14 | actor_double: switch.lobby 15 | 16 | DeconzXiaomiButtonLobby: 17 | module: deconz_xiaomi_button 18 | class: DeconzXiaomiButton 19 | id: flur_switch 20 | actor_single: switch.lobby 21 | actor_double: switch.lobby 22 | 23 | DeconzXiaomiButtonBathroom: 24 | module: deconz_xiaomi_button 25 | class: DeconzXiaomiButton 26 | id: round_button_bad 27 | actor_single: light.lower_bathroom_yeelight 28 | actor_double: light.lower_bathroom_yeelight 29 | actor_hold: light.lower_bathroom_yeelight -------------------------------------------------------------------------------- /deconz_xiaomi_button/deconz_xiaomi_button.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which toggles entities for single/double/hold presses of Xiaomi buttons connected via deconz 6 | # 7 | # Args: 8 | # 9 | # id: id of the xiaomi button 10 | # actor_single: actor to toggle on single click 11 | # actor_double: actor to toggle on double click 12 | # actor_hold: actor to dim on hold 13 | # 14 | # Release Notes 15 | # 16 | # Version 2.0.2 17 | # use else when toggling 18 | # 19 | # Version 2.0.1 20 | # use elif when toggling 21 | # 22 | # Version 2.0: 23 | # Removed unneeded workaround for yeelight 24 | # 25 | # Version 1.0: 26 | # Initial Version 27 | 28 | 29 | class DeconzXiaomiButton(hass.Hass): 30 | def initialize(self): 31 | self.listen_event_handle_list = [] 32 | self.timer_handle_list = [] 33 | 34 | self.actor_single = self.args.get("actor_single") 35 | self.actor_double = self.args.get("actor_double") 36 | self.actor_hold = self.args.get("actor_hold") 37 | self.id = self.args["id"] 38 | 39 | self.dimmer_timer_handle = None 40 | 41 | self.listen_event_handle_list.append( 42 | self.listen_event(self.event_detected, "deconz_event") 43 | ) 44 | 45 | def event_detected(self, event_name, data, kwargs): 46 | if data["id"] == self.id: 47 | if data["event"] == 1002 and self.actor_single is not None: 48 | self.log("ButtonClicked: {}".format(data["id"])) 49 | self.log("Toggling {}".format(self.actor_double)) 50 | # Is on 51 | if self.get_state(self.actor_single) == "on": 52 | self.log("Turning {} off".format(self.actor_single)) 53 | self.turn_off(self.actor_single) 54 | # Is off 55 | else: 56 | self.log("Turning {} on".format(self.actor_single)) 57 | self.turn_on(self.actor_single) 58 | 59 | if data["event"] == 1004 and self.actor_double is not None: 60 | self.log("Double Button Click: {}".format(data["id"])) 61 | self.log("Toggling {}".format(self.actor_double)) 62 | # Is on 63 | if self.get_state(self.actor_double) == "on": 64 | self.turn_off(self.actor_double) 65 | # Is off 66 | else: 67 | self.log("Turning {} on".format(self.actor_double)) 68 | self.turn_on(self.actor_double) 69 | 70 | if data["event"] == 1001 and self.actor_hold is not None: 71 | self.log("Long Button Click: {}".format(data["id"])) 72 | self.log("Starting Dimmer") 73 | self.dimmer_timer_handle = self.run_every( 74 | self.dimmer_callback, 75 | datetime.datetime.now(), 76 | 0.5, 77 | entity_id=self.actor_hold, 78 | ) 79 | self.timer_handle_list.append(self.dimmer_timer_handle) 80 | 81 | if data["event"] == 1003 and self.actor_hold is not None: 82 | self.log("Button Release: {}".format(data["id"])) 83 | self.log("Stopping Dimmer") 84 | if self.dimmer_timer_handle is not None: 85 | self.cancel_timer(self.dimmer_timer_handle) 86 | 87 | def dimmer_callback(self, kwargs): 88 | """Dimm the by 10% light. If it would dim above 100% start again at 10%""" 89 | brightness_pct_old = ( 90 | int( 91 | self.get_state(self.actor_hold, attribute="all")["attributes"][ 92 | "brightness" 93 | ] 94 | ) 95 | / 255 96 | ) 97 | brightness_pct_new = brightness_pct_old + 0.1 98 | if brightness_pct_new > 1: 99 | brightness_pct_new = 0.1 100 | self.call_service( 101 | "light/turn_on", 102 | entity_id=kwargs["entity_id"], 103 | brightness_pct=brightness_pct_new * 100, 104 | ) 105 | 106 | def terminate(self): 107 | for listen_event_handle in self.listen_event_handle_list: 108 | self.cancel_listen_event(listen_event_handle) 109 | 110 | for timer_handle in self.timer_handle_list: 111 | self.cancel_timer(timer_handle) 112 | -------------------------------------------------------------------------------- /detectWrongState/detectWrongState.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App which notifies of wrong states based on a state change 5 | # 6 | # Args: 7 | # 8 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 9 | # entities_on (optional): list of entities which should be on 10 | # entities_off (optional): list of entities which should off 11 | # trigger_entity: entity which triggers this app. example: input_boolean.is_home 12 | # trigger_state: new state of trigger_entity which triggers this app. example: "off" 13 | # after_sundown (optional): Only trigger after sundown. example: True 14 | # message_: message to use in notification 15 | # message_off_: message to use in notification 16 | # message_reed_: message to use in notification 17 | # message_reed_off_: message to use in notification 18 | # notify_name: who to notify. example: group_notifications 19 | # use_alexa: use alexa for notification. example: False 20 | # 21 | # Release Notes 22 | # 23 | # Version 2.1: 24 | # More off_states to support alexa_media 25 | # 26 | # Version 2.0: 27 | # Renamed to detectWrongState, notification optional 28 | # 29 | # Version 1.9: 30 | # check unavailable when using get_state 31 | # 32 | # Version 1.8: 33 | # check None when using get_state 34 | # 35 | # Version 1.7: 36 | # check for != off instead of == on 37 | # 38 | # Version 1.6.1: 39 | # fix wrong key access for attributes 40 | # 41 | # Version 1.6: 42 | # garage_door to device_classes of reed sensors 43 | # 44 | # Version 1.5: 45 | # distinguish normal and reed switches by device_class 46 | # 47 | # Version 1.4.1: 48 | # fix wrong assignment of app_switch 49 | # 50 | # Version 1.4: 51 | # Generalize to detectWrongState 52 | # 53 | # Version 1.3: 54 | # use Notify App 55 | # 56 | # Version 1.2: 57 | # message now directly in own yaml instead of message module 58 | # 59 | # Version 1.1: 60 | # Using globals and app_switch 61 | # 62 | # Version 1.0: 63 | # Initial Version 64 | 65 | 66 | class DetectWrongState(hass.Hass): 67 | def initialize(self): 68 | self.listen_state_handle_list = [] 69 | 70 | self.app_switch = self.args["app_switch"] 71 | try: 72 | self.entities_on = self.args["entities_on"].split(",") 73 | except KeyError: 74 | self.entities_on = [] 75 | try: 76 | self.entities_off = self.args["entities_off"].split(",") 77 | except KeyError: 78 | self.entities_off = [] 79 | self.after_sundown = self.args.get("after_sundown") 80 | self.trigger_entity = self.args["trigger_entity"] 81 | self.trigger_state = self.args["trigger_state"] 82 | self.message = self.args.get("message") 83 | self.message_off = self.args.get("message_off") 84 | self.message_reed = self.args.get("message_reed") 85 | self.message_reed_off = self.args.get("message_reed_off") 86 | self.notify_name = self.args.get("notify_name") 87 | self.use_alexa = self.args.get("use_alexa") 88 | 89 | self.notifier = self.get_app("Notifier") 90 | 91 | self.listen_state_handle_list.append( 92 | self.listen_state(self.state_change, self.trigger_entity) 93 | ) 94 | 95 | def state_change(self, entity, attribute, old, new, kwargs): 96 | if self.get_state(self.app_switch) == "on": 97 | if new != "" and new == self.trigger_state: 98 | if self.after_sundown is None or ( 99 | (self.after_sundown and self.sun_down()) 100 | or self.after_sundown is not False 101 | ): 102 | self.check_entities_should_be_off() 103 | self.check_entities_should_be_on() 104 | 105 | def check_entities_should_be_off(self): 106 | off_states = ["off", "unavailable", "paused", "standby"] 107 | for entity in self.entities_off: 108 | state = self.get_state(entity) 109 | self.log(f"entity: {entity}") 110 | if state is not None and state not in off_states: 111 | if self.is_entity_reed_contact(entity): 112 | message = self.message_reed 113 | else: 114 | self.turn_off(entity) 115 | message = self.message 116 | self.send_notification(message, entity) 117 | 118 | def check_entities_should_be_on(self): 119 | for entity in self.entities_on: 120 | state = self.get_state(entity) 121 | if state == "off": 122 | if self.is_entity_reed_contact(entity): 123 | message = self.message_reed_off 124 | else: 125 | self.turn_on(entity) 126 | message = self.message_on 127 | self.send_notification(message, entity) 128 | 129 | def is_entity_reed_contact(self, entity): 130 | reed_types = ["window", "door", "garage_door"] 131 | full_state = self.get_state(entity, attribute="all") 132 | if full_state is not None: 133 | attributes = full_state["attributes"] 134 | self.log("full_state: {}".format(full_state), level="DEBUG") 135 | if attributes.get("device_class") in reed_types: 136 | return True 137 | return False 138 | 139 | def send_notification(self, message, entity): 140 | if message is not None: 141 | formatted_message = message.format(self.friendly_name(entity)) 142 | self.log(formatted_message) 143 | if self.notify_name is not None: 144 | self.notifier.notify( 145 | self.notify_name, formatted_message, useAlexa=self.use_alexa, 146 | ) 147 | 148 | def terminate(self): 149 | for listen_state_handle in self.listen_state_handle_list: 150 | self.cancel_listen_state(listen_state_handle) 151 | -------------------------------------------------------------------------------- /detectWrongState/detectWrongState.yaml: -------------------------------------------------------------------------------- 1 | # detectWrongStateWhenLeaving: 2 | # module: detectWrongState 3 | # class: DetectWrongState 4 | # app_switch: input_boolean.detect_wrong_state_when_leaving 5 | # entities_off: "binary_sensor.contact_bedroom_door,\ 6 | # binary_sensor.contact_bedroom_door_tilted,binary_sensor.contact_door,binary_sensor.contact_guest_window,\ 7 | # binary_sensor.contact_kitchen_window,binary_sensor.contact_studyroom_door,\ 8 | # binary_sensor.contact_studyroom_door_tilted,binary_sensor.contact_terrace_door,\ 9 | # binary_sensor.contact_terrace_door_tilted,binary_sensor.contact_upper_bathroom_window,\ 10 | # media_player.denon_avr_x1300w,switch.large_lamp,switch.small_lamp,switch.snowboard,\ 11 | # light.bedroom_yeelight,light.bar_table,light.lobby_yeelight,light.reading_lamp_yeelight,\ 12 | # light.upper_stairs_yeelight,light.stairs_lower_yeelight,switch.ventilator,light.livingroom_yeelight,\ 13 | # switch.tv,switch.weihnachtslichter,switch.bedroom_receiver,light.lower_bathroom_yeelight,\ 14 | # media_player.kevin_s_echo_dot_unten,media_player.kevins_echo,media_player.kevins_echo_dot,\ 15 | # media_player.kevins_echo_dot_oben,binary_sensor.contact_upper_bathroom_window_tilted,\ 16 | # binary_sensor.contact_badfenster" 17 | # trigger_entity: input_boolean.is_home 18 | # trigger_state: "off" 19 | # message: "Du hast {} angelassen. Ich habe es für dich ausgemacht." 20 | # #message: "You left on {}. I turned it off for you" 21 | # message_off: "Du hast {} vergessen anzumachen. Ich habe es für dich angemacht." 22 | # #message_off: "You forgot to turn on {}. I turned it on for you" 23 | # message_reed: "Du hast {} offen gelassen." 24 | # #message_reed: "You left open {} Dummy." 25 | # message_reed_off: "Du hast {} zu gelassen." 26 | # #message_reed_off: "You left {} closed Dummy." 27 | # notify_name: group_notifications 28 | # use_alexa: False 29 | # log_level: DEBUG 30 | # dependencies: 31 | # - Notifier 32 | 33 | # detectWindowsOpenWhenGoingToBed: 34 | # module: detectWrongState 35 | # class: DetectWrongState 36 | # app_switch: input_boolean.detect_windows_open_when_going_to_bed 37 | # entities_off: "binary_sensor.contact_bathroom_window_tilted,binary_sensor.contact_bedroom_door,\ 38 | # binary_sensor.contact_bedroom_door_tilted,binary_sensor.contact_door,binary_sensor.contact_guest_window,\ 39 | # binary_sensor.contact_kitchen_window,binary_sensor.contact_studyroom_door,\ 40 | # binary_sensor.contact_studyroom_door_tilted,binary_sensor.contact_terrace_door,\ 41 | # binary_sensor.contact_terrace_door_tilted,binary_sensor.contact_upper_bathroom_window,\ 42 | # binary_sensor.contact_upper_bathroom_window_tilted,binary_sensor.contact_badfenster" 43 | # after_sundown: True 44 | # trigger_entity: input_boolean.sleepmode 45 | # trigger_state: "on" 46 | # message: "Du hast {} angelassen. Ich habe es für dich ausgemacht." 47 | # #message: "You left on {}. I turned it off for you" 48 | # message_off: "Du hast {} vergessen anzumachen. Ich habe es für dich angemacht." 49 | # #message_off: "You forgot to turn on {}. I turned it on for you" 50 | # message_reed: "Du hast {} offen gelassen." 51 | # #message_reed: "You left open {} Dummy." 52 | # message_reed_off: "Du hast {} zu gelassen." 53 | # #message_reed_off: "You left {} closed Dummy." 54 | # notify_name: group_notifications 55 | # use_alexa: True 56 | # log_level: DEBUG 57 | # dependencies: 58 | # - Notifier 59 | 60 | # detectDevicesOnWhenGoingToBed: 61 | # module: detectWrongState 62 | # class: DetectWrongState 63 | # app_switch: input_boolean.detect_devices_on_when_going_to_bed 64 | # entities_off: "media_player.denon_avr_x1300w,switch.large_lamp,\ 65 | # switch.small_lamp,switch.snowboard,light.bedroom_yeelight,light.bar_table,light.lobby_yeelight,\ 66 | # light.reading_lamp_yeelight,light.upper_stairs_yeelight,light.stairs_lower_yeelight,switch.ventilator,light.livingroom_yeelight,\ 67 | # switch.tv,switch.weihnachtslichter,switch.bedroom_receiver,switch.tv,light.bar_table,light.lower_bathroom_yeelight,\ 68 | # switch.markise, switch.coffee_machine_plug_relay" 69 | # trigger_entity: input_boolean.sleepmode 70 | # trigger_state: "on" 71 | # log_level: DEBUG 72 | # dependencies: 73 | # - Notifier -------------------------------------------------------------------------------- /ench.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ench: 3 | module: ench 4 | class: EnCh 5 | notify: "notify.kevin" 6 | exclude: 7 | - device_tracker.venue_8* 8 | - person.kevin 9 | - device_tracker.sonoff_large_ventilator_7998 10 | - device_tracker.sonoff_ventilator_8133 11 | - device_tracker.android* 12 | - device_tracker.astrids_mbp 13 | - device_tracker.franzi_s_iphone 14 | - device_tracker.galaxy* 15 | - device_tracker.iphone* 16 | - device_tracker.oneplus* 17 | - device_tracker.unifi* 18 | - light.group_0 19 | - media_player.kevin_s* 20 | - sensor.192_168_1_39* 21 | - sensor.192_168_1_48* 22 | - sensor.large_ventilator* 23 | - sensor.ventilator* 24 | - switch.ventilator 25 | - switch.large_ventilator 26 | - sensor.travel_time_next* 27 | - sensor.glances*_temp 28 | - sensor.publish_ip_on_boot 29 | - sensor.*odroid* 30 | - media_player.55pus7304_12 31 | - media_player.fernseher 32 | - sensor.consumption_31 33 | - sensor.power_30 34 | - sensor.openweathermap* 35 | - light.bar_table 36 | - sensor.*_nachster_wecker 37 | - switch.xiaomi_plug 38 | - media_player.55pus7304_12_* 39 | # Alexa Media Player 40 | - sensor.*next_alarm 41 | - sensor.*next_reminder 42 | - sensor.*next_timer 43 | - sensor.this_device* 44 | - switch.*_repeat_switch* 45 | - switch.*_shuffle_switch* 46 | - switch.*do_not_disturb_switch 47 | battery: 48 | interval_min: 180 49 | min_level: 20 50 | unavailable: 51 | interval_min: 60 52 | max_unavailable_min: 15 53 | -------------------------------------------------------------------------------- /eventMonitor/eventMonitor.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | """ 4 | Monitor events and output changes to the verbose_log. Nice for debugging purposes. 5 | Arguments: 6 | - events: List of events to monitor 7 | """ 8 | 9 | 10 | class Monitor(hass.Hass): 11 | def initialize(self): 12 | self.listen_event_handle_list = [] 13 | 14 | events = self.args["events"] 15 | 16 | if events != None: 17 | for event in self.split_device_list(self.args["events"]): 18 | self.log('watching event "{}" for state changes'.format(event)) 19 | self.listen_event_handle_list.append( 20 | self.listen_event(self.changed, event) 21 | ) 22 | if len(self.listen_event_handle_list) == 0: 23 | self.log("watching all events for state changes") 24 | self.listen_event_handle_list.append(self.listen_event(self.changed)) 25 | 26 | def changed(self, event_name, data, kwargs): 27 | self.log(event_name + ": " + str(data)) 28 | 29 | def terminate(self): 30 | for listen_event_handle in self.listen_event_handle_list: 31 | self.cancel_listen_event(listen_event_handle) 32 | -------------------------------------------------------------------------------- /eventMonitor/eventMonitor.yaml: -------------------------------------------------------------------------------- 1 | #eventMonitor: 2 | # module: eventMonitor 3 | # class: Monitor 4 | # events: -------------------------------------------------------------------------------- /faceRecognitionBot/faceRecognitionBot.yaml: -------------------------------------------------------------------------------- 1 | # faceRecognitionBot: 2 | # module: faceRecognitionBot 3 | # class: FaceRecognitionBot 4 | # app_switch: input_boolean.facebox_notifier 5 | # sensor: binary_sensor.contact_door 6 | # button: binary_sensor.switch_158d000215aa28 7 | # camera: camera.android_ip_webcam_door 8 | # local_file_camera: camera.saved_image 9 | # filename: !secret facebox_notifier_filename 10 | # image_processing: image_processing.facebox 11 | # notify_name: group_notifications 12 | # wol_switch: switch.facebox_wol 13 | # user_id: !secret telegram_user_id 14 | # facebox_source_directory: !secret facebox_folderpath 15 | # facebox_unknown_directory: !secret facebox_unknown_directory 16 | # facebox_noface_directory: !secret facebox_noface_directory 17 | # facebox_known_faces_directory: !secret facebox_known_faces_directory 18 | # facebox_healthcheck_filename: !secret facebox_healthcheck_filename 19 | # healthcheck_face_name: Kevin 20 | # number_of_images: 10 21 | # waitBeforeSnapshot: 1 22 | # ip: !secret facebox_ip 23 | # port: !secret facebox_port 24 | # message_face_identified: "Ich habe {} erkannt" 25 | # #message_face_identified: "I have recognized {}." 26 | # message_unkown_face: "Ich habe dieses Gesicht nicht erkannt. Kennst du es?" 27 | # #message_unkown_face: "I have not recognized this face. Do you know it?" 28 | # message_unkown_face_with_known: "Ich habe auch ein unbekanntes Gesicht entdeckt." 29 | # #message_unkown_face_with_known: "I have also discovered an unknown face." 30 | # message_provide_name: "Wenn du das Gesicht kennst, kannst du mir einfach innerhalb der nächsten {} Minuten den Namen schreiben. Dann merke ich ihn mir!" 31 | # #message_provide_name: "If you know the face you can write the name to me within the next {} minutes. I will remember it!" 32 | # message_name_provided: "Okay. Ich merke mir, dass das {} ist" 33 | # #message_name_provided: "Okay. I will remember that this is {}" 34 | # message_name_provided_callback: "{} sagte, dass dies {} ist." 35 | # #message_name_provided_callback: "{} said that this is {}" 36 | # dependencies: 37 | # - Notifier -------------------------------------------------------------------------------- /globals.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def random_arg(argList): 5 | ############################################ 6 | # pick a random text from a list 7 | ############################################ 8 | if isinstance(argList, list): 9 | text = random.choice(argList) 10 | else: 11 | text = argList 12 | return text 13 | -------------------------------------------------------------------------------- /heartbeat/heartbeat.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | from requests.exceptions import HTTPError 3 | 4 | # 5 | # App which sets a homeassistant entity as a heartbeat to check for threadstarvation etc 6 | # 7 | # Args: 8 | # sensor: sensor.appdaemon_heartbeat 9 | # 10 | # Release Notes 11 | # 12 | # Version 1.1: 13 | # Set start to None run_minutely will run after 1 minute 14 | # 15 | # Version 1.0: 16 | # Initial Version 17 | 18 | 19 | class Heartbeat(hass.Hass): 20 | def initialize(self): 21 | self.timer_handle_list = [] 22 | 23 | self.sensor = self.args["sensor"] 24 | 25 | self.heartbeat(None) 26 | 27 | self.timer_handle_list.append(self.run_minutely(self.heartbeat, start=None)) 28 | 29 | def heartbeat(self, kwargs): 30 | try: 31 | self.set_state(self.sensor, state=str(self.time())) 32 | self.log("Heartbeat", level="DEBUG") 33 | except HTTPError as exception: 34 | self.log( 35 | "Error trying to set entity. Will try again in 5s. Error: {}".format( 36 | exception 37 | ), 38 | level="WARNING", 39 | ) 40 | self.timer_handle_list.append(self.run_in(self.heartbeat, 5)) 41 | 42 | def terminate(self): 43 | for timer_handle in self.timer_handle_list: 44 | self.cancel_timer(timer_handle) 45 | -------------------------------------------------------------------------------- /heartbeat/heartbeat.yaml: -------------------------------------------------------------------------------- 1 | heartbeat: 2 | module: heartbeat 3 | class: Heartbeat 4 | sensor: sensor.appdaemon_heartbeat -------------------------------------------------------------------------------- /homeArrivalNotifier/homeArrivalNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App to send a notification if someone arrives at home 5 | # 6 | # Args: 7 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 8 | # input_boolean: input boolean which holds the information of someone is home or not 9 | # notify_name: Who to notify 10 | # user_name: name to use in notification message 11 | # zone_name: Name of the zone 12 | # message: message to use in notification 13 | # Release Notes 14 | # 15 | # Version 1.4.1: 16 | # Use consistent message variable 17 | # 18 | # Version 1.4: 19 | # use Notify App 20 | # 21 | # Version 1.3: 22 | # message now directly in own yaml instead of message module 23 | # 24 | # Version 1.2: 25 | # Added app_switch 26 | # 27 | # Version 1.1: 28 | # Added user_name 29 | # 30 | # Version 1.0: 31 | # Initial Version 32 | 33 | 34 | class HomeArrivalNotifier(hass.Hass): 35 | def initialize(self): 36 | self.listen_state_handle_list = [] 37 | 38 | self.app_switch = self.args["app_switch"] 39 | self.zone_name = self.args["zone_name"] 40 | self.input_boolean = self.args["input_boolean"] 41 | self.notify_name = self.args["notify_name"] 42 | self.user_name = self.args["user_name"] 43 | self.message = self.args["message"] 44 | 45 | self.notifier = self.get_app("Notifier") 46 | 47 | self.listen_state_handle_list.append( 48 | self.listen_state(self.state_change, self.input_boolean) 49 | ) 50 | 51 | def state_change(self, entity, attribute, old, new, kwargs): 52 | if self.get_state(self.app_switch) == "on": 53 | if new != "" and new != old: 54 | self.log("{} changed from {} to {}".format(entity, old, new)) 55 | if new == "on": 56 | self.log( 57 | "{} arrived at {}".format(self.notify_name, self.zone_name) 58 | ) 59 | self.notifier.notify( 60 | self.notify_name, self.message.format(self.user_name) 61 | ) 62 | 63 | def terminate(self): 64 | for listen_state_handle in self.listen_state_handle_list: 65 | self.cancel_listen_state(listen_state_handle) 66 | -------------------------------------------------------------------------------- /homeArrivalNotifier/homeArrivalNotifier.yaml: -------------------------------------------------------------------------------- 1 | #Notification if user one arrives at home 2 | # homeArrivalNotifierUserOne: 3 | # module: homeArrivalNotifier 4 | # class: HomeArrivalNotifier 5 | # app_switch: input_boolean.home_arrival_notifier_user_one 6 | # input_boolean: input_boolean.user_one_home 7 | # notify_name: group_notifications 8 | # user_name: Kevin 9 | # zone_name: Home 10 | # message: "Willkommen zu Hause {}." 11 | # #message: "Welcome Home {}." 12 | # dependencies: 13 | # - Notifier 14 | 15 | # #Notification if user two arrives at home 16 | # homeArrivalNotifierUserTwo: 17 | # module: homeArrivalNotifier 18 | # class: HomeArrivalNotifier 19 | # app_switch: input_boolean.home_arrival_notifier_user_two 20 | # input_boolean: input_boolean.user_two_home 21 | # notify_name: group_notifications 22 | # user_name: Sina 23 | # zone_name: Home 24 | # message: "Willkommen zu Hause {}." 25 | # #message: "Welcome Home {}." 26 | # dependencies: 27 | # - Notifier -------------------------------------------------------------------------------- /images/alarmClock.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/alarmClock.PNG -------------------------------------------------------------------------------- /images/dishWasherNotify.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/dishWasherNotify.PNG -------------------------------------------------------------------------------- /images/failedLogin.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/failedLogin.PNG -------------------------------------------------------------------------------- /images/googleTravelTimes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/googleTravelTimes.PNG -------------------------------------------------------------------------------- /images/logo-pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/logo-pretty.png -------------------------------------------------------------------------------- /images/logo-round-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/logo-round-192x192.png -------------------------------------------------------------------------------- /images/next_appoint_leave_modifier.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/next_appoint_leave_modifier.PNG -------------------------------------------------------------------------------- /images/next_appoint_leave_modifier_notification.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/next_appoint_leave_modifier_notification.PNG -------------------------------------------------------------------------------- /images/notifyOfActionWhenAway.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/notifyOfActionWhenAway.PNG -------------------------------------------------------------------------------- /images/plantWateringReminder.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/plantWateringReminder.PNG -------------------------------------------------------------------------------- /images/plantWateringReminderAcknowledged.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/plantWateringReminderAcknowledged.PNG -------------------------------------------------------------------------------- /images/roggenNotify.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/roggenNotify.PNG -------------------------------------------------------------------------------- /images/ventilatorAutomation.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/ventilatorAutomation.PNG -------------------------------------------------------------------------------- /images/washingMachineStart.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eifinger/appdaemon-scripts/0a8e19f5616bb71049907f53fdd818aea68269c7/images/washingMachineStart.PNG -------------------------------------------------------------------------------- /isHomeDeterminer/isHomeDeterminer.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import globals 3 | 4 | # 5 | # App to 6 | # 7 | # Args: 8 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 9 | # input_booleans: list of input boolean which determine if a user is home 10 | # ishome: input boolean which determins if someone is home 11 | # message: message to use in notification 12 | # Release Notes 13 | # 14 | # Version 1.3: 15 | # message now a list 16 | # 17 | # Version 1.2: 18 | # message now directly in own yaml instead of message module 19 | # 20 | # Version 1.1: 21 | # Added app_switch 22 | # 23 | # Version 1.0: 24 | # Initial Version 25 | 26 | 27 | class IsHomeDeterminer(hass.Hass): 28 | def initialize(self): 29 | self.listen_state_handle_list = [] 30 | 31 | self.app_switch = self.args["app_switch"] 32 | self.ishome = self.args["ishome"] 33 | self.input_booleans = self.args["input_booleans"].split(",") 34 | self.message = self.args["message"] 35 | 36 | if self.get_state(self.app_switch) == "on": 37 | for input_boolean in self.input_booleans: 38 | self.log( 39 | "{} is {}".format(input_boolean, self.get_state(input_boolean)) 40 | ) 41 | self.listen_state_handle_list.append( 42 | self.listen_state(self.state_change, input_boolean) 43 | ) 44 | if ( 45 | self.get_state(input_boolean) == "on" 46 | and self.get_state(self.ishome) == "off" 47 | ): 48 | self.turn_on(self.ishome) 49 | self.log("Setting {} to on".format(self.ishome)) 50 | if ( 51 | self.get_state(input_boolean) == "off" 52 | and self.get_state(self.ishome) == "on" 53 | ): 54 | if self.are_others_away(input_boolean): 55 | self.turn_off(self.ishome) 56 | self.log("Setting {} to off".format(self.ishome)) 57 | notify_message = globals.random_arg(self.message) 58 | self.log("notify_messsage: {}".format(notify_message)) 59 | self.call_service( 60 | "notify/group_notifications", message=notify_message 61 | ) 62 | 63 | def state_change(self, entity, attribute, old, new, kwargs): 64 | if self.get_state(self.app_switch) == "on": 65 | if new != "" and new != old: 66 | self.log("{} changed from {} to {}".format(entity, old, new)) 67 | if new == "on": 68 | self.turn_on(self.ishome) 69 | self.log("Setting {} to on".format(self.ishome)) 70 | if new == "off": 71 | if self.are_others_away(entity): 72 | self.turn_off(self.ishome) 73 | self.log("Setting {} to off".format(self.ishome)) 74 | notify_message = globals.random_arg(self.message) 75 | self.log("notify_messsage: {}".format(notify_message)) 76 | self.call_service( 77 | "notify/group_notifications", message=notify_message 78 | ) 79 | 80 | def are_others_away(self, entity): 81 | self.log("Entity: {}".format(entity)) 82 | for input_boolean in self.input_booleans: 83 | self.log("{} is {}".format(input_boolean, self.get_state(input_boolean))) 84 | if input_boolean == entity: 85 | pass 86 | elif self.get_state(input_boolean) == "on": 87 | self.log("{} is still at on".format(input_boolean)) 88 | return False 89 | self.log("Everybody not home") 90 | return True 91 | 92 | def terminate(self): 93 | for listen_state_handle in self.listen_state_handle_list: 94 | self.cancel_listen_state(listen_state_handle) 95 | -------------------------------------------------------------------------------- /isHomeDeterminer/isHomeDeterminer.yaml: -------------------------------------------------------------------------------- 1 | # #Control the isHome state. Determines if someone is home or all persons are away 2 | # isHomeDeterminer: 3 | # module: isHomeDeterminer 4 | # class: IsHomeDeterminer 5 | # app_switch: input_boolean.is_home_determiner 6 | # ishome: input_boolean.is_home 7 | # input_booleans: input_boolean.user_one_home,input_boolean.user_two_home 8 | # message: 9 | # - "Es ist keiner mehr zu Hause." 10 | # - "Keiner mehr da? Panda Party!" 11 | # - "Ich passe auf die Wohnung auf, einen schönen Tag" 12 | # - "Tschüss, bis nachher" 13 | # #message: "Everyone left home. Setting isHome to off" 14 | # global_dependencies: 15 | # - globals 16 | # - secrets -------------------------------------------------------------------------------- /isUserHomeDeterminer/isUserHomeDeterminer.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | from requests.exceptions import HTTPError 4 | 5 | # 6 | # App to toggle an input boolean when a person enters or leaves home. 7 | # This is determined based on a combination of a GPS device tracker and the door sensor. 8 | # 9 | # - If the door sensor opens and the device_tracker changed to "home" in the last self.delay minutes 10 | # this means someone got home 11 | # - If the door sensor opens and the device_tracker changes to "not_home" after that 12 | # this means someone left home 13 | # 14 | # Args: 15 | # 16 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 17 | # input_boolean: input_boolean which shows if someone is home e.g. input_boolean.isHome 18 | # device_tracker: device tracker or person of the user to track e.g. device_tracker.simon 19 | # door_sensor: Door sensor which indicated the front door opened e.g. binary_sensor.door_window_sensor_158d000126a57b 20 | # 21 | # Release Notes 22 | # 23 | # Version 1.5: 24 | # Wait to leave home until door is opened again 25 | # 26 | # Version 1.4.3: 27 | # check for listen_state_callback == None before triggering again 28 | # 29 | # Version 1.4.2: 30 | # cancel listen callback only when its not None 31 | # 32 | # Version 1.4.1: 33 | # fix for 503, fix for listen callback not being cancelled correctly 34 | # 35 | # Version 1.4: 36 | # message now directly in own yaml instead of message module 37 | # 38 | # Version 1.3: 39 | # Added app_switch 40 | # 41 | # Version 1.2: 42 | # Change checking after a delay to a event based system 43 | # 44 | # Version 1.1: 45 | # Set when initializing (also when HA restarts) 46 | # 47 | # Version 1.0: 48 | # Initial Version 49 | 50 | 51 | class IsUserHomeDeterminer(hass.Hass): 52 | def initialize(self): 53 | self.listen_state_handle_list = [] 54 | self.timer_handle_list = [] 55 | 56 | self.delay = 600 57 | 58 | self.app_switch = self.args["app_switch"] 59 | self.input_boolean = self.args["input_boolean"] 60 | self.device_tracker = self.args["device_tracker"] 61 | self.door_sensor = self.args["door_sensor"] 62 | 63 | device_tracker_state = self.get_state(self.device_tracker, attribute="all") 64 | if self.get_state(self.app_switch) == "on": 65 | if device_tracker_state["state"] == "home": 66 | self.log("User is home") 67 | self.timer_handle_list.append( 68 | self.run_in( 69 | self.turn_on_callback, 0, turn_on_entity=self.input_boolean 70 | ) 71 | ) 72 | else: 73 | self.log("User is not home") 74 | self.timer_handle_list.append( 75 | self.run_in( 76 | self.turn_off_callback, 0, turn_off_entity=self.input_boolean 77 | ) 78 | ) 79 | 80 | self.listen_state_handle_list.append( 81 | self.listen_state(self.state_change, self.door_sensor) 82 | ) 83 | 84 | self.listen_state_handle = None 85 | 86 | def state_change(self, entity, attribute, old, new, kwargs): 87 | if self.get_state(self.app_switch) == "on": 88 | if new != "" and new != old: 89 | self.log("{} changed from {} to {}".format(entity, old, new)) 90 | if new == "on" and old == "off": 91 | self.cancel_listen_state_callback(None) 92 | device_tracker_state = self.get_state( 93 | self.device_tracker, attribute="all" 94 | ) 95 | self.log("device_tracker_state: {}".format(device_tracker_state),) 96 | last_changed = device_tracker_state["last_changed"] 97 | self.log("last_changed: {}".format(last_changed)) 98 | # User got home: Device tracker changed to home before door sensor triggered 99 | if device_tracker_state["state"] == "home" and ( 100 | ( 101 | datetime.datetime.now(datetime.timezone.utc) 102 | - self.convert_utc(last_changed) 103 | ) 104 | < datetime.timedelta(seconds=self.delay) 105 | ): 106 | self.log("User got home") 107 | self.turn_on(self.input_boolean) 108 | # User got home: Device tracker is still not home. 109 | # Wait if it changes to home in the next self.delay seconds 110 | elif device_tracker_state["state"] != "home": 111 | self.log("Wait for device tracker to change to 'home'") 112 | self.listen_state_handle = self.listen_state( 113 | self.check_if_user_got_home, self.device_tracker 114 | ) 115 | self.listen_state_handle_list.append(self.listen_state_handle) 116 | self.timer_handle_list.append( 117 | self.run_in(self.cancel_listen_state_callback, self.delay) 118 | ) 119 | # User left home: Device tracker is still home. 120 | # Wait if it changes to not_home 121 | elif device_tracker_state["state"] == "home": 122 | self.log("Wait for device tracker to change to 'not_home'") 123 | self.listen_state_handle = self.listen_state( 124 | self.check_if_user_left_home, self.device_tracker 125 | ) 126 | self.listen_state_handle_list.append(self.listen_state_handle) 127 | 128 | def cancel_listen_state_callback(self, kwargs): 129 | if self.listen_state_handle is not None: 130 | self.log( 131 | "Timeout while waiting for user to get/leave home. Cancel listen_state" 132 | ) 133 | if self.listen_state_handle in self.listen_state_handle_list: 134 | self.listen_state_handle_list.remove(self.listen_state_handle) 135 | self.cancel_listen_state(self.listen_state_handle) 136 | self.listen_state_handle = None 137 | 138 | def check_if_user_left_home(self, entity, attribute, old, new, kwargs): 139 | if new != "home": 140 | self.log("User left home") 141 | if self.listen_state_handle in self.listen_state_handle_list: 142 | self.listen_state_handle_list.remove(self.listen_state_handle) 143 | if self.listen_state_handle != None: 144 | self.cancel_listen_state(self.listen_state_handle) 145 | self.listen_state_handle = None 146 | self.timer_handle_list.append( 147 | self.run_in( 148 | self.turn_off_callback, 1, turn_off_entity=self.input_boolean 149 | ) 150 | ) 151 | 152 | def check_if_user_got_home(self, entity, attribute, old, new, kwargs): 153 | if new == "home": 154 | self.log("User got home") 155 | if self.listen_state_handle in self.listen_state_handle_list: 156 | self.listen_state_handle_list.remove(self.listen_state_handle) 157 | if self.listen_state_handle is not None: 158 | self.cancel_listen_state(self.listen_state_handle) 159 | self.listen_state_handle = None 160 | self.timer_handle_list.append( 161 | self.run_in( 162 | self.turn_on_callback, 1, turn_on_entity=self.input_boolean 163 | ) 164 | ) 165 | 166 | def turn_on_callback(self, kwargs): 167 | """This is needed because the turn_on command can result in a HTTP 503 when homeassistant is restarting""" 168 | try: 169 | self.turn_on(kwargs["turn_on_entity"]) 170 | except HTTPError as exception: 171 | self.log( 172 | "Error trying to turn on entity. Will try again in 1s. Error: {}".format( 173 | exception 174 | ), 175 | level="WARNING", 176 | ) 177 | self.timer_handle_list.append( 178 | self.run_in( 179 | self.turn_on_callback, 1, turn_on_entity=kwargs["turn_on_entity"] 180 | ) 181 | ) 182 | 183 | def turn_off_callback(self, kwargs): 184 | """This is needed because the turn_off command can result in a HTTP 503 when homeassistant is restarting""" 185 | try: 186 | self.turn_off(kwargs["turn_off_entity"]) 187 | except HTTPError as exception: 188 | self.log( 189 | "Error trying to turn off entity. Will try again in 1s. Error: {}".format( 190 | exception 191 | ), 192 | level="WARNING", 193 | ) 194 | self.timer_handle_list.append( 195 | self.run_in( 196 | self.turn_off_callback, 1, turn_off_entity=kwargs["turn_off_entity"] 197 | ) 198 | ) 199 | 200 | def terminate(self): 201 | for listen_state_handle in self.listen_state_handle_list: 202 | self.cancel_listen_state(listen_state_handle) 203 | 204 | for timer_handle in self.timer_handle_list: 205 | self.cancel_timer(timer_handle) 206 | -------------------------------------------------------------------------------- /isUserHomeDeterminer/isUserHomeDeterminer.yaml: -------------------------------------------------------------------------------- 1 | # #Determine if user one gets/leaves home 2 | # isUserHomeDeterminerUserOne: 3 | # module: isUserHomeDeterminer 4 | # class: IsUserHomeDeterminer 5 | # app_switch: input_boolean.is_user_home_determiner_user_one 6 | # input_boolean: input_boolean.user_one_home 7 | # device_tracker: person.kevin 8 | # door_sensor: binary_sensor.contact_door 9 | 10 | # #Determine if user two gets/leaves home 11 | # isUserHomeDeterminerUserTwo: 12 | # module: isUserHomeDeterminer 13 | # class: IsUserHomeDeterminer 14 | # app_switch: input_boolean.is_user_home_determiner_user_two 15 | # input_boolean: input_boolean.user_two_home 16 | # device_tracker: person.sina 17 | # door_sensor: binary_sensor.contact_door -------------------------------------------------------------------------------- /leavingZoneNotifier/leavingZoneNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App to notify if user_one is leaving a zone. 6 | # User had to be in that zone 3 minutes before 7 | # in order for the notification to be triggered 8 | # 9 | # Args: 10 | # app_switch: on/off switch for this app. 11 | # example: input_boolean.turn_fan_on_when_hot 12 | # device: Device to track 13 | # user_name: Name of the user used in the notification message 14 | # delay: seconds to wait before notifying. Maybe user returns to zone. 15 | # This should be too small to avoid false positives from your tracker. 16 | # I am using GPS Logger on Android and sometimes my device switches 17 | # from work to home and 2 minutes later back. example: 120 18 | # lingering_time: time a user has to be in a zone to trigger this app. 19 | # example: 3600 20 | # zone: zone name from which the user is leaving 21 | # notify_name: Who to notify. example: group_notifications 22 | # message: localized message to use in notification 23 | # travel_time_sensor (optional): Sensor showing the travel time home. 24 | # example: sensor.travel_time_home_user_one 25 | # travel_time_sensor_message (optional): Additional notify message. 26 | # 27 | # Release Notes 28 | # 29 | # Version 1.11: 30 | # Catch new state might be None 31 | # 32 | # Version 1.10: 33 | # Catch old state might be None during startup 34 | # 35 | # Version 1.9: 36 | # PEP8 style and log message when updating travel_time_sensor 37 | # 38 | # Version 1.8: 39 | # Add travel time in notification message 40 | # 41 | # Version 1.7.1: 42 | # Fix delay in notify message. Only input the minutes not the tuple 43 | # 44 | # Version 1.7: 45 | # use Notify App 46 | # 47 | # Version 1.6: 48 | # notify message includes delay 49 | # 50 | # Version 1.5: 51 | # message now directly in own yaml instead of message module 52 | # 53 | # Version 1.4: 54 | # additional note for delay and better handling of zone_entered for 55 | # false positives 56 | # 57 | # Version 1.3: 58 | # delay and lingering_time now as args 59 | # 60 | # Version 1.2: 61 | # Added app_switch 62 | # 63 | # Version 1.1: 64 | # Rework without proximity 65 | # 66 | # Version 1.0: 67 | # Initial Version 68 | 69 | 70 | class LeavingZoneNotifier(hass.Hass): 71 | def initialize(self): 72 | 73 | self.listen_state_handle_list = [] 74 | self.timer_handle_list = [] 75 | 76 | self.app_switch = self.args["app_switch"] 77 | self.user_name = self.args["user_name"] 78 | self.zone = self.args["zone"] 79 | self.notify_name = self.args["notify_name"] 80 | self.device = self.args["device"] 81 | # 'lingering_time' the time a user has to stay in a zone 82 | # for this app to trigger 83 | self.lingering_time = self.args["lingering_time"] 84 | self.delay = self.args["delay"] 85 | self.message = self.args["message"] 86 | self.travel_time_sensor = self.args.get("travel_time_sensor") 87 | self.travel_time_sensor_message = self.args.get("travel_time_sensor_message") 88 | 89 | self.user_entered_zone = None 90 | self.false_positive = False 91 | 92 | self.notifier = self.get_app("Notifier") 93 | 94 | self.listen_state_handle_list.append( 95 | self.listen_state(self.zone_state_change, self.device, attribute="all") 96 | ) 97 | 98 | def zone_state_change(self, entity, attributes, old, new, kwargs): 99 | """Check if user entered or left a zone.""" 100 | if self.get_state(self.app_switch) == "on": 101 | if new is not None: 102 | last_changed = self.convert_utc(new["last_changed"]) 103 | if old is not None: 104 | old_state = old["state"] 105 | self.log( 106 | "Zone of {} changed from {} to {}.".format( 107 | self.friendly_name(entity), old_state, new["state"] 108 | ), 109 | ) 110 | if ( 111 | new["state"] == self.zone 112 | and old_state != self.zone 113 | and self.false_positive is False 114 | ): 115 | self.log("Setting user_entered_zone to {}".format(last_changed)) 116 | self.user_entered_zone = last_changed 117 | if old_state == self.zone and new["state"] != self.zone: 118 | if self.user_entered_zone is None or ( 119 | last_changed - self.user_entered_zone 120 | >= datetime.timedelta(seconds=self.lingering_time) 121 | ): 122 | self.log( 123 | "Zone of {} changed from {} to {}. Wait {} seconds until notification.".format( 124 | self.friendly_name(entity), 125 | old_state, 126 | new["state"], 127 | self.delay, 128 | ) 129 | ) 130 | self.timer_handle_list.append( 131 | self.run_in(self.notify_user, self.delay, old_zone=old) 132 | ) 133 | self.false_positive = True 134 | self.log("Setting false_positive to {}".format(self.false_positive)) 135 | 136 | def notify_user(self, kwargs): 137 | # Check if user did not come back to the zone in the meantime 138 | if self.get_state(self.device) != kwargs["old_zone"]: 139 | if self.travel_time_sensor is not None: 140 | self.log( 141 | "Updating travel_time_sensor: {}".format(self.travel_time_sensor) 142 | ) 143 | 144 | self.call_service( 145 | "homeassistant/update_entity", entity_id=self.travel_time_sensor 146 | ) 147 | 148 | self.timer_handle_list.append(self.run_in(self.notify_user_callback, 2)) 149 | else: 150 | self.log("self.travel_time_sensor is not None") 151 | self.log("Notify user") 152 | self.notifier.notify( 153 | self.notify_name, 154 | self.message.format( 155 | self.user_name, self.zone, divmod(self.delay, 60)[0] 156 | ), 157 | ) 158 | self.false_positive = False 159 | self.log("Setting false_positive to {}".format(self.false_positive)) 160 | 161 | def notify_user_callback(self, kwargs): 162 | self.log("Notify user") 163 | self.notifier.notify( 164 | self.notify_name, 165 | self.message.format(self.user_name, self.zone, divmod(self.delay, 60)[0]) 166 | + self.travel_time_sensor_message.format( 167 | self.get_state(self.travel_time_sensor) 168 | ), 169 | ) 170 | self.false_positive = False 171 | self.log("Setting false_positive to {}".format(self.false_positive)) 172 | 173 | def terminate(self): 174 | for listen_state_handle in self.listen_state_handle_list: 175 | self.cancel_listen_state(listen_state_handle) 176 | 177 | for timer_handle in self.timer_handle_list: 178 | self.cancel_timer(timer_handle) 179 | -------------------------------------------------------------------------------- /leavingZoneNotifier/leavingZoneNotifier.yaml: -------------------------------------------------------------------------------- 1 | # leavingWorkNotifierUserOne: 2 | # module: leavingZoneNotifier 3 | # class: LeavingZoneNotifier 4 | # app_switch: input_boolean.leaving_work_notifier_user_one 5 | # device: person.kevin 6 | # user_name: Kevin 7 | # lingering_time: 3600 8 | # delay: 120 9 | # zone: Arbeit 10 | # notify_name: group_notifications 11 | # message: "{} hat {} vor {} Minuten verlassen." 12 | # #message: "{} left {} {} minutes ago" 13 | # travel_time_sensor: sensor.travel_time_home_user_one 14 | # travel_time_sensor_message: "Es dauert circa {} Minuten bis nach Hause." 15 | # #travel_time_sensor_message: "The travel time is {}." 16 | # dependencies: 17 | # - Notifier 18 | 19 | # leavingWorkNotifierUserTwo: 20 | # module: leavingZoneNotifier 21 | # class: LeavingZoneNotifier 22 | # app_switch: input_boolean.leaving_work_notifier_user_two 23 | # device: person.sina 24 | # user_name: Sina 25 | # lingering_time: 3600 26 | # delay: 120 27 | # zone: !secret friendly_name_work_user_two 28 | # notify_name: group_notifications 29 | # message: "{} hat {} vor {} Minuten verlassen." 30 | # #message: "{} left {} {} minutes ago" 31 | # travel_time_sensor: sensor.travel_time_home_user_two 32 | # travel_time_sensor_message: "Es dauert circa {} Minuten bis nach Hause." 33 | # #travel_time_sensor_message: "The travel time is {}." 34 | # dependencies: 35 | # - Notifier 36 | 37 | # leavingElmoNotifierUserTwo: 38 | # module: leavingZoneNotifier 39 | # class: LeavingZoneNotifier 40 | # app_switch: input_boolean.leaving_elmo_notifier_user_two 41 | # device: person.sina 42 | # user_name: Sina 43 | # lingering_time: 3600 44 | # delay: 120 45 | # zone: Elmo 46 | # notify_name: group_notifications 47 | # message: "{} hat {} vor {} Minuten verlassen." 48 | # #message: "{} left {} {} minutes ago" 49 | # travel_time_sensor: sensor.travel_time_home_user_two 50 | # travel_time_sensor_message: "Es dauert circa {} Minuten bis nach Hause." 51 | # #travel_time_sensor_message: "The travel time is {}." 52 | # dependencies: 53 | # - Notifier -------------------------------------------------------------------------------- /motionTrigger/motionTrigger.yaml: -------------------------------------------------------------------------------- 1 | # bedroomMotionTrigger: 2 | # module: motionTrigger 3 | # class: MotionTrigger 4 | # app_switch: input_boolean.bedroom_motion_trigger 5 | # sensor: binary_sensor.presence_bedroom 6 | # entity_on: light.bedroom_yeelight 7 | # sensor_type: deconz 8 | # after_sundown: True 9 | # turn_on_constraint_entities_off: input_boolean.sleepmode 10 | 11 | # studyroomMotionTrigger: 12 | # module: motionTrigger 13 | # class: MotionTrigger 14 | # app_switch: input_boolean.studyroom_motion_trigger 15 | # sensor: binary_sensor.presence_studyroom 16 | # entity_on: light.philips_miio_light_bulb 17 | # sensor_type: deconz 18 | # after_sundown: True 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /newWifiDeviceNotify/newWifiDeviceNotify.yaml: -------------------------------------------------------------------------------- 1 | newWifiDeviceNotify: 2 | module: newWifiDeviceNotify 3 | class: DeviceNotify 4 | notify_name: group_notifications 5 | user_id: !secret telegram_user_id 6 | message: "Unbekanntes Gerät entdeckt. Hostname: {}. MAC: {}." 7 | #message: "Unknown device connected. Hostname: {}. MAC: {}" 8 | #fritzbox_url: fritzbox_url 9 | #fritzbox_user: '' 10 | #fritzbox_password: fritzbox_password 11 | #fritzbox_profile_name: 'Unbeschränkt' 12 | #fritzbox_message_allow_access: "Soll ich das Gerät ins Internet lassen?" 13 | #fritzbox_message_allow_access: "Should I let the device access the Internet?" 14 | #fritzbox_message_access_allowed: "Großzügig wie ich bin, habe ich das Gerät ins Internet gelassen" 15 | #fritzbox_message_access_allowed: "I have let the device access the internet. How kind of me!" 16 | #fritzbox_message_access_blocked: "Ich habe das Gerät vor den Schrecken des Internets bewahrt" 17 | #fritzbox_message_access_blocked: "I have saved the device from the dangers of the Internet" 18 | dependencies: 19 | - Notifier -------------------------------------------------------------------------------- /newWifiDeviceNotify/requirements.txt: -------------------------------------------------------------------------------- 1 | fritz_switch_profiles >= 1.0.0 -------------------------------------------------------------------------------- /nextAppointmentLeaveNotifier/nextAppointmentLeaveNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which notifies the user to start to the next appointment 6 | # 7 | # 8 | # Args: 9 | # sensor: sensor to watch. example: sensor.calc_leave_time 10 | # notify_input_boolean: input_boolean determining whether to notify. example: input_boolean.announce_time_to_leave 11 | # notify_name: Who to notify. example: group_notifications 12 | # destination_name_sensor: Sensor which holds the Destination name to use in notification. example: sensor.cal_next_appointment_location 13 | # travel_time_sensor: sensor which holds the travel time. example: sensor.travel_time_next_appointment_location 14 | # message: message to use in notification 15 | # 16 | # Release Notes 17 | # 18 | # Version 1.5: 19 | # Catch None when Home Assistant is still starting 20 | # 21 | # Version 1.4.2: 22 | # Fix notification for location_name "None" 23 | # 24 | # Version 1.4.1: 25 | # Fix google maps url message 26 | # 27 | # Version 1.4: 28 | # Don't include Google Maps Link in Notification for Alexa 29 | # 30 | # Version 1.3: 31 | # Also notify when Notification time is in the past 32 | # 33 | # Version 1.2: 34 | # use Notify App 35 | # 36 | # Version 1.1: 37 | # Using globals, message now directly in own yaml instead of message module 38 | # 39 | # Version 1.0: 40 | # Initial Version 41 | 42 | 43 | class NextAppointmentLeaveNotifier(hass.Hass): 44 | def initialize(self): 45 | 46 | self.listen_state_handle_list = [] 47 | 48 | self.sensor = self.args["sensor"] 49 | self.notify_input_boolean = self.args["notify_input_boolean"] 50 | self.notify_name = self.args["notify_name"] 51 | self.destination_name_sensor = self.args["destination_name_sensor"] 52 | self.travel_time_sensor = self.args["travel_time_sensor"] 53 | self.message = self.args["message"] 54 | self.message_google_link = self.args["message_google_link"] 55 | 56 | self.timer_handle = None 57 | 58 | self.google_source_url = "http://maps.google.com/maps?q=" 59 | 60 | self.notifier = self.get_app("Notifier") 61 | 62 | # Used to check of user got already notified for this event 63 | self.location_of_last_notified_event = "" 64 | self.set_timer_handle() 65 | 66 | self.listen_state_handle_list.append( 67 | self.listen_state(self.state_change, self.sensor) 68 | ) 69 | 70 | def state_change(self, entity, attributes, old, new, kwargs): 71 | try: 72 | self.cancel_timer(self.timer_handle) 73 | self.timer_handle = None 74 | except AttributeError: 75 | # Timer was not set 76 | pass 77 | self.set_timer_handle() 78 | 79 | def set_timer_handle(self): 80 | destination_name = self.get_state(self.destination_name_sensor) 81 | self.log(f"destination_name_sensor: {destination_name}") 82 | if self.get_state(self.sensor) != None: 83 | if destination_name != "unknown" and destination_name != "None": 84 | notification_time = datetime.datetime.strptime( 85 | self.get_state(self.sensor), "%Y-%m-%d %H:%M" 86 | ) 87 | if self.get_state(self.travel_time_sensor) != "unknown": 88 | try: 89 | self.timer_handle = self.run_at( 90 | self.notify_user, notification_time 91 | ) 92 | self.log(f"Will notify at {notification_time}") 93 | except ValueError: 94 | self.log("Notification time is in the past") 95 | self.timer_handle = self.run_at( 96 | self.notify_user, datetime.datetime.now() 97 | ) 98 | 99 | def notify_user(self, *kwargs): 100 | if self.get_state(self.notify_input_boolean) == "on": 101 | location_name = self.get_state(self.destination_name_sensor) 102 | if location_name != "None": 103 | if self.location_of_last_notified_event == location_name: 104 | self.log(f"User already got notified for {location_name}") 105 | else: 106 | google_maps_url = self.google_source_url + location_name.replace( 107 | " ", "+" 108 | ) 109 | self.log("Notify user") 110 | self.notifier.notify( 111 | self.notify_name, 112 | self.message.format( 113 | location_name, self.get_state(self.travel_time_sensor) 114 | ), 115 | ) 116 | self.notifier.notify( 117 | self.notify_name, 118 | self.message_google_link.format(google_maps_url), 119 | useAlexa=False, 120 | ) 121 | self.location_of_last_notified_event = location_name 122 | else: 123 | self.log(f"location_name: {location_name}") 124 | else: 125 | self.log("Notification is turned off") 126 | 127 | def terminate(self): 128 | for listen_state_handle in self.listen_state_handle_list: 129 | self.cancel_listen_state(listen_state_handle) 130 | if self.timer_handle is not None: 131 | self.cancel_timer(self.timer_handle) 132 | -------------------------------------------------------------------------------- /nextAppointmentLeaveNotifier/nextAppointmentLeaveNotifier.yaml: -------------------------------------------------------------------------------- 1 | # nextAppointmentLeaveNotifier: 2 | # module: nextAppointmentLeaveNotifier 3 | # class: NextAppointmentLeaveNotifier 4 | # sensor: sensor.calc_leave_time 5 | # notify_input_boolean: input_boolean.announce_time_to_leave 6 | # notify_name: Kevin 7 | # input_number: input_number.leave_time_offset 8 | # destination_name_sensor: sensor.cal_next_appointment_location 9 | # travel_time_sensor: sensor.travel_time_next_appointment_location 10 | # message: "Es ist Zeit loszufahren nach {}. Du brauchst {} Minuten." 11 | # #message: "It's time to leave to {}. It will take {} minutes." 12 | # message_google_link: " Hier ist ein Google Maps Link: {}" 13 | # #message_google_link: " Here is a Google Maps Link: {}" 14 | # dependencies: 15 | # - Notifier 16 | 17 | # nextAppointmentLeaveNotifierUserTwo: 18 | # module: nextAppointmentLeaveNotifier 19 | # class: NextAppointmentLeaveNotifier 20 | # sensor: sensor.calc_leave_time_user_two 21 | # notify_input_boolean: input_boolean.announce_time_to_leave_user_two 22 | # notify_name: Sina 23 | # input_number: input_number.leave_time_offset_user_two 24 | # destination_name_sensor: sensor.cal_next_appointment_location_user_two 25 | # travel_time_sensor: sensor.travel_time_next_appointment_location_user_two 26 | # message: "Es ist Zeit loszufahren nach {}. Du brauchst {} Minuten." 27 | # #message: "It's time to leave to {}. It will take {} minutes." 28 | # message_google_link: " Hier ist ein Google Maps Link: {}" 29 | # #message_google_link: " Here is a Google Maps Link: {}" 30 | # dependencies: 31 | # - Notifier -------------------------------------------------------------------------------- /notifier/notifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # Centralizes messaging. Among other things, it will determine whether a user is at home and if yes in which room. 6 | # Then Alexa in that room will be used additionally to Telegram 7 | # 8 | # Args: 9 | # app_switch_alexa: mutes alexa. example: 10 | # alexa_tts: name of the notification service. example: alexa_media 11 | # alexa_media_player: media player entity of alexa to use. example: media_player.kevins_echo_dot_oben 12 | # user_location_sensors: sensors showing the location of users 13 | # alexa_to_location_mapping: mapping of which alexa device is used for which room 14 | # 15 | # 16 | # 17 | # Release Notes 18 | # 19 | # Version 1.5: 20 | # Allow multiple alexa_media_player 21 | # 22 | # Version 1.4: 23 | # Use type announce 24 | # 25 | # Version 1.3: 26 | # Use Version 1.2.1 of alexa_media_player 27 | # 28 | # Version 1.2.1: 29 | # Fix: Enqueue alexa messages 30 | # 31 | # Version 1.2: 32 | # Enqueue alexa messages 33 | # 34 | # Version 1.1: 35 | # Remove media_player constraints. If connected via bluetooth alexa can always be heard 36 | # 37 | # Version 1.0: 38 | # Initial Version 39 | 40 | __GROUP_NOTIFICATIONS__ = "group_notifications" 41 | __NOTIFY__ = "notify/" 42 | __WAIT_TIME__ = 5 # seconds 43 | 44 | 45 | class Notifier(hass.Hass): 46 | def initialize(self): 47 | self.timer_handle_list = [] 48 | 49 | self.alexa_tts = self.args["alexa_tts"] 50 | self.alexa_media_player = self.args["alexa_media_player"].split(",") 51 | self.app_switch_alexa = self.args["app_switch_alexa"] 52 | 53 | self.last_alexa_notification_time = None 54 | 55 | def notify(self, notify_name, message, useAlexa=True, useTelegram=True): 56 | if useTelegram: 57 | self.log("Notifying via Telegram") 58 | self.call_service(__NOTIFY__ + notify_name, message=message) 59 | if useAlexa and self.get_state(self.app_switch_alexa) == "on": 60 | self.log("Notifying via Alexa") 61 | # check last message 62 | if self.last_alexa_notification_time is not None and ( 63 | datetime.datetime.now() - self.last_alexa_notification_time 64 | < datetime.timedelta(seconds=__WAIT_TIME__) 65 | ): 66 | self.timer_handle_list.append( 67 | self.run_in(self.notify_callback, __WAIT_TIME__, message=message) 68 | ) 69 | else: 70 | self.run_in(self.notify_callback, 0, message=message) 71 | 72 | def notify_callback(self, kwargs): 73 | self.last_alexa_notification_time = datetime.datetime.now() 74 | self.call_service( 75 | __NOTIFY__ + self.alexa_tts, 76 | data={"type": "announce", "method": "speak"}, 77 | target=self.alexa_media_player, 78 | message=kwargs["message"], 79 | ) 80 | 81 | def getAlexaDeviceForUserLocation(self, notify_name): 82 | if notify_name == __GROUP_NOTIFICATIONS__: 83 | return self.args["alexa_to_location_mapping"]["Wohnzimmer"] 84 | elif notify_name.lower() in self.args["user_location_sensors"]: 85 | location = self.get_state( 86 | self.args["user_location_sensors"][notify_name.lower()] 87 | ) 88 | if location in self.args["alexa_to_location_mapping"]: 89 | return self.args["alexa_to_location_mapping"][location] 90 | else: 91 | return None 92 | else: 93 | self.log("Unknown notify_name: {}".format(notify_name)) 94 | return None 95 | 96 | def terminate(self): 97 | for timer_handle in self.timer_handle_list: 98 | self.cancel_timer(timer_handle) 99 | -------------------------------------------------------------------------------- /notifier/notifier.yaml: -------------------------------------------------------------------------------- 1 | Notifier: 2 | module: notifier 3 | class: Notifier 4 | app_switch_alexa: input_boolean.notifier_alexa 5 | alexa_tts: alexa_media 6 | alexa_media_player: media_player.kevins_echo_dot_oben,media_player.kevin_s_echo_dot_unten,media_player.kevins_echo,media_player.kevins_echo_dot 7 | user_location_sensors: 8 | kevin: sensor.location_user_one 9 | sina: sensor.location_user_two 10 | alexa_to_location_mapping: 11 | Wohnzimmer: media_player.kevins_echo_dot_oben 12 | Küche: media_player.kevins_echo_dot_oben 13 | Balkon Oben: media_player.kevins_echo_dot_oben 14 | Arbeitszimmer: media_player.kevin_s_echo_dot_unten 15 | Schlafzimmer: media_player.kevin_s_echo_dot_unten -------------------------------------------------------------------------------- /notifyOfActionWhenAway/notifyOfActionWhenAway.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App to send notification when a sensor changes state 5 | # 6 | # Args: 7 | # 8 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 9 | # sensor: sensor to monitor. example: sensor.upstairs_smoke 10 | # isHome: input_boolean which shows if someone is home. example: input_boolean.isHome 11 | # isHome_delay: delay to wait for user to come home before notifying. example: 10 12 | # 13 | # Release Notes 14 | # 15 | # Version 1.3.1: 16 | # Use consistent message variable 17 | # 18 | # Version 1.3: 19 | # use Notify App 20 | # 21 | # Version 1.2: 22 | # message now directly in own yaml instead of message module 23 | # 24 | # Version 1.1: 25 | # Added isHome_delay 26 | # 27 | # Version 1.0: 28 | # Initial Version 29 | 30 | 31 | class NotifyOfActionWhenAway(hass.Hass): 32 | def initialize(self): 33 | 34 | self.listen_state_handle_list = [] 35 | self.timer_handle_list = [] 36 | 37 | self.app_switch = self.args["app_switch"] 38 | self.notify_name = self.args["notify_name"] 39 | self.isHome_delay = self.args["isHome_delay"] 40 | self.isHome = self.args["isHome"] 41 | self.message = self.args["message"] 42 | 43 | self.notifier = self.get_app("Notifier") 44 | 45 | for sensor in self.args["sensor"].split(","): 46 | self.listen_state_handle_list.append( 47 | self.listen_state(self.state_change, sensor) 48 | ) 49 | 50 | def state_change(self, entity, attribute, old, new, kwargs): 51 | if self.get_state(self.app_switch) == "on": 52 | if new != "" and new != old: 53 | if self.get_state(self.isHome) == "off": 54 | if ( 55 | entity.startswith("binary_sensor.motion_sensor") 56 | and new == "off" 57 | ): 58 | pass 59 | else: 60 | self.log( 61 | "Waiting {} seconds for someone to come home".format( 62 | self.isHome_delay 63 | ) 64 | ) 65 | self.timer_handle_list.append( 66 | self.run_in( 67 | self.notify_if_no_one_home, 68 | self.isHome_delay, 69 | sensor=entity, 70 | new=new, 71 | ) 72 | ) 73 | 74 | def notify_if_no_one_home(self, kwargs): 75 | if self.get_state(self.isHome) == "off": 76 | self.log( 77 | "{} changed to {}".format( 78 | self.friendly_name(kwargs["sensor"]), kwargs["new"] 79 | ) 80 | ) 81 | self.notifier.notify( 82 | self.notify_name, 83 | self.message.format( 84 | self.friendly_name(kwargs["sensor"]), kwargs["new"] 85 | ), 86 | useAlexa=False, 87 | ) 88 | 89 | def terminate(self): 90 | for listen_state_handle in self.listen_state_handle_list: 91 | self.cancel_listen_state(listen_state_handle) 92 | 93 | for timer_handle in self.timer_handle_list: 94 | self.cancel_timer(timer_handle) 95 | -------------------------------------------------------------------------------- /notifyOfActionWhenAway/notifyOfActionWhenAway.yaml: -------------------------------------------------------------------------------- 1 | notifyOfActionWhenAway: 2 | module: notifyOfActionWhenAway 3 | class: NotifyOfActionWhenAway 4 | app_switch: input_boolean.notify_of_action_when_away 5 | sensor: "binary_sensor.contact_bathroom_window_tilted,binary_sensor.contact_bedroom_door,\ 6 | binary_sensor.contact_bedroom_door_tilted,binary_sensor.contact_door,binary_sensor.contact_guest_window,\ 7 | binary_sensor.contact_kitchen_window,binary_sensor.contact_studyroom_door,\ 8 | binary_sensor.contact_studyroom_door_tilted,binary_sensor.contact_terrace_door,\ 9 | binary_sensor.contact_terrace_door_tilted,binary_sensor.contact_upper_bathroom_window,\ 10 | binary_sensor.presence_stairs,binary_sensor.presence_bathroom,binary_sensor.presence_lobby,\ 11 | binary_sensor.presence_bedroom,binary_sensor.presence_kitchen,binary_sensor.presence_upper_stairs,\ 12 | binary_sensor.contact_badfenster,binary_sensor.contact_upper_bathroom_window_tilted" 13 | isHome: input_boolean.is_home 14 | notify_name: group_notifications 15 | isHome_delay: 20 16 | message: "Alarm: {} ist gewechselt auf {}" 17 | #message: "Alarm: {} changed to {}" 18 | dependencies: 19 | - Notifier -------------------------------------------------------------------------------- /plantWateringNotifier/plantWateringNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which reminds you daily to water your plants if it won't rain 6 | # 7 | # 8 | # Args: 9 | # 10 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 11 | # rain_precip_sensor: sensor which shows rain probability. example: sensor.dark_sky_precip_probability 12 | # rain_precip_intensity_sensor: sensor which shows rain probability. example: sensor.dark_sky_precip_intensity 13 | # precip_type_sensor: sensor which shows precip type. example: sensor.dark_sky_precip 14 | # notify_name: Who to notify. example: group_notifications 15 | # user_id: The user_id of the telegram user to ask whether he knows an unknown face. example: 812391 16 | # reminder_acknowledged_entity: Input Boolean to store the information whether the user acknowledged the notification. 17 | # This prevents new notifications upon HA/Appdaemon restart. 18 | # example: input_boolean.persistence_plantwateringnotifier_reminder_acknowledged 19 | # message: localized message to use in notification 20 | # 21 | # Release Notes 22 | # 23 | # Version 1.5.1: 24 | # Use consistent message variable 25 | # 26 | # Version 1.5: 27 | # use Notify App 28 | # 29 | # Version 1.4: 30 | # message now directly in own yaml instead of message module 31 | # 32 | # Version 1.3: 33 | # Added app_switch 34 | # 35 | # Version 1.2: 36 | # Update original message with information when the reminder was acknowledged 37 | # 38 | # Version 1.1: 39 | # Store reminder acknowledged in an input_boolean to prevent notifications after HA/Appdaemon restarts 40 | # 41 | # Version 1.0: 42 | # Initial Version 43 | 44 | 45 | class PlantWateringNotifier(hass.Hass): 46 | def initialize(self): 47 | 48 | self.timer_handle_list = [] 49 | self.listen_event_handle_list = [] 50 | self.listen_state_handle_list = [] 51 | 52 | self.app_switch = self.args["app_switch"] 53 | self.rain_precip_sensor = self.args["rain_precip_sensor"] 54 | self.rain_precip_intensity_sensor = self.args["rain_precip_intensity_sensor"] 55 | self.precip_type_sensor = self.args["precip_type_sensor"] 56 | self.notify_name = self.args["notify_name"] 57 | self.user_id = self.args["user_id"] 58 | self.reminder_acknowledged_entity = self.args["reminder_acknowledged_entity"] 59 | self.message = self.args["message"] 60 | self.message_not_needed = self.args["message_not_needed"] 61 | self.message_evening = self.args["message_evening"] 62 | 63 | self.intensity_minimum = 2 # mm/h 64 | self.propability_minimum = 90 # % 65 | 66 | self.keyboard_callback = "/plants_watered" 67 | 68 | self.notifier = self.get_app("Notifier") 69 | 70 | self.reminder_acknowledged = self.get_state(self.reminder_acknowledged_entity) 71 | 72 | self.listen_event_handle_list.append( 73 | self.listen_event(self.receive_telegram_callback, "telegram_callback") 74 | ) 75 | 76 | # Remind daily at 08:00 77 | self.timer_handle_list.append( 78 | self.run_daily(self.run_morning_callback, datetime.time(8, 0, 0)) 79 | ) 80 | # Remind daily at 18:00 81 | self.timer_handle_list.append( 82 | self.run_daily(self.run_evening_callback, datetime.time(18, 0, 0)) 83 | ) 84 | 85 | def run_morning_callback(self, kwargs): 86 | """Check if it will rain and if not remind the user to water the plants""" 87 | if self.get_state(self.app_switch) == "on": 88 | precip_propability = self.get_state(self.rain_precip_sensor) 89 | self.log("Rain Propability: {}".format(float(precip_propability))) 90 | precip_intensity = self.get_state(self.rain_precip_intensity_sensor) 91 | self.log("Rain Intensity: {}".format(float(precip_intensity))) 92 | precip_type = self.get_state(self.precip_type_sensor) 93 | self.log("Precip Type: {}".format(precip_type)) 94 | 95 | if ( 96 | precip_propability != None 97 | and precip_propability != "" 98 | and float(precip_propability) < self.propability_minimum 99 | and precip_intensity != None 100 | and precip_intensity != "" 101 | and float(precip_intensity) < self.intensity_minimum 102 | ): 103 | self.turn_off(self.reminder_acknowledged_entity) 104 | self.log("Setting reminder_acknowledged to: {}".format("off")) 105 | self.log("Reminding user") 106 | keyboard = [[("Hab ich gemacht", self.keyboard_callback)]] 107 | self.call_service( 108 | "telegram_bot/send_message", 109 | target=self.user_id, 110 | message=self.message.format(precip_propability), 111 | inline_keyboard=keyboard, 112 | ) 113 | 114 | else: 115 | self.turn_on(self.reminder_acknowledged_entity) 116 | self.log("Setting reminder_acknowledged to: {}".format("off")) 117 | self.log("Notifying user") 118 | self.notifier.notify( 119 | self.notify_name, 120 | self.message_not_needed.format( 121 | precip_propability, precip_intensity 122 | ), 123 | ) 124 | 125 | def run_evening_callback(self, kwargs): 126 | """Remind user to water the plants he if didn't acknowledge it""" 127 | if self.get_state(self.app_switch) == "on": 128 | if self.get_state(self.reminder_acknowledged_entity) == "off": 129 | self.log("Reminding user") 130 | self.call_service( 131 | "notify/" + self.notify_name, message=self.message_evening 132 | ) 133 | 134 | def receive_telegram_callback(self, event_name, data, kwargs): 135 | """Event listener for Telegram callback queries.""" 136 | assert event_name == "telegram_callback" 137 | data_callback = data["data"] 138 | callback_id = data["id"] 139 | chat_id = data["chat_id"] 140 | message_id = data["message"]["message_id"] 141 | text = data["message"]["text"] 142 | self.log("callback data: {}".format(data), level="DEBUG") 143 | 144 | if data_callback == self.keyboard_callback: # Keyboard editor: 145 | # Answer callback query 146 | self.call_service( 147 | "telegram_bot/answer_callback_query", 148 | message="Super!", 149 | callback_query_id=callback_id, 150 | ) 151 | self.turn_on(self.reminder_acknowledged_entity) 152 | self.log("Setting reminder_acknowledged to: {}".format("on")) 153 | 154 | self.call_service( 155 | "telegram_bot/edit_message", 156 | chat_id=chat_id, 157 | message_id=message_id, 158 | message=text 159 | + " Hast du um {}:{} erledigt.".format( 160 | datetime.datetime.now().hour, datetime.datetime.now().minute 161 | ), 162 | inline_keyboard=[], 163 | ) 164 | 165 | def terminate(self): 166 | for timer_handle in self.timer_handle_list: 167 | self.cancel_timer(timer_handle) 168 | 169 | for listen_event_handle in self.listen_event_handle_list: 170 | self.cancel_listen_event(listen_event_handle) 171 | 172 | for listen_state_handle in self.listen_state_handle_list: 173 | self.cancel_listen_state(listen_state_handle) 174 | -------------------------------------------------------------------------------- /plantWateringNotifier/plantWateringNotifier.yaml: -------------------------------------------------------------------------------- 1 | # plantWateringNotifier: 2 | # module: plantWateringNotifier 3 | # class: PlantWateringNotifier 4 | # app_switch: input_boolean.plant_watering_notifier 5 | # rain_precip_sensor: sensor.dark_sky_precip_probability 6 | # rain_precip_intensity_sensor: sensor.dark_sky_precip_intensity 7 | # precip_type_sensor: sensor.dark_sky_precip 8 | # notify_name: group_notifications 9 | # user_id: !secret telegram_user_id 10 | # reminder_acknowledged_entity: input_boolean.persistence_plantwateringnotifier_reminder_acknowledged 11 | # message: "Die Regenwahrscheinlichkeit beträgt heute nur {}. Vergiss nicht die Pflanzen zu gießen!" 12 | # #message: "The Rain Propability is only {}. Don't forget to water the plants!" 13 | # message_not_needed: "Es wird heute mit einer Wahrscheinlichkeit von {} Prozent ungefähr {} Millimeter pro Stunde regnen. Du brauchst nicht selbst gießen." 14 | # #message_not_needed: "It will rain today {} millimeter per hour with a propability of {}. You don't have to water your plants" 15 | # message_evening: "Ich bin mir nicht sicher ob du vergessen hast die Pflanzen zu gießen, deswegen erinnere ich dich lieber noch einmal daran." 16 | # #message_evening: "I'm not sure whether you waterd your plants, so I thought I better remind you again" 17 | # dependencies: 18 | # - Notifier -------------------------------------------------------------------------------- /pollenNotifier/pollenNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which notifies you when there is a pollen forecast for today 6 | # Used with sensors getting data from https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json 7 | # 8 | # 9 | # Args: 10 | # 11 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 12 | # pollen_sensor: sensor which shows pollen for today. example: sensor.pollen_101_roggen_today 13 | # pollen_name: Name of the allergen. example: Roggen 14 | # notify_name: Who to notify. example: group_notifications 15 | # notify_time: When to notify. example: 08:00 16 | # notify_threshold: Minimum level of pollen needed to notify. example: 1.0 17 | # message: localized message to use in notification 18 | # 19 | # Release Notes 20 | # 21 | # Version 1.3.1: 22 | # Use consistent message variable 23 | # 24 | # Version 1.3: 25 | # use Notify App 26 | # 27 | # Version 1.2: 28 | # message now directly in own yaml instead of message module 29 | # 30 | # Version 1.1: 31 | # Added notify_threshold 32 | # 33 | # Version 1.0: 34 | # Initial Version 35 | 36 | 37 | class PollenNotifier(hass.Hass): 38 | def initialize(self): 39 | 40 | self.timer_handle_list = [] 41 | self.listen_event_handle_list = [] 42 | self.listen_state_handle_list = [] 43 | 44 | self.app_switch = self.args["app_switch"] 45 | self.pollen_sensor = self.args["pollen_sensor"] 46 | self.pollen_name = self.args["pollen_name"] 47 | self.notify_name = self.args["notify_name"] 48 | self.notify_time = self.args["notify_time"] 49 | self.notify_threshold = self.args["notify_threshold"] 50 | self.message = self.args["message"] 51 | self.message_no_data = self.args["message_no_data"] 52 | 53 | self.mappingsdict = {} 54 | self.mappingsdict["-1"] = "keine Daten" 55 | self.mappingsdict["0"] = "Keine" 56 | self.mappingsdict["0-1"] = "Keine bis Geringe" 57 | self.mappingsdict["1"] = "Geringe" 58 | self.mappingsdict["1-2"] = "Geringe bis Mittlere" 59 | self.mappingsdict["2"] = "Mittlere" 60 | self.mappingsdict["2-3"] = "Mittlere bis Hohe" 61 | self.mappingsdict["3"] = "Hohe" 62 | 63 | self.level_mapping_dict = {} 64 | self.level_mapping_dict["-1"] = -1.0 65 | self.level_mapping_dict["0"] = 0.0 66 | self.level_mapping_dict["0-1"] = 0.5 67 | self.level_mapping_dict["1"] = 1.0 68 | self.level_mapping_dict["1-2"] = 1.5 69 | self.level_mapping_dict["2"] = 2.0 70 | self.level_mapping_dict["2-3"] = 2.5 71 | self.level_mapping_dict["3"] = 3 72 | 73 | self.notifier = self.get_app("Notifier") 74 | 75 | hours = self.notify_time.split(":", 1)[0] 76 | minutes = self.notify_time.split(":", 1)[1] 77 | self.timer_handle_list.append( 78 | self.run_daily( 79 | self.run_daily_callback, datetime.time(int(hours), int(minutes), 0) 80 | ) 81 | ) 82 | 83 | def run_daily_callback(self, kwargs): 84 | """Check if there is an pollen forcast and notify the user about it""" 85 | if self.get_state(self.app_switch) == "on": 86 | pollen_sensor_state = self.get_state(self.pollen_sensor) 87 | self.log( 88 | "{} Belastung Heute: {}".format(self.pollen_name, pollen_sensor_state) 89 | ) 90 | 91 | if pollen_sensor_state == "-1": 92 | message = self.message_no_data.format("Heute", self.pollen_name) 93 | elif pollen_sensor_state == "0": 94 | message = ( 95 | self.message.format( 96 | "Heute", 97 | self.mappingsdict[pollen_sensor_state], 98 | self.pollen_name, 99 | ) 100 | + " Genieß den Tag!" 101 | ) 102 | else: 103 | message = self.message.format( 104 | "Heute", self.mappingsdict[pollen_sensor_state], self.pollen_name 105 | ) 106 | 107 | if self.level_mapping_dict[pollen_sensor_state] >= float( 108 | self.notify_threshold 109 | ): 110 | self.log("Notifying user") 111 | self.notifier.notify(self.notify_name, message) 112 | else: 113 | self.log("Threshold not met. Not notifying user") 114 | 115 | def terminate(self): 116 | for timer_handle in self.timer_handle_list: 117 | self.cancel_timer(timer_handle) 118 | 119 | for listen_event_handle in self.listen_event_handle_list: 120 | self.cancel_listen_event(listen_event_handle) 121 | 122 | for listen_state_handle in self.listen_state_handle_list: 123 | self.cancel_listen_state(listen_state_handle) 124 | -------------------------------------------------------------------------------- /pollenNotifier/pollenNotifier.yaml: -------------------------------------------------------------------------------- 1 | roggenNotifier: 2 | module: pollenNotifier 3 | class: PollenNotifier 4 | app_switch: input_boolean.roggen_notifier 5 | pollen_sensor: sensor.pollen_101_roggen_today 6 | pollen_name: Roggen 7 | notify_name: group_notifications 8 | notify_time: 08:00 9 | notify_threshold: 1.0 10 | message: "{} ist {} {} Belastung." 11 | #message: "The {} intensity {} is {}." 12 | message_no_data: "Ich habe {} leider keine Daten für {}." 13 | #message_no_data: "{} I have no pollen data for {}." 14 | dependencies: 15 | - Notifier 16 | 17 | graeserNotifier: 18 | module: pollenNotifier 19 | class: PollenNotifier 20 | app_switch: input_boolean.graeser_notifier 21 | pollen_sensor: sensor.pollen_101_graeser_today 22 | pollen_name: Gräser 23 | notify_name: group_notifications 24 | notify_time: 08:00 25 | notify_threshold: 1.0 26 | message: "{} ist {} {} Belastung." 27 | #message: "The {} intensity {} is {}." 28 | message_no_data: "Ich habe {} leider keine Daten für {}." 29 | #message_no_data: "{} I have no pollen data for {}." 30 | dependencies: 31 | - Notifier 32 | 33 | birkeNotifier: 34 | module: pollenNotifier 35 | class: PollenNotifier 36 | app_switch: input_boolean.birke_notifier 37 | pollen_sensor: sensor.pollen_101_birke_today 38 | pollen_name: Birke 39 | notify_name: group_notifications 40 | notify_time: 08:00 41 | notify_threshold: 1.0 42 | message: "{} ist {} {} Belastung." 43 | #message: "The {} intensity {} is {}." 44 | message_no_data: "Ich habe {} leider keine Daten für {}." 45 | #message_no_data: "{} I have no pollen data for {}." 46 | dependencies: 47 | - Notifier -------------------------------------------------------------------------------- /powerUsageNotification/powerUsageNotification.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App which notifies you when a power usage sensor indicated a device is on/off 5 | # 6 | # 7 | # Args: 8 | # 9 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 10 | # sensor: power sensor. example: sensor.dishwasher_power_usage 11 | # input_boolean (optional): input_boolean to set to on/off 12 | # notify_name: Who to notify. example: group_notifications 13 | # notify_start (optional): Notify if start was detected: example True (default: True) 14 | # notify_start_use_alexa (optional): Notify with alexa if start was detected: example True (default: True) 15 | # notify_end (optional): Notify if end was detected: example True (default: True) 16 | # notify_end_use_alexa (optional): Notify with alexa if end was detected: example True (default: True) 17 | # delay: seconds to wait until a the device is considered "off". example: 60 18 | # threshold: amount of "usage" which indicated the device is on. example: 2 19 | # alternative_name: Name to use in notification. example: Waschmaschine 20 | # message: Message to use when notifying device is on 21 | # message_off: Message to use when notifying device is off 22 | # 23 | # Release Notes 24 | # 25 | # Version 1.5: 26 | # Catch unknown 27 | # 28 | # Version 1.4: 29 | # Added notify_start, notify_start_use_alexa, notify_end, notify_end_use_alexa, input_boolean 30 | # 31 | # Version 1.3: 32 | # use Notify App 33 | # 34 | # Version 1.2: 35 | # message now directly in own yaml instead of message module 36 | # 37 | # Version 1.1: 38 | # Added app_switch 39 | # 40 | # Version 1.0: 41 | # Initial Version 42 | 43 | 44 | class PowerUsageNotification(hass.Hass): 45 | def initialize(self): 46 | 47 | self.timer_handle_list = [] 48 | self.listen_event_handle_list = [] 49 | self.listen_state_handle_list = [] 50 | 51 | self.app_switch = self.args["app_switch"] 52 | self.input_boolean = self.args.get("input_boolean") 53 | self.sensor = self.args["sensor"] 54 | self.alternative_name = self.args["alternative_name"] 55 | self.notify_name = self.args["notify_name"] 56 | try: 57 | self.notify_start = self.args["notify_start"] 58 | except KeyError: 59 | self.notify_start = True 60 | try: 61 | self.notify_start_use_alexa = self.args["notify_start_use_alexa"] 62 | except KeyError: 63 | self.notify_start_use_alexa = True 64 | try: 65 | self.notify_end = self.args["notify_end"] 66 | except KeyError: 67 | self.notify_end = True 68 | try: 69 | self.notify_end_use_alexa = self.args["notify_end_use_alexa"] 70 | except KeyError: 71 | self.notify_end_use_alexa = True 72 | self.delay = self.args["delay"] 73 | self.threshold = self.args["threshold"] 74 | self.message = self.args["message"] 75 | self.message_off = self.args["message_off"] 76 | 77 | self.triggered = False 78 | self.isWaitingHandle = None 79 | 80 | self.notifier = self.get_app("Notifier") 81 | 82 | # Subscribe to sensors 83 | self.listen_state_handle_list.append( 84 | self.listen_state(self.state_change, self.sensor) 85 | ) 86 | 87 | def state_change(self, entity, attribute, old, new, kwargs): 88 | if self.get_state(self.app_switch) == "on": 89 | # Initial: power usage goes up 90 | if ( 91 | new != None 92 | and new != "" 93 | and new != "unknown" 94 | and not self.triggered 95 | and float(new) > self.threshold 96 | ): 97 | self.triggered = True 98 | self.log("Power Usage is: {}".format(float(new))) 99 | self.log("Setting triggered to: {}".format(self.triggered)) 100 | if self.input_boolean is not None: 101 | self.turn_on(self.input_boolean) 102 | if self.notify_start: 103 | self.notifier.notify( 104 | self.notify_name, 105 | self.message.format(self.alternative_name), 106 | useAlexa=self.notify_start_use_alexa, 107 | ) 108 | else: 109 | self.log("Not notifying user") 110 | # Power usage goes down below threshold 111 | elif ( 112 | new != None 113 | and new != "" 114 | and self.triggered 115 | and self.isWaitingHandle == None 116 | and float(new) <= self.threshold 117 | ): 118 | self.log("Waiting: {} seconds to notify.".format(self.delay)) 119 | self.isWaitingHandle = self.run_in(self.notify_device_off, self.delay) 120 | self.log("Setting isWaitingHandle to: {}".format(self.isWaitingHandle)) 121 | self.timer_handle_list.append(self.isWaitingHandle) 122 | # Power usage goes up before delay 123 | elif ( 124 | new != None 125 | and new != "" 126 | and self.triggered 127 | and self.isWaitingHandle != None 128 | and float(new) > self.threshold 129 | ): 130 | self.log("Cancelling timer") 131 | self.cancel_timer(self.isWaitingHandle) 132 | self.isWaitingHandle = None 133 | self.log("Setting isWaitingHandle to: {}".format(self.isWaitingHandle)) 134 | 135 | def notify_device_off(self, kwargs): 136 | """Notify User that device is off. This may get cancelled if it turns on again in the meantime""" 137 | self.triggered = False 138 | self.log("Setting triggered to: {}".format(self.triggered)) 139 | self.isWaitingHandle = None 140 | self.log("Setting isWaitingHandle to: {}".format(self.isWaitingHandle)) 141 | if self.input_boolean is not None: 142 | self.turn_off(self.input_boolean) 143 | if self.notify_end: 144 | self.notifier.notify( 145 | self.notify_name, 146 | self.message_off.format(self.alternative_name), 147 | useAlexa=self.notify_end_use_alexa, 148 | ) 149 | else: 150 | self.log("Not notifying user") 151 | 152 | def terminate(self): 153 | for timer_handle in self.timer_handle_list: 154 | self.cancel_timer(timer_handle) 155 | 156 | for listen_event_handle in self.listen_event_handle_list: 157 | self.cancel_listen_event(listen_event_handle) 158 | 159 | for listen_state_handle in self.listen_state_handle_list: 160 | self.cancel_listen_state(listen_state_handle) 161 | -------------------------------------------------------------------------------- /powerUsageNotification/powerUsageNotification.yaml: -------------------------------------------------------------------------------- 1 | powerUsageNotification_Dishwasher: 2 | module: powerUsageNotification 3 | class: PowerUsageNotification 4 | app_switch: input_boolean.power_usage_notification_dishwasher 5 | input_boolean: input_boolean.dishwasher 6 | sensor: sensor.dishwasher_power_usage 7 | notify_name: group_notifications 8 | notify_start: False 9 | delay: 1260 #21 minutes 10 | threshold: 2 11 | alternative_name: Die Spülmaschine 12 | message: "{} ist gestartet." 13 | #message: "{} just started." 14 | message_off: "{} ist fertig." 15 | #message_off: "{} just finished." 16 | dependencies: 17 | - Notifier 18 | 19 | powerUsageNotification_Washingmachine: 20 | module: powerUsageNotification 21 | class: PowerUsageNotification 22 | app_switch: input_boolean.power_usage_notification_washingmachine 23 | input_boolean: input_boolean.washingmachine 24 | sensor: sensor.washingmachine_power_usage 25 | notify_name: group_notifications 26 | notify_start: False 27 | delay: 60 28 | threshold: 2 29 | alternative_name: Die Waschmaschine 30 | message: "{} ist gestartet." 31 | #message: "{} just started." 32 | message_off: "{} ist fertig." 33 | #message_off: "{} just finished." 34 | dependencies: 35 | - Notifier -------------------------------------------------------------------------------- /reminder/reminder.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | import uuid 4 | 5 | # 6 | # App which reminds you daily and again in the evening if not acknowledged 7 | # 8 | # 9 | # Args: 10 | # 11 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 12 | # notify_name: Who to notify. example: group_notifications 13 | # user_id: The user_id of the telegram user to ask whether he knows an unknown face. example: 812391 14 | # reminder_acknowledged_entity: Input Boolean to store the information whether the user acknowledged the notification. 15 | # This prevents new notifications upon HA/Appdaemon restart. 16 | # example: input_boolean.persistence_plantwateringnotifier_reminder_acknowledged 17 | # message: localized message to use in notification 18 | # 19 | # Release Notes 20 | # 21 | # Version 1.0: 22 | # Initial Version 23 | 24 | KEYBOARD_CALLBACK_BASE = "/reminder_acknowledged" 25 | 26 | 27 | class Reminder(hass.Hass): 28 | def initialize(self): 29 | 30 | self.timer_handle_list = [] 31 | self.listen_event_handle_list = [] 32 | self.listen_state_handle_list = [] 33 | 34 | self.app_switch = self.args["app_switch"] 35 | self.notify_name = self.args["notify_name"] 36 | self.user_id = self.args["user_id"] 37 | self.reminder_acknowledged_entity = self.args["reminder_acknowledged_entity"] 38 | self.message = self.args["message"] 39 | self.message_evening = self.args["message_evening"] 40 | 41 | self.keyboard_callback = ( 42 | KEYBOARD_CALLBACK_BASE + uuid.uuid4().hex 43 | ) # Unique callback for each instance 44 | 45 | self.notifier = self.get_app("Notifier") 46 | 47 | self.reminder_acknowledged = self.get_state(self.reminder_acknowledged_entity) 48 | 49 | self.listen_event_handle_list.append( 50 | self.listen_event(self.receive_telegram_callback, "telegram_callback") 51 | ) 52 | 53 | # Remind daily at 08:00 54 | self.timer_handle_list.append( 55 | self.run_daily(self.run_morning_callback, datetime.time(8, 0, 0)) 56 | ) 57 | # Remind daily at 18:00 58 | self.timer_handle_list.append( 59 | self.run_daily(self.run_evening_callback, datetime.time(18, 0, 0)) 60 | ) 61 | 62 | def run_morning_callback(self, kwargs): 63 | """Remind the user of {self.message}""" 64 | if self.get_state(self.app_switch) == "on": 65 | 66 | self.turn_off(self.reminder_acknowledged_entity) 67 | self.log("Setting reminder_acknowledged to: off") 68 | self.log("Reminding user") 69 | keyboard = [[("Hab ich gemacht", self.keyboard_callback)]] 70 | self.call_service( 71 | "telegram_bot/send_message", 72 | target=self.user_id, 73 | message=self.message, 74 | inline_keyboard=keyboard, 75 | ) 76 | 77 | def run_evening_callback(self, kwargs): 78 | """Remind the user again if he didn't acknowledge it""" 79 | if self.get_state(self.app_switch) == "on": 80 | if self.get_state(self.reminder_acknowledged_entity) == "off": 81 | self.log("Reminding user") 82 | self.call_service( 83 | "notify/" + self.notify_name, message=self.message_evening 84 | ) 85 | 86 | def receive_telegram_callback(self, event_name, data, kwargs): 87 | """Event listener for Telegram callback queries.""" 88 | assert event_name == "telegram_callback" 89 | data_callback = data["data"] 90 | callback_id = data["id"] 91 | chat_id = data["chat_id"] 92 | message_id = data["message"]["message_id"] 93 | text = data["message"]["text"] 94 | self.log(f"callback data: {data}", level="DEBUG") 95 | 96 | if data_callback == self.keyboard_callback: # Keyboard editor: 97 | # Answer callback query 98 | self.call_service( 99 | "telegram_bot/answer_callback_query", 100 | message="Super!", 101 | callback_query_id=callback_id, 102 | ) 103 | self.turn_on(self.reminder_acknowledged_entity) 104 | self.log("Setting reminder_acknowledged to: on") 105 | 106 | self.call_service( 107 | "telegram_bot/edit_message", 108 | chat_id=chat_id, 109 | message_id=message_id, 110 | message=text 111 | + f" Hast du um {datetime.datetime.now().hour}:{datetime.datetime.now().minute} erledigt.", 112 | inline_keyboard=[], 113 | ) 114 | 115 | def terminate(self): 116 | for timer_handle in self.timer_handle_list: 117 | self.cancel_timer(timer_handle) 118 | 119 | for listen_event_handle in self.listen_event_handle_list: 120 | self.cancel_listen_event(listen_event_handle) 121 | 122 | for listen_state_handle in self.listen_state_handle_list: 123 | self.cancel_listen_state(listen_state_handle) 124 | -------------------------------------------------------------------------------- /reminder/reminder.yaml: -------------------------------------------------------------------------------- 1 | # reminderPlantwateringNew: 2 | # module: reminder 3 | # class: Reminder 4 | # app_switch: input_boolean.plant_watering_reminder_new 5 | # notify_name: group_notifications 6 | # user_id: !secret telegram_user_id 7 | # reminder_acknowledged_entity: input_boolean.persistence_reminder_plantwatering_new_reminder_acknowledged 8 | # message: "Vergiss nicht die neuen Pflanzen zu gießen!" 9 | # #message: "Don't forget to water the plants!" 10 | # message_evening: "Ich bin mir nicht sicher ob du vergessen hast die neuen Pflanzen zu gießen, deswegen erinnere ich dich lieber noch einmal daran." 11 | # #message_evening: "I'm not sure whether you watered your plants, so I thought I better remind you again" 12 | # dependencies: 13 | # - Notifier -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wheel -------------------------------------------------------------------------------- /seqSink/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.24.0 -------------------------------------------------------------------------------- /seqSink/seqSink.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import socket 3 | import os 4 | import json 5 | import requests 6 | 7 | # 8 | # App which forwards all logs to seq. 9 | # 10 | # Args: 11 | # 12 | # server_url: Server url of the seq instance to log to. example: "http://seq:5341/" 13 | # api_key (optional): Api key to use. 14 | # 15 | # Release Notes 16 | # 17 | # Version 1.0: 18 | # Initial Version 19 | 20 | 21 | class SeqSink(hass.Hass): 22 | def initialize(self): 23 | self.server_url = self.args["server_url"] 24 | if not self.server_url.endswith("/"): 25 | self.server_url += "/" 26 | self.server_url += "api/events/raw" 27 | 28 | self.session = requests.Session() 29 | self.session.headers["Content-Type"] = "application/json" 30 | 31 | api_key = self.args.get("api_key") 32 | if api_key: 33 | self.session.headers["X-Seq-ApiKey"] = api_key 34 | 35 | self.handle = self.listen_log(self.log_message_callback) 36 | 37 | def log_message_callback(self, app_name, ts, level, log_type, message, kwargs): 38 | if app_name != "seqSink": 39 | event_data = { 40 | "Timestamp": str(ts), 41 | "Level": str(level), 42 | "MessageTemplate": str(message), 43 | "Properties": { 44 | "Type": "Appdaemon", 45 | "AppName": str(app_name) 46 | }, 47 | } 48 | request_body = {"Events": [event_data]} 49 | 50 | try: 51 | request_body_json = json.dumps(request_body) 52 | except TypeError: 53 | self.log(f"Could not serialize {request_body}") 54 | return 55 | 56 | try: 57 | response = self.session.post( 58 | self.server_url, 59 | data=request_body_json, 60 | stream=True, # prevent '362' 61 | ) 62 | response.raise_for_status() 63 | except requests.RequestException as requestFailed: 64 | self.log(f"Could not serialize {message}") 65 | 66 | if not requestFailed.response: 67 | self.log("Response from Seq was unavailable.") 68 | elif not requestFailed.response.text: 69 | self.log("Response body from Seq was empty.") 70 | else: 71 | self.log(f"Response body from Seq:{requestFailed.response.text}") 72 | 73 | def terminate(self): 74 | self.cancel_listen_log(self.handle) 75 | -------------------------------------------------------------------------------- /seqSink/seqSink.yaml: -------------------------------------------------------------------------------- 1 | # seqSink: 2 | # module: seqSink 3 | # class: SeqSink 4 | # server_url: "http://seq:5341/" 5 | # api_key: !secret seq_api_key -------------------------------------------------------------------------------- /setThermostat/setThermostat.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App which sets a thermostat to a target temperature based on a time from an entity 6 | # 7 | # Args: 8 | # 9 | # app_switch: on/off switch for this app. example: input_boolean.warm_bath_before_wakeup 10 | # isHome: entity which shows if someone is home. example: input_boolean.is_home 11 | # sleepMode: entity which shows if users are sleeping. example: input_boolean.sleepmode 12 | # time_entity: sensor which determines when to run in the format 14:30. example: sensor.alarm_time 13 | # upfront_time: how many minutes before the time_sensor to run. example: 60 14 | # duration: After how many minutes should the thermostat be set back to its previous value. example: 60 15 | # climat_entity: climate entity to set. example: climate.bad_thermostat 16 | # target_entity: the entity holding the target temp. example: warm_bath_before_wakeup 17 | # message: message to use in notification 18 | # notify_name: who to notify. example: group_notifications 19 | # use_alexa: use alexa for notification. example: False 20 | # 21 | # Release Notes 22 | # 23 | # Version 1.5: 24 | # Catch new is unknown 25 | # 26 | # Version 1.4: 27 | # Use sleepmode 28 | # 29 | # Version 1.3: 30 | # Use new formatted alarm_time 31 | # 32 | # Version 1.2.1: 33 | # Reschedule timer after first run 34 | # 35 | # Version 1.2: 36 | # Added isHome. Only run when someone is home 37 | # 38 | # Version 1.1: 39 | # Actually set the previous temp 40 | # 41 | # Version 1.0: 42 | # Initial Version 43 | 44 | 45 | class SetThermostat(hass.Hass): 46 | def initialize(self): 47 | self.timer_handle_list = [] 48 | self.listen_state_handle_list = [] 49 | 50 | self.app_switch = self.args["app_switch"] 51 | self.time_entity = self.args["time_entity"] 52 | self.upfront_time = self.args["upfront_time"] 53 | self.duration = self.args["duration"] 54 | self.climat_entity = self.args["climat_entity"] 55 | self.target_entity = self.args["target_entity"] 56 | self.message = self.args["message"] 57 | self.notify_name = self.args["notify_name"] 58 | self.use_alexa = self.args["use_alexa"] 59 | self.isHome = self.args["isHome"] 60 | self.sleepMode = self.args["sleepMode"] 61 | 62 | self.notifier = self.get_app("Notifier") 63 | 64 | self.run_timer = None 65 | 66 | self.cached_alarm_time = None 67 | 68 | self.listen_state_handle_list.append( 69 | self.listen_state(self.schedule_trigger, self.time_entity) 70 | ) 71 | self.schedule_trigger( 72 | self.time_entity, None, None, self.get_state(self.time_entity), None 73 | ) 74 | 75 | def schedule_trigger(self, entity, attribute, old, new, kwargs): 76 | if ( 77 | new is not None 78 | and new != old 79 | and new != "" 80 | and new != "unknown" 81 | and new != self.cached_alarm_time 82 | ): 83 | if self.run_timer is not None: 84 | self.cancel_timer(self.run_timer) 85 | self.log("Cancelled scheduled trigger") 86 | self.run_timer = None 87 | self.cached_alarm_time = new 88 | event_time = datetime.datetime.strptime(new, "%Y-%m-%d %H:%M:%S") 89 | event_time = event_time - datetime.timedelta(minutes=self.upfront_time) 90 | try: 91 | self.run_timer = self.run_at(self.trigger_thermostat, event_time) 92 | self.timer_handle_list.append(self.run_timer) 93 | self.log("Thermostat will trigger at {}".format(event_time)) 94 | except ValueError: 95 | self.log("New trigger time would be in the past: {}".format(event_time)) 96 | 97 | def trigger_thermostat(self, kwargs): 98 | if ( 99 | self.get_state(self.app_switch) == "on" 100 | and self.get_state(self.isHome) == "on" 101 | and self.get_state(self.sleepMode) == "on" 102 | ): 103 | self.log( 104 | self.message.format( 105 | self.friendly_name(self.climat_entity), 106 | self.get_state(self.target_entity), 107 | ) 108 | ) 109 | self.notifier.notify( 110 | self.notify_name, 111 | self.message.format( 112 | self.friendly_name(self.climat_entity), 113 | self.get_state(self.target_entity), 114 | ), 115 | useAlexa=self.use_alexa, 116 | ) 117 | self.log("Turning {} on".format(self.climat_entity)) 118 | self.call_service("climate/turn_on", entity_id=self.climat_entity) 119 | self.previous_temp = self.get_state(self.climat_entity, attribute="all")[ 120 | "attributes" 121 | ]["temperature"] 122 | self.call_service( 123 | "climate/set_temperature", 124 | entity_id=self.climat_entity, 125 | temperature=self.get_state(self.target_entity), 126 | ) 127 | self.log("Resetting Thermostat in {} minutes.".format(self.duration)) 128 | self.timer_handle_list.append( 129 | self.run_in(self.reset_thermostat, float(self.duration) * 60) 130 | ) 131 | if self.run_timer is not None: 132 | self.cancel_timer(self.run_timer) 133 | 134 | def reset_thermostat(self, kwargs): 135 | if self.previous_temp is not None: 136 | self.log( 137 | self.message.format( 138 | self.friendly_name(self.climat_entity), self.previous_temp 139 | ) 140 | ) 141 | self.notifier.notify( 142 | self.notify_name, 143 | self.message.format( 144 | self.friendly_name(self.climat_entity), self.previous_temp 145 | ), 146 | useAlexa=self.use_alexa, 147 | ) 148 | self.call_service( 149 | "climate/set_temperature", 150 | entity_id=self.climat_entity, 151 | temperature=self.previous_temp, 152 | ) 153 | self.schedule_trigger( 154 | self.time_entity, None, None, self.get_state(self.time_entity), None 155 | ) 156 | 157 | def terminate(self): 158 | for timer_handle in self.timer_handle_list: 159 | self.cancel_timer(timer_handle) 160 | 161 | for listen_state_handle in self.listen_state_handle_list: 162 | self.cancel_listen_state(listen_state_handle) 163 | -------------------------------------------------------------------------------- /setThermostat/setThermostat.yaml: -------------------------------------------------------------------------------- 1 | # warm_bath_before_wakeup: 2 | # module: setThermostat 3 | # class: SetThermostat 4 | # app_switch: input_boolean.warm_bath_before_wakeup 5 | # isHome: input_boolean.is_home 6 | # sleepMode: input_boolean.sleepmode 7 | # time_entity: sensor.alarm_time 8 | # upfront_time: 120 9 | # duration: 120 10 | # climat_entity: climate.bad_thermostat 11 | # target_entity: input_number.warm_bath_before_wakeup 12 | # message: "Ich habe {} auf {} °C gestellt" 13 | # #message: "I have set {} to {}" 14 | # notify_name: group_notifications 15 | # use_alexa: False 16 | # dependencies: 17 | # - Notifier 18 | 19 | # warm_upper_bath_before_wakeup: 20 | # module: setThermostat 21 | # class: SetThermostat 22 | # app_switch: input_boolean.warm_upper_bath_before_wakeup 23 | # isHome: input_boolean.is_home 24 | # sleepMode: input_boolean.sleepmode 25 | # time_entity: sensor.alarm_time 26 | # upfront_time: 60 27 | # duration: 60 28 | # climat_entity: climate.bad_oben_thermostat 29 | # target_entity: input_number.warm_upper_bath_before_wakeup 30 | # message: "Ich habe {} auf {} °C gestellt" 31 | # #message: "I have set {} to {}" 32 | # notify_name: group_notifications 33 | # use_alexa: False 34 | # dependencies: 35 | # - Notifier 36 | 37 | -------------------------------------------------------------------------------- /setThermostatOnStateChange/setThermostatOnStateChange.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | # 4 | # App which sets a thermostat to a target temperature on state change 5 | # 6 | # Args: 7 | # 8 | # app_switch: on/off switch for this app. example: input_boolean.warm_bath_before_wakeup 9 | # trigger_entity: entity which triggers this app. example: input_boolean.is_home 10 | # trigger_state: new state of trigger_entity which triggers this app. example: "off" 11 | # climate_entity: climate entity to set. example: climate.bad_thermostat 12 | # target_entity: the entity holding the target temp. example: warm_bath_before_wakeup 13 | # message (optional): message to use in notification 14 | # notify_name (optional): who to notify. example: group_notifications 15 | # use_alexa (optional): use alexa for notification. example: False 16 | # 17 | # Release Notes 18 | # 19 | # Version 1.2: 20 | # Rename of SetThermostatOnStateChange 21 | # 22 | # Version 1.1: 23 | # Use isHome as trigger 24 | # 25 | # Version 1.0: 26 | # Initial Version 27 | 28 | 29 | class SetThermostatOnStateChange(hass.Hass): 30 | def initialize(self): 31 | self.timer_handle_list = [] 32 | self.listen_state_handle_list = [] 33 | 34 | self.app_switch = self.args["app_switch"] 35 | self.trigger_entity = self.args["trigger_entity"] 36 | self.trigger_state = self.args["trigger_state"] 37 | self.climate_entity = self.args["climate_entity"] 38 | self.target_entity = self.args["target_entity"] 39 | self.message = self.args.get("message") 40 | self.notify_name = self.args.get("notify_name") 41 | try: 42 | self.use_alexa = self.args["use_alexa"] 43 | except KeyError: 44 | self.use_alexa = False 45 | 46 | self.notifier = self.get_app("Notifier") 47 | 48 | self.listen_state_handle_list.append( 49 | self.listen_state(self.state_change, self.trigger_entity) 50 | ) 51 | 52 | def state_change(self, entity, attribute, old, new, kwargs): 53 | if self.get_state(self.app_switch) == "on": 54 | if new != "" and new == self.trigger_state and old != new: 55 | if self.message is not None: 56 | self.log( 57 | self.message.format( 58 | self.friendly_name(self.climate_entity), 59 | self.get_state(self.target_entity), 60 | ) 61 | ) 62 | self.call_service("climate/turn_on", entity_id=self.climate_entity) 63 | self.call_service( 64 | "climate/set_temperature", 65 | entity_id=self.climate_entity, 66 | temperature=self.get_state(self.target_entity), 67 | ) 68 | if self.notify_name is not None: 69 | self.notifier.notify( 70 | self.notify_name, 71 | self.message.format( 72 | self.friendly_name(self.climate_entity), 73 | self.get_state(self.target_entity), 74 | ), 75 | useAlexa=self.use_alexa, 76 | ) 77 | 78 | def terminate(self): 79 | for timer_handle in self.timer_handle_list: 80 | self.cancel_timer(timer_handle) 81 | 82 | for listen_state_handle in self.listen_state_handle_list: 83 | self.cancel_listen_state(listen_state_handle) 84 | -------------------------------------------------------------------------------- /setThermostatOnStateChange/setThermostatOnStateChange.yaml: -------------------------------------------------------------------------------- 1 | # setWohnzimmerThermostatWhenLeaving: 2 | # module: setThermostatOnStateChange 3 | # class: SetThermostatOnStateChange 4 | # app_switch: input_boolean.set_livingroom_thermostat_when_leaving 5 | # trigger_entity: input_boolean.is_home 6 | # trigger_state: "off" 7 | # climate_entity: climate.wohnzimmer_thermostat 8 | # target_entity: input_number.set_livingroom_thermostat_when_leaving 9 | # message: "Ich habe {} auf {} °C gestellt" 10 | # #message: "I have set {} to {}" 11 | # dependencies: 12 | # - Notifier 13 | 14 | # setBadObenThermostatWhenLeaving: 15 | # module: setThermostatOnStateChange 16 | # class: SetThermostatOnStateChange 17 | # app_switch: input_boolean.set_upper_bath_thermostat_when_leaving 18 | # trigger_entity: input_boolean.is_home 19 | # trigger_state: "off" 20 | # climate_entity: climate.bad_oben_thermostat 21 | # target_entity: input_number.set_upper_bath_thermostat_when_leaving 22 | # message: "Ich habe {} auf {} °C gestellt" 23 | # #message: "I have set {} to {}" 24 | # dependencies: 25 | # - Notifier 26 | 27 | # setBadThermostatWhenLeaving: 28 | # module: setThermostatOnStateChange 29 | # class: SetThermostatOnStateChange 30 | # app_switch: input_boolean.set_bath_thermostat_when_leaving 31 | # trigger_entity: input_boolean.is_home 32 | # trigger_state: "off" 33 | # climate_entity: climate.bad_thermostat 34 | # target_entity: input_number.set_bath_thermostat_when_leaving 35 | # message: "Ich habe {} auf {} °C gestellt" 36 | # #message: "I have set {} to {}" 37 | # dependencies: 38 | # - Notifier 39 | 40 | # setKitchenThermostatWhenLeaving: 41 | # module: setThermostatOnStateChange 42 | # class: SetThermostatOnStateChange 43 | # app_switch: input_boolean.set_kitchen_thermostat_when_leaving 44 | # trigger_entity: input_boolean.is_home 45 | # trigger_state: "off" 46 | # climate_entity: climate.kuche_thermostat 47 | # target_entity: input_number.set_kitchen_thermostat_when_leaving 48 | # message: "Ich habe {} auf {} °C gestellt" 49 | # #message: "I have set {} to {}" 50 | # dependencies: 51 | # - Notifier 52 | 53 | # setWohnzimmerThermostatWhenSleeping: 54 | # module: setThermostatOnStateChange 55 | # class: SetThermostatOnStateChange 56 | # app_switch: input_boolean.set_livingroom_thermostat_when_sleeping 57 | # trigger_entity: input_boolean.sleepmode 58 | # trigger_state: "on" 59 | # climate_entity: climate.wohnzimmer_thermostat 60 | # target_entity: input_number.set_livingroom_thermostat_when_sleeping 61 | # message: "Ich habe {} auf {} °C gestellt" 62 | # #message: "I have set {} to {}" 63 | # dependencies: 64 | # - Notifier 65 | 66 | # setBadObenThermostatWhenSleeping: 67 | # module: setThermostatOnStateChange 68 | # class: SetThermostatOnStateChange 69 | # app_switch: input_boolean.set_upper_bath_thermostat_when_sleeping 70 | # trigger_entity: input_boolean.sleepmode 71 | # trigger_state: "on" 72 | # climate_entity: climate.bad_oben_thermostat 73 | # target_entity: input_number.set_upper_bath_thermostat_when_sleeping 74 | # message: "Ich habe {} auf {} °C gestellt" 75 | # #message: "I have set {} to {}" 76 | # dependencies: 77 | # - Notifier 78 | 79 | # setBadThermostatWhenSleeping: 80 | # module: setThermostatOnStateChange 81 | # class: SetThermostatOnStateChange 82 | # app_switch: input_boolean.set_bath_thermostat_when_sleeping 83 | # trigger_entity: input_boolean.sleepmode 84 | # trigger_state: "on" 85 | # climate_entity: climate.bad_thermostat 86 | # target_entity: input_number.set_bath_thermostat_when_sleeping 87 | # message: "Ich habe {} auf {} °C gestellt" 88 | # #message: "I have set {} to {}" 89 | # dependencies: 90 | # - Notifier 91 | 92 | # setKitchenThermostatWhenSleeping: 93 | # module: setThermostatOnStateChange 94 | # class: SetThermostatOnStateChange 95 | # app_switch: input_boolean.set_kitchen_thermostat_when_sleeping 96 | # trigger_entity: input_boolean.sleepmode 97 | # trigger_state: "on" 98 | # climate_entity: climate.kuche_thermostat 99 | # target_entity: input_number.set_kitchen_thermostat_when_sleeping 100 | # message: "Ich habe {} auf {} °C gestellt" 101 | # #message: "I have set {} to {}" 102 | # dependencies: 103 | # - Notifier 104 | 105 | # setLivingroomThermostatWhenWakingUp: 106 | # module: setThermostatOnStateChange 107 | # class: SetThermostatOnStateChange 108 | # app_switch: input_boolean.set_livingroom_thermostat_when_waking_up 109 | # trigger_entity: input_boolean.sleepmode 110 | # trigger_state: "off" 111 | # climate_entity: climate.wohnzimmer_thermostat 112 | # target_entity: input_number.set_livingroom_thermostat_when_waking_up 113 | # message: "Ich habe {} auf {} °C gestellt" 114 | # #message: "I have set {} to {}" 115 | # notify_name: group_notifications 116 | # use_alexa: False 117 | # dependencies: 118 | # - Notifier 119 | 120 | # setKitchenThermostatWhenWakingUp: 121 | # module: setThermostatOnStateChange 122 | # class: SetThermostatOnStateChange 123 | # app_switch: input_boolean.set_kitchen_thermostat_when_waking_up 124 | # trigger_entity: input_boolean.sleepmode 125 | # trigger_state: "off" 126 | # climate_entity: climate.kuche_thermostat 127 | # target_entity: input_number.set_kitchen_thermostat_when_waking_up 128 | # message: "Ich habe {} auf {} °C gestellt" 129 | # #message: "I have set {} to {}" 130 | # notify_name: group_notifications 131 | # use_alexa: False 132 | # dependencies: 133 | # - Notifier 134 | 135 | # setBadThermostatWhenWakingUp: 136 | # module: setThermostatOnStateChange 137 | # class: SetThermostatOnStateChange 138 | # app_switch: input_boolean.set_bath_thermostat_when_waking_up 139 | # trigger_entity: input_boolean.sleepmode 140 | # trigger_state: "off" 141 | # climate_entity: climate.bad_thermostat 142 | # target_entity: input_number.set_bath_thermostat_when_waking_up 143 | # message: "Ich habe {} auf {} °C gestellt" 144 | # #message: "I have set {} to {}" 145 | # notify_name: group_notifications 146 | # use_alexa: False 147 | # dependencies: 148 | # - Notifier 149 | 150 | # setBadObenThermostatWhenWakingUp: 151 | # module: setThermostatOnStateChange 152 | # class: SetThermostatOnStateChange 153 | # app_switch: input_boolean.set_upper_bath_thermostat_when_waking_up 154 | # trigger_entity: input_boolean.sleepmode 155 | # trigger_state: "off" 156 | # climate_entity: climate.bad_oben_thermostat 157 | # target_entity: input_number.set_upper_bath_thermostat_when_waking_up 158 | # message: "Ich habe {} auf {} °C gestellt" 159 | # #message: "I have set {} to {}" 160 | # notify_name: group_notifications 161 | # use_alexa: False 162 | # dependencies: 163 | # - Notifier 164 | 165 | # setLivingroomThermostatWhenComingHome: 166 | # module: setThermostatOnStateChange 167 | # class: SetThermostatOnStateChange 168 | # app_switch: input_boolean.set_livingroom_thermostat_when_coming_home 169 | # trigger_entity: input_boolean.is_home 170 | # trigger_state: "on" 171 | # climate_entity: climate.wohnzimmer_thermostat 172 | # target_entity: input_number.set_livingroom_thermostat_when_coming_home 173 | # message: "Ich habe {} auf {} °C gestellt" 174 | # #message: "I have set {} to {}" 175 | # notify_name: group_notifications 176 | # use_alexa: False 177 | # dependencies: 178 | # - Notifier 179 | 180 | # setKitchenThermostatWhenComingHome: 181 | # module: setThermostatOnStateChange 182 | # class: SetThermostatOnStateChange 183 | # app_switch: input_boolean.set_kitchen_thermostat_when_coming_home 184 | # trigger_entity: input_boolean.is_home 185 | # trigger_state: "on" 186 | # climate_entity: climate.kuche_thermostat 187 | # target_entity: input_number.set_kitchen_thermostat_when_coming_home 188 | # message: "Ich habe {} auf {} °C gestellt" 189 | # #message: "I have set {} to {}" 190 | # notify_name: group_notifications 191 | # use_alexa: False 192 | # dependencies: 193 | # - Notifier 194 | 195 | # setBadThermostatWhenComingHome: 196 | # module: setThermostatOnStateChange 197 | # class: SetThermostatOnStateChange 198 | # app_switch: input_boolean.set_bath_thermostat_when_coming_home 199 | # trigger_entity: input_boolean.is_home 200 | # trigger_state: "on" 201 | # climate_entity: climate.bad_thermostat 202 | # target_entity: input_number.set_bath_thermostat_when_coming_home 203 | # message: "Ich habe {} auf {} °C gestellt" 204 | # #message: "I have set {} to {}" 205 | # notify_name: group_notifications 206 | # use_alexa: False 207 | # dependencies: 208 | # - Notifier 209 | 210 | # setBadObenThermostatWhenComingHome: 211 | # module: setThermostatOnStateChange 212 | # class: SetThermostatOnStateChange 213 | # app_switch: input_boolean.set_upper_bath_thermostat_when_coming_home 214 | # trigger_entity: input_boolean.is_home 215 | # trigger_state: "on" 216 | # climate_entity: climate.bad_oben_thermostat 217 | # target_entity: input_number.set_upper_bath_thermostat_when_coming_home 218 | # message: "Ich habe {} auf {} °C gestellt" 219 | # #message: "I have set {} to {}" 220 | # notify_name: group_notifications 221 | # use_alexa: False 222 | # dependencies: 223 | # - Notifier -------------------------------------------------------------------------------- /sleepModeHandler/sleepModeHandler.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | 3 | 4 | # 5 | # App which sets the sleep mode on/off 6 | # 7 | # Args: 8 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 9 | # sleepmode: input_boolean holding the sleepmode. example: input_boolean.sleepmode 10 | # users: configuration for users 11 | # 12 | # Release Notes 13 | # 14 | # Version 1.2: 15 | # Add use_alexa 16 | # 17 | # Version 1.1: 18 | # Only send notification if sleepmode is actually changed 19 | # 20 | # Version 1.0: 21 | # Initial Version 22 | 23 | 24 | class SleepModeHandler(hass.Hass): 25 | def initialize(self): 26 | self.listen_state_handle_list = [] 27 | 28 | self.app_switch = self.args["app_switch"] 29 | self.sleepmode = self.args["sleepmode"] 30 | self.users = self.args["users"] 31 | self.notify_name = self.args["notify_name"] 32 | self.message_sleeping = self.args["message_sleeping"] 33 | self.message_awake = self.args["message_awake"] 34 | 35 | try: 36 | self.use_alexa = self.args["use_alexa"] 37 | except KeyError: 38 | self.use_alexa = False 39 | 40 | self.notifier = self.get_app("Notifier") 41 | 42 | for user in self.users: 43 | self.listen_state_handle_list.append( 44 | self.listen_state(self.state_change, user["sleep_mode"]) 45 | ) 46 | 47 | def state_change(self, entity, attribute, old, new, kwargs): 48 | if self.get_state(self.app_switch) == "on": 49 | if new != "" and new != old: 50 | if new == "on": 51 | if self.are_all_that_are_home_sleeping(): 52 | if self.get_state(self.sleepmode) == "off": 53 | self.log("All at home are sleeping") 54 | self.turn_on(self.sleepmode) 55 | self.notifier.notify( 56 | self.notify_name, 57 | self.message_sleeping, 58 | useAlexa=self.use_alexa, 59 | ) 60 | elif new == "off": 61 | if self.are_all_that_are_home_awake(): 62 | if self.get_state(self.sleepmode) == "on": 63 | self.log("All at home are awake") 64 | self.turn_off(self.sleepmode) 65 | self.notifier.notify( 66 | self.notify_name, 67 | self.message_awake, 68 | useAlexa=self.use_alexa, 69 | ) 70 | 71 | def are_all_that_are_home_sleeping(self): 72 | for user in self.users: 73 | if self.get_state(user["isHome"]) == "on": 74 | if self.get_state(user["sleep_mode"]) != "on": 75 | return False 76 | return True 77 | 78 | def are_all_that_are_home_awake(self): 79 | for user in self.users: 80 | if self.get_state(user["isHome"]) == "on": 81 | if self.get_state(user["sleep_mode"]) == "on": 82 | return False 83 | return True 84 | 85 | def terminate(self): 86 | for listen_state_handle in self.listen_state_handle_list: 87 | self.cancel_listen_state(listen_state_handle) 88 | -------------------------------------------------------------------------------- /sleepModeHandler/sleepModeHandler.yaml: -------------------------------------------------------------------------------- 1 | # sleepModeHandler: 2 | # module: sleepModeHandler 3 | # class: SleepModeHandler 4 | # app_switch: input_boolean.sleep_mode_handler 5 | # sleepmode: input_boolean.sleepmode 6 | # notify_name: group_notifications 7 | # message_sleeping: "Alle zu Hause sind im Bett" 8 | # #message_sleeping: "All home are in bed" 9 | # message_awake: "Alle zu Hause sind wach" 10 | # #message_awake: "All home are awake" 11 | # use_alexa: False 12 | # users: 13 | # - sleep_mode: input_boolean.user_one_sleep 14 | # isHome: input_boolean.user_one_home 15 | # - sleep_mode: input_boolean.user_two_sleep 16 | # isHome: input_boolean.user_two_home 17 | # dependencies: 18 | # - Notifier -------------------------------------------------------------------------------- /sleepModeHandler/userSleepModeHandler.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | from queue import Queue 3 | 4 | # 5 | # App which sets the sleep mode on/off 6 | # 7 | # Args: 8 | # app_switch: 9 | # on/off switch for this app. 10 | # example: input_boolean.turn_fan_on_when_hot 11 | # input_boolean: 12 | # input_boolean holding the sleepmode. example: input_boolean.sleepmode 13 | # location_sensor: 14 | # location sensor of user. example: sensor.location_user_one 15 | # room: 16 | # Room name in which user must be. example: Wohnzimmer 17 | # asleep_duration: 18 | # seconds to wait before turning sleepmode on. example: 120 19 | # awake_duration: 20 | # seconds to wait before turning sleepmode off. example: 120 21 | # is_home_input_boolean: 22 | # input_boolean for the is home state of the user 23 | # example: input_boolean.is_user_home_determiner_user_one 24 | # 25 | # Release Notes 26 | # 27 | # Version 1.4: 28 | # Also watch is_home 29 | # 30 | # Version 1.3: 31 | # Reimplementation 32 | # 33 | # Version 1.2: 34 | # Only trigger on an actual change 35 | # 36 | # Version 1.1: 37 | # Added asleep_duration and awake_duration instead of duration 38 | # 39 | # Version 1.0: 40 | # Initial Version 41 | 42 | 43 | class UserSleepModeHandler(hass.Hass): 44 | def initialize(self): 45 | self.listen_state_handle_list = [] 46 | 47 | self.app_switch = self.args["app_switch"] 48 | self.location_sensor = self.args["location_sensor"] 49 | self.room = self.args["room"] 50 | self.asleep_duration = self.args["asleep_duration"] 51 | self.awake_duration = self.args["awake_duration"] 52 | self.input_boolean = self.args["input_boolean"] 53 | self.is_home_input_boolean = self.args["is_home_input_boolean"] 54 | 55 | self.timer_handle = None 56 | 57 | self.queue = Queue() 58 | 59 | self.listen_state_handle_list.append( 60 | self.listen_state(self.state_change, self.location_sensor) 61 | ) 62 | 63 | self.listen_state_handle_list.append( 64 | self.listen_state(self.state_change, self.is_home_input_boolean) 65 | ) 66 | 67 | def home_state_change(self, entity, attribute, old, new, kwargs): 68 | """is_home state changed.""" 69 | if new != old: 70 | if self.get_state(self.app_switch) == "on": 71 | if new == "off": 72 | self.log( 73 | f"User left home. Turning {self.input_boolean} off") 74 | self.turn_off(self.input_boolean) 75 | 76 | def state_change(self, entity, attribute, old, new, kwargs): 77 | """Handle state changes of the location sensor.""" 78 | if new != old: 79 | if self.get_state(self.app_switch) == "on": 80 | # User left room 81 | if old == self.room: 82 | self.log( 83 | f"User left {self.room}. Resetting timer." 84 | f"Will trigger awake in {self.awake_duration}s" 85 | ) 86 | if self.timer_handle is not None: 87 | self.cancel_timer(self.timer_handle) 88 | self.timer_handle = self.run_in( 89 | self.awake, 90 | self.awake_duration) 91 | elif new == self.room: 92 | self.log( 93 | f"User entered {self.room}. " 94 | f"Resetting timer. " 95 | f"Will trigger asleep in {self.asleep_duration}s" 96 | ) 97 | if self.timer_handle is not None: 98 | self.cancel_timer(self.timer_handle) 99 | self.timer_handle = self.run_in( 100 | self.asleep, 101 | self.asleep_duration) 102 | 103 | def awake(self, kwargs): 104 | """User left room for more than self.awake_duration. 105 | Turn off sleep mode.""" 106 | current_location = self.get_state(self.location_sensor) 107 | if current_location != self.room: 108 | if self.get_state(self.input_boolean) == "on": 109 | self.log( 110 | f"{self.friendly_name(self.location_sensor)} " 111 | f"is outside {self.room} " 112 | f"for more than {self.asleep_duration}s. " 113 | f"Turning {self.input_boolean} off" 114 | ) 115 | self.turn_off(self.input_boolean) 116 | else: 117 | self.log(f"Timer ran out but user is in {current_location}") 118 | 119 | def asleep(self, kwargs): 120 | """User stayed in room for more than self.asleep_duration. 121 | Turn on sleep mode.""" 122 | current_location = self.get_state(self.location_sensor) 123 | if current_location == self.room: 124 | if self.get_state(self.input_boolean) == "off": 125 | self.log( 126 | f"{self.friendly_name(self.location_sensor)} " 127 | f"is in {self.room} " 128 | f"for more than {self.asleep_duration}s. " 129 | f"Turning {self.input_boolean} on" 130 | ) 131 | self.turn_on(self.input_boolean) 132 | else: 133 | self.log(f"Timer ran out but user is in {current_location}") 134 | 135 | def insert_room_state_change(self, entity, attribute, old, new, kwargs): 136 | """Insert a new room state change into the queue.""" 137 | self.queue.put(new) 138 | 139 | def calculate_room_presence(self, kwargs): 140 | """Calculate the percentage the person was in the target room 141 | since the last invocation.""" 142 | state_changes = [] 143 | while not self.queue.empty(): 144 | state_changes.append(self.queue.get()) 145 | for state_change in state_changes: 146 | pass 147 | 148 | def terminate(self): 149 | if self.timer_handle is not None: 150 | self.cancel_timer(self.timer_handle) 151 | 152 | for listen_state_handle in self.listen_state_handle_list: 153 | self.cancel_listen_state(listen_state_handle) 154 | -------------------------------------------------------------------------------- /sleepModeHandler/userSleepModeHandler.yaml: -------------------------------------------------------------------------------- 1 | # userSleepModeHandlerUserOne: 2 | # module: userSleepModeHandler 3 | # class: UserSleepModeHandler 4 | # app_switch: input_boolean.user_sleep_mode_handler_user_one 5 | # input_boolean: input_boolean.user_one_sleep 6 | # location_sensor: sensor.location_user_one 7 | # is_home_input_boolean: input_boolean.is_user_home_determiner_user_one 8 | # room: Schlafzimmer 9 | # awake_duration: 600 10 | # asleep_duration: 1800 11 | 12 | # userSleepModeHandlerUserTwo: 13 | # module: userSleepModeHandler 14 | # class: UserSleepModeHandler 15 | # app_switch: input_boolean.user_sleep_mode_handler_user_two 16 | # input_boolean: input_boolean.user_two_sleep 17 | # location_sensor: sensor.location_user_two 18 | # is_home_input_boolean: input_boolean.is_user_home_determiner_user_two 19 | # room: Schlafzimmer 20 | # awake_duration: 600 21 | # asleep_duration: 1800 -------------------------------------------------------------------------------- /travelTimeNotifier/travelTimeNotifier.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | from typing import Optional 4 | 5 | # 6 | # App which notifies the user if the travel time is within a normal amount 7 | # 8 | # 9 | # Args: 10 | # sensor: google_travel_time or here_travel_time sensor to watch. example: sensor.travel_time_home_from_work 11 | # notify_input_boolean: input_boolean determining whether to notify. example: input_boolean.travel_time_home_from_work 12 | # notify_name: Who to notify. example: group_notifications 13 | # acceptable_range (optional): Multiplier of the normal travel time that is still acceptable. example: 1.2 14 | # message_: message to use in notification 15 | # notify_use_Alexa: use Alexa as TTS. Defaults to True. example: False 16 | # 17 | # Release Notes 18 | # 19 | # Version 1.7: 20 | # Catch NoneType when Homeassistant is still starting 21 | # 22 | # Version 1.6: 23 | # Introduce methods to deal with minor differences between google and here 24 | # 25 | # Version 1.5: 26 | # Rename to TravelTimeNotifier as this can be used with here_travel_time also 27 | # 28 | # Version 1.4: 29 | # use Notify App 30 | # 31 | # Version 1.3: 32 | # message now directly in own yaml instead of message module 33 | # 34 | # Version 1.2: 35 | # Moved to standard google travel sensors. Now only notification 36 | # 37 | # Version 1.1: 38 | # Add notification feature 39 | # 40 | # Version 1.0: 41 | # Initial Version 42 | 43 | 44 | class TravelTimeNotifier(hass.Hass): 45 | def initialize(self): 46 | 47 | self.listen_state_handle_list = [] 48 | self.timer_handle_list = [] 49 | 50 | self.sensor = self.args["sensor"] 51 | self.notify_input_boolean = self.args["notify_input_boolean"] 52 | self.notify_name = self.args["notify_name"] 53 | self.message = self.args["message"] 54 | try: 55 | self.acceptable_range = self.args["acceptable_range"] 56 | except KeyError: 57 | self.acceptable_range = 1.2 58 | try: 59 | self.notify_use_Alexa = self.args["notify_use_Alexa"] 60 | except KeyError: 61 | self.notify_use_Alexa = True 62 | 63 | self.notifier = self.get_app("Notifier") 64 | 65 | self.listen_state_handle_list.append( 66 | self.listen_state(self.state_change, self.sensor, attribute="all") 67 | ) 68 | 69 | def state_change(self, entity, attributes, old, new, kwargs) -> None: 70 | self.log("entity: {}".format(entity)) 71 | self.log("old: {}".format(old)) 72 | self.log("new: {}".format(new)) 73 | 74 | duration_in_traffic_minutes = self.parse_duration_in_traffic_minutes(new) 75 | self.log("duration_in_traffic_minutes: {}".format(duration_in_traffic_minutes),) 76 | 77 | duration_minutes = self.parse_duration_minutes(new) 78 | self.log("duration_minutes: {}".format(duration_minutes)) 79 | 80 | if duration_minutes is None or duration_in_traffic_minutes is None: 81 | self.log("Sensor is None. Homeassistant might not be fully started.") 82 | else: 83 | if duration_in_traffic_minutes <= duration_minutes * self.acceptable_range: 84 | if self.get_state(self.notify_input_boolean) == "on": 85 | destination_address = self.parse_destination_address(new) 86 | self.notify_user(destination_address) 87 | self.turn_off(self.notify_input_boolean) 88 | 89 | def notify_user(self, address: str) -> None: 90 | """Notify the user it is time to leave for the given address.""" 91 | message = self.message.format(address) 92 | self.log("Notify user") 93 | self.notifier.notify(self.notify_name, message, useAlexa=self.notify_use_Alexa) 94 | 95 | def parse_duration_in_traffic_minutes(self, state) -> Optional[int]: 96 | """Get duration_in_traffic from the states attributes.""" 97 | duration_in_traffic = state["attributes"].get("duration_in_traffic") 98 | duration_in_traffic_minutes = None 99 | if duration_in_traffic is not None: 100 | if isinstance(duration_in_traffic, float): 101 | duration_in_traffic_minutes = int(duration_in_traffic) 102 | else: 103 | duration_in_traffic_minutes = int( 104 | duration_in_traffic[: duration_in_traffic.find(" ")] 105 | ) 106 | else: 107 | self.log( 108 | "Could not find duration_in_traffic in state attributes.", 109 | level="WARNING", 110 | ) 111 | return duration_in_traffic_minutes 112 | 113 | def parse_destination_address(self, state) -> Optional[str]: 114 | """Get a destination address from the states attributes.""" 115 | attributes = state["attributes"] 116 | destination_address = None 117 | if "destination_name" in attributes: 118 | destination_address = attributes["destination_name"] 119 | elif "destination_addresses" in attributes: 120 | destination_address = attributes["destination_addresses"][0] 121 | else: 122 | self.log( 123 | "Could not find destination_name or destination_addresses in state attributes.", 124 | level="WARNING", 125 | ) 126 | return destination_address 127 | 128 | def parse_duration_minutes(self, state) -> Optional[int]: 129 | """Get duration from the states attributes.""" 130 | duration_minutes = None 131 | duration = state["attributes"].get("duration") 132 | if duration is not None: 133 | if isinstance(duration, float): 134 | duration_minutes = int(duration) 135 | else: 136 | duration_minutes = int(duration[: duration.find(" ")]) 137 | else: 138 | self.log("Could not find duration in state attributes.", level="WARNING") 139 | return duration_minutes 140 | 141 | def terminate(self) -> None: 142 | for listen_state_handle in self.listen_state_handle_list: 143 | self.cancel_listen_state(listen_state_handle) 144 | -------------------------------------------------------------------------------- /travelTimeNotifier/travelTimeNotifier.yaml: -------------------------------------------------------------------------------- 1 | # travelTime_home_from_work: 2 | # module: travelTimeNotifier 3 | # class: TravelTimeNotifier 4 | # sensor: sensor.travel_time_home_from_work_here 5 | # notify_input_boolean: input_boolean.travel_time_home_from_work 6 | # notify_name: group_notifications 7 | # message: "Du kannst losfahren nach {}" 8 | # #message: "You can start your journey to {}" 9 | # notify_use_Alexa: False 10 | # dependencies: 11 | # - Notifier 12 | 13 | # travelTime_work_from_home: 14 | # module: travelTimeNotifier 15 | # class: TravelTimeNotifier 16 | # sensor: sensor.travel_time_work_from_home_here 17 | # notify_input_boolean: input_boolean.travel_time_work_from_home 18 | # notify_name: group_notifications 19 | # message: "Du kannst losfahren nach {}" 20 | # #message: "You can start your journey to {}" 21 | # dependencies: 22 | # - Notifier 23 | 24 | # travelTime_elmo_from_home: 25 | # module: travelTimeNotifier 26 | # class: TravelTimeNotifier 27 | # sensor: sensor.travel_time_elmo_from_home_here 28 | # notify_input_boolean: input_boolean.travel_time_elmo_from_home 29 | # notify_name: group_notifications 30 | # message: "Du kannst losfahren nach {}" 31 | # #message: "You can start your journey to {}" 32 | # dependencies: 33 | # - Notifier 34 | 35 | # travelTime_home_from_elmo: 36 | # module: travelTimeNotifier 37 | # class: TravelTimeNotifier 38 | # sensor: sensor.travel_time_home_from_elmo_here 39 | # notify_input_boolean: input_boolean.travel_time_home_from_elmo 40 | # notify_name: group_notifications 41 | # message: "Du kannst losfahren nach {}" 42 | # #message: "You can start your journey to {}" 43 | # dependencies: 44 | # - Notifier 45 | 46 | # travelTime_work_user_two_from_home: 47 | # module: travelTimeNotifier 48 | # class: TravelTimeNotifier 49 | # sensor: sensor.travel_time_work_user_two_from_home_here 50 | # notify_input_boolean: input_boolean.travel_time_work_user_two_from_home 51 | # notify_name: group_notifications 52 | # message: "Du kannst losfahren nach {}" 53 | # #message: "You can start your journey to {}" 54 | # dependencies: 55 | # - Notifier 56 | 57 | # travelTime_home_from_work_user_two_here: 58 | # module: travelTimeNotifier 59 | # class: TravelTimeNotifier 60 | # sensor: sensor.travel_time_home_from_work_user_two_here 61 | # notify_input_boolean: input_boolean.travel_time_home_from_work_user_two 62 | # notify_name: group_notifications 63 | # message: "Du kannst losfahren nach {}" 64 | # #message: "You can start your journey to {}" 65 | # dependencies: 66 | # - Notifier 67 | -------------------------------------------------------------------------------- /turnFanOnWhenHot/turnFanOnWhenHot.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | import datetime 3 | 4 | # 5 | # App to Turn on fan when temp is above a threshold and someone is in the room 6 | # 7 | # Args: 8 | # 9 | # app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot 10 | # temp_sensor: temp sensor to monitor. example: sensor.large_lamp_temperature 11 | # threshold_entity: entity which holds the temp threshold which must be reached. example: input_number.turn_fan_on_when_hot_threshold 12 | # location_sensors: location sensors of users. example: sensor.location_user_one,sensor.location_user_two 13 | # room: Room name in which one of the users must be. example: Wohnzimmer 14 | # actor: actor to turn on 15 | # delay: seconds to wait before turning off. example: 120 16 | # Release Notes 17 | # 18 | # Version 1.3: 19 | # Added delay 20 | # 21 | # Version 1.2: 22 | # Using entities from HA now. Added turned_on_by_me 23 | # 24 | # Version 1.1: 25 | # Only turn on when someone is in the room. Turn off otherwise 26 | # 27 | # Version 1.0: 28 | # Initial Version 29 | 30 | 31 | class TurnFanOnWhenHot(hass.Hass): 32 | def initialize(self): 33 | self.listen_state_handle_list = [] 34 | self.timer_handle_list = [] 35 | 36 | self.app_switch = self.args["app_switch"] 37 | self.temp_sensor = self.args["temp_sensor"] 38 | self.threshold_entity = self.args["threshold_entity"] 39 | self.location_sensors = self.args["location_sensors"].split(",") 40 | self.room = self.args["room"] 41 | self.actor = self.args["actor"] 42 | self.delay = self.args["delay"] 43 | 44 | self.turned_on_by_me = False # Giggedi 45 | 46 | self.turn_off_timer_handle = None 47 | 48 | self.listen_state_handle_list.append( 49 | self.listen_state(self.state_change, self.temp_sensor) 50 | ) 51 | for sensor in self.location_sensors: 52 | self.listen_state_handle_list.append( 53 | self.listen_state(self.state_change, sensor) 54 | ) 55 | 56 | def state_change(self, entity, attribute, old, new, kwargs): 57 | if self.get_state(self.app_switch) == "on": 58 | turn_on = False 59 | if ( 60 | self.get_state(self.temp_sensor) != None 61 | and self.get_state(self.temp_sensor) != "unkown" 62 | and self.get_state(self.threshold_entity) != None 63 | and float(self.get_state(self.temp_sensor)) 64 | > float(self.get_state(self.threshold_entity)) 65 | ): 66 | for sensor in self.location_sensors: 67 | if self.get_state(sensor) == self.room: 68 | if self.get_state(self.actor) != "on": 69 | self.log( 70 | "{} is {}. This is above theshold of {}".format( 71 | self.friendly_name(self.temp_sensor), 72 | self.get_state(self.temp_sensor), 73 | self.get_state(self.threshold_entity), 74 | ) 75 | ) 76 | self.log("{} is in {}".format(sensor, self.room)) 77 | self.log( 78 | "Turning on {}".format(self.friendly_name(self.actor)) 79 | ) 80 | self.turn_on(self.actor) 81 | self.turned_on_by_me = True 82 | turn_on = True 83 | if self.turn_off_timer_handle != None: 84 | self.timer_handle_list.remove(self.turn_off_timer_handle) 85 | self.cancel_timer(self.turn_off_timer_handle) 86 | self.turn_off_timer_handle = None 87 | if not turn_on and self.turned_on_by_me: 88 | if self.get_state(self.actor) != "off": 89 | self.turn_off_timer_handle = self.run_in( 90 | self.turn_off_callback, self.delay 91 | ) 92 | self.timer_handle_list.append(self.turn_off_timer_handle) 93 | 94 | def turn_off_callback(self, kwargs): 95 | """Turn off the actor again if the timer was not cancelled in the meantime""" 96 | self.log("Turning off {}".format(self.friendly_name(self.actor))) 97 | self.turn_off(self.actor) 98 | self.turned_on_by_me = False 99 | self.turn_off_timer_handle = None 100 | 101 | def terminate(self): 102 | for listen_state_handle in self.listen_state_handle_list: 103 | self.cancel_listen_state(listen_state_handle) 104 | 105 | for timer_handle in self.timer_handle_list: 106 | self.cancel_timer(timer_handle) 107 | -------------------------------------------------------------------------------- /turnFanOnWhenHot/turnFanOnWhenHot.yaml: -------------------------------------------------------------------------------- 1 | # turnLargeFanOnWhenHot: 2 | # module: turnFanOnWhenHot 3 | # class: TurnFanOnWhenHot 4 | # app_switch: input_boolean.turn_large_fan_on_when_hot 5 | # temp_sensor: sensor.large_ventilator_temperature 6 | # threshold_entity: input_number.turn_large_fan_on_when_hot_threshold 7 | # location_sensors: sensor.location_user_one,sensor.location_user_two 8 | # room: Wohnzimmer 9 | # actor: switch.large_ventilator 10 | # delay: 120 11 | 12 | # turnSmallFanOnWhenHot: 13 | # module: turnFanOnWhenHot 14 | # class: TurnFanOnWhenHot 15 | # app_switch: input_boolean.turn_small_fan_on_when_hot 16 | # temp_sensor: sensor.small_ventilator_temperature 17 | # threshold_entity: input_number.turn_small_fan_on_when_hot_threshold 18 | # location_sensors: sensor.location_user_one,sensor.location_user_two 19 | # room: Wohnzimmer 20 | # actor: switch.small_ventilator 21 | # delay: 120 -------------------------------------------------------------------------------- /turnOffBarAfterRestart/turnOffBarAfterRestart.py: -------------------------------------------------------------------------------- 1 | import appdaemon.plugins.hass.hassapi as hass 2 | from requests.exceptions import HTTPError 3 | 4 | # 5 | # Will turn the bar table green and then off when homeassistant restarts to indicate the restart went well 6 | # 7 | # 8 | # Args: 9 | # 10 | # light: light. example: light.bar_table 11 | # 12 | # Release Notes 13 | # 14 | # Version 1.0: 15 | # Initial Version 16 | 17 | 18 | class TurnOffBarAfterRestart(hass.Hass): 19 | def initialize(self): 20 | 21 | self.timer_handle_list = [] 22 | self.listen_event_handle_list = [] 23 | self.listen_state_handle_list = [] 24 | 25 | self.light = self.args["light"] 26 | 27 | self.timer_handle_list.append(self.run_in(self.turn_green_callback, 1)) 28 | 29 | def turn_off_callback(self, kwargs): 30 | """Turn off light""" 31 | try: 32 | self.log("Turning {} off".format(self.friendly_name(self.light))) 33 | self.turn_off(self.light) 34 | except HTTPError as exception: 35 | self.log( 36 | "Error trying to turn off entity. Will try again in 1s. Error: {}".format( 37 | exception 38 | ), 39 | level="WARNING", 40 | ) 41 | self.timer_handle_list.append(self.run_in(self.turn_off_callback, 1)) 42 | 43 | def turn_green_callback(self, kwargs): 44 | """This is needed because the turn_on command can result in a HTTP 503 when homeassistant is restarting""" 45 | try: 46 | self.call_service( 47 | "light/turn_on", 48 | entity_id=self.light, 49 | rgb_color=[0, 255, 0], 50 | white_value=0, 51 | ) 52 | self.log("Turning {} green".format(self.friendly_name(self.light))) 53 | self.timer_handle_list.append(self.run_in(self.turn_off_callback, 5)) 54 | except HTTPError as exception: 55 | self.log( 56 | "Error trying to turn on entity. Will try again in 1s. Error: {}".format( 57 | exception 58 | ), 59 | level="WARNING", 60 | ) 61 | self.timer_handle_list.append(self.run_in(self.turn_green_callback, 1)) 62 | 63 | def terminate(self): 64 | for timer_handle in self.timer_handle_list: 65 | self.cancel_timer(timer_handle) 66 | 67 | for listen_event_handle in self.listen_event_handle_list: 68 | self.cancel_listen_event(listen_event_handle) 69 | 70 | for listen_state_handle in self.listen_state_handle_list: 71 | self.cancel_listen_state(listen_state_handle) 72 | -------------------------------------------------------------------------------- /turnOffBarAfterRestart/turnOffBarAfterRestart.yaml: -------------------------------------------------------------------------------- 1 | # turnOffBarAfterRestart: 2 | # module: turnOffBarAfterRestart 3 | # class: TurnOffBarAfterRestart 4 | # light: light.bar_table --------------------------------------------------------------------------------