Showing preview only (325K chars total). Download the full file or copy to clipboard to get everything.
Repository: eifinger/appdaemon-scripts
Branch: master
Commit: 0a8e19f5616b
Files: 100
Total size: 298.5 KB
Directory structure:
gitextract_92xa7u3p/
├── .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/
│ └── deconz_xiaomi_button.py
├── deconz_xiaomi_button.yaml
├── 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
├── 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
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
#appdaemon
secrets.py
#VS Code
.vscode
#PyCharm
.idea
#AppDaemon
secrets.yaml
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Kevin Eifinger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<h1 align="center">
<a name="logo" href=""><img src="images/logo-round-192x192.png" alt="Home Assistant Logo" width="192"></a>
<br>
eifinger's Appdaemon Scripts
</h1>
## About
This is the repository containing all my Appdaemon apps.
Used together with my Homeassistant config which you can find here:
[https://github.com/eifinger/homeassistant-config](https://github.com/eifinger/homeassistant-config)
I use Appdaemon for all my automations since I am a programmer myself and it provides me with all the possibilities
of the Python world and a far better debugging experience than
[HA Automations](https://www.home-assistant.io/getting-started/automation/) or [nodered](https://nodered.org/).
### No longer actively maintained
I no longer use appdaemon for my automations and switched over to automations completely:
* Traces allow easy debugging of what happened and why
* I can see related entities from automations and vise versa
* I can quickly adjust automations via the UI editor on my smartphone
* There is no disconnect/lag between Appdaemon and Homeassistant.
## How to contribute
Just open an Issue or a Pull Request for any Comments, Questions, etc.
**Or you can message me on twitter :** [@eifinger](https://twitter.com/eifinger)
## How to use
If you have never used Appdaemon before I suggest you start with the
[tutorial](https://appdaemon.readthedocs.io/en/latest/TUTORIAL.html) and the
[guide](https://appdaemon.readthedocs.io/en/latest/APPGUIDE.html).
Both contain more links to great tutorials and examples.
I tried to write each App in this repository with reusability in mind.
This means that every app in here has a short documentation
and is (if possible) written to be easily adjusted to your environment and your needs.
### app_switch
Every App has an input_boolean inside HA which turns it on/off.
This is useful if I don't want any notifications right now or an App is misbehaving.
## App list
* [Alexa Intents](#alexaintents)
* [AlexaSpeakerConnector](#alexaspeakerconnector)
* [appWatcher](#appWatcher)
* [alarmClock](#alarmclock)
* [buttonClicked](#buttonclicked)
* [comingHome](#cominghome)
* [deconzXiaomiButton](#deconzxiaomibutton)
* [detectWrongState](#detectwrongstate)
* [eventMonitor](#eventmonitor)
* [faceRecognitionBot](#facerecognitionbot)
* [google_travel_time](#google_travel_time)
* [heartbeat](#heartbeat)
* [homeArrivalNotifier](#homearrivalnotifier)
* [isHomeDeterminer](#ishomedeterminer)
* [isUserHomeDeterminer](#isuserhomedeterminer)
* [leavingZoneNotifier](#leavingzonenotifier)
* [motionTrigger](#motiontrigger)
* [newWifiDeviceNotify](#newwifidevicenotify)
* [nextAppointmentLeaveNotifier](#nextappointmentleavenotifier)
* [notifyOfActionWhenAway](#notifyofactionwhenaway)
* [plantWateringNotifier](#plantwateringnotifier)
* [powerUsageNotification](#powerusagenotification)
* [seqSink](#seqsink)
* [setThermostat](#setthermostat)
* [setThermostatOnStateChange](#setthermostatonstatechange)
* [sleepModeHandler](#sleepmodehandler)
* [turnFanOnWhenHot](#turnfanonwhenhot)
* [turnOffBarAfterRestart](#turnoffbarafterrestart)
* [updateEntityService](#updateentityservice)
* [notify](#notify)
### AlexaIntents
Are explained [here](alexa/README.md)
### AlexaSpeakerConnector
App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers.
Uses a [custom_component](https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639)
for control alexa as a media player.
```yaml
alexaSpeakerConnector:
module: alexaSpeakerConnector
class: AlexaSpeakerConnector
app_switch: input_boolean.alexaSpeakerConnector
alexa_entity: media_player.kevins_echo_dot_oben
alexa_entity_source: Denon AVR-X1300W
receiver: media_player.denon_avr_x1300w
receiver_source: Bluetooth
```
### appWatcher
Sends a notification if a WARNING or ERROR is logged in Appdaemon
```yaml
heartbeat:
module: appWatcher
class: AppWatcher
notify_name: kevin
include_log_message_in_notification: True
notify_message: "Es ist ein Fehler aufgetreten in App: {}"
#notify_message: "An Error occurred in App: {}"
dependencies:
- Notifier
```
### alarmClock
Alarm Clock App inspired by [this](https://community.home-assistant.io/t/creating-a-alarm-clock/410)
forum post.
It fades in my bedroom light and sends a notifcation.
The fade in and alarm time is defined by input_number sliders in HA
```yaml
alarmClock:
module: alarmClock
class: AlarmClock
alarm_time: sensor.alarm_time
wakemeup: input_boolean.wakemeup
naturalwakeup: input_number.alarm_natural_wakeup_fade_in
alarmweekday: input_boolean.alarmweekday
radiowakeup: input_boolean.radiowakeup
#TODO radioplayer: input_select.wakeup_radioplayer
wakeup_light: light.bedroom_yeelight
isweekday: binary_sensor.workday_today
notify_name: group_notifications
message: "Guten Morgen!"
#message: "Good Morning!"
```

### buttonClicked
My multipurpose App to link any switch/light to a Xiaomi Button.
You can map different entities to the click types ``single`` and ``double``.
For the 1st Generation Button you can hold the button to use it as a light dimmer.
```yaml
xiaomiroundButtonBedroomClicked:
module: buttonClicked
class: ButtonClicked
sensor: binary_sensor.switch_158d0001b12a12
actor_single: light.bedroom_yeelight
actor_double: group.all
actor_hold: light.bedroom_yeelight
dependencies:
- Notifier
```
### comingHome
When the front door openes and no one was home before this will turn on something.
I am using it to turn on the light (if the sun is down) and turn on the receiver so I can hear Alexa
```yaml
comingHomeYeelight:
module: comingHome
class: ComingHome
app_switch: input_boolean.coming_home_yeelight
sensor: binary_sensor.door_window_sensor_158d000126a57b
isHome: input_boolean.is_home
actor: switch.large_lamp
after_sundown: True
```
### deconzXiaomiButton
App which toggles entities for single/double/hold presses of Xiaomi buttons connected via deconz.
This app is installed via [HACS](https://github.com/custom-components/hacs).
The repository itself with the full documentation can be found on github:
[appdaemon-deconz-xiaomi-button](https://github.com/eifinger/appdaemon-deconz-xiaomi-button)
```yaml
DeconzXiaomiButtonBedroom:
module: deconz_xiaomi_button
class: DeconzXiaomiButton
id: round_button_schlafzimmer
actor_single: light.bedroom_yeelight
actor_double: group.all
actor_hold: light.bedroom_yeelight
```
### detectWrongState
Checks a list of entities which should be on/off when everybody left the house.
If something isn't right it will try to turn it off (e.g. a light) and send a notification.
```yaml
detectWrongStateWhenLeaving:
module: detectWrongStateWhenLeaving
class: DetectWrongStateWhenLeaving
app_switch: input_boolean.detect_wrong_state_when_leaving
entities_off: "binary_sensor.door_window_sensor_158d000205b808,binary_sensor.door_window_sensor_158d00020499ad,\
binary_sensor.door_window_sensor_158d0002059ddf,media_player.denon_avr_x1300w,switch.large_lamp,\
switch.small_lamp,switch.snowboard,light.bedroom_yeelight,light.bar_table,light.lobby_yeelight,\
light.reading_lamp_yeelight,light.treppe_oben,light.stairs_lower_yeelight,switch.ventilator"
message: "Du hast {} angelassen. Ich habe es für dich ausgemacht."
#message: "You left on {}. I turned it off for you"
message_off: "Du hast {} vergessen anzumachen. Ich habe es für dich angemacht."
#message_off: "You forgot to turn on {}. I turned it on for you"
message_reed: "Du hast {} offen gelassen."
#message_reed: "You left open {} Dummy."
message_reed_off: "Du hast {} zu gelassen."
#message_reed_off: "You left {} closed Dummy."
isHome: input_boolean.is_home
```
### eventMonitor
Monitor all events. Useful for debugging and developing
```yaml
eventMonitor:
module: eventMonitor
class: Monitor
events:
```
### faceRecognitionBot
COMING SOON
### travel_time
Monitors my Travel Time Sensors e.g. between home and work.
I can enable an input_boolean in HA which causes this App to send me a notication
as soon as the traffic is in an acceptable range. I use this drive to/from work when there is the least traffic.
```yaml
travelTime_home_from_work:
module: travelTimeNotifier
class: TravelTimeNotifier
sensor: sensor.travel_time_home_from_work
notify_input_boolean: input_boolean.travel_time_home_from_work
notify_name: group_notifications
message: "Du kannst losfahren nach {}"
#message: "You can start your journey to {}"
```

### heartbeat
Sets a sensor in Homeassistant which is checked by an automation.
The [automation](https://github.com/eifinger/homeassistant-config/blob/master/automation.yaml)
sends out a notification if appdaemon does not respond.
### homeArrivalNotifier
Greet the person coming home with a notification
```yaml
homeArrivalNotifierUserOne:
module: homeArrivalNotifier
class: HomeArrivalNotifier
app_switch: input_boolean.home_arrival_notifier_user_one
input_boolean: input_boolean.user_one_home
notify_name: group_notifications
user_name: Kevin
zone_name: Home
message: "Willkommen zu Hause {}."
#message: "Welcome Home {}."
```
### isHomeDeterminer
Controls an input_boolean "isHome" which is used as a trigger for other Apps.
The state depends on other input_booleans controlled by the
[isUserHomeDeterminer](isUserHomeDeterminer/isUserHomeDeterminer.py)
```yaml
isHomeDeterminer:
module: isHomeDeterminer
class: IsHomeDeterminer
app_switch: input_boolean.is_home_determiner
ishome: input_boolean.is_home
input_booleans: input_boolean.user_one_home,input_boolean.user_two_home
message: "Es ist keiner mehr zu Hause. Setze isHome auf off"
#message: "Everyone left home. Setting isHome to off"
```
### isUserHomeDeterminer
The GPS Logger tells me where someone is. But I want to know for sure who just came in the door.
App to toggle an input boolean when a person enters or leaves home.
This is determined based on a combination of a GPS device tracker and the door sensor.
* If the door sensor opens and the device_tracker changed to "home" in the last self.delay minutes this means someone got home
* If the door sensor opens and the device_tracker changes to "not_home" in the next self.delay minutes this means someone left home
```yaml
isUserHomeDeterminerUserOne:
module: isUserHomeDeterminer
class: IsUserHomeDeterminer
app_switch: input_boolean.is_user_home_determiner_user_one
input_boolean: input_boolean.user_one_home
device_tracker: person.kevin
door_sensor: binary_sensor.door_window_sensor_158d000126a57b
```
### leavingZoneNotifier
Notify if a user is leaving a zone after being there for a certain amount of time.
I use this to notify my SO that I am leaving work and driving home
```yaml
leavingWorkNotifierUserOne:
module: leavingZoneNotifier
class: LeavingZoneNotifier
app_switch: input_boolean.leaving_work_notifier_user_one
device: person.kevin
user_name: Kevin
lingering_time: 3600
delay: 120
zone: Arbeit
notify_name: group_notifications
message: "{} hat {} vor {} Minuten verlassen."
travel_time_sensor: sensor.travel_time_home_user_one
travel_time_sensor_message: "Die momentane Reisezeit beträgt {}."
dependencies:
- Notifier
```
### motionTrigger
Turn something on/off when a motion sensor turns on. Automatically turn it off again after a delay.
```yaml
bathMotionTrigger:
module: motionTrigger
class: MotionTrigger
app_switch: input_boolean.bath_motion_trigger
sensor: binary_sensor.0x00158d000236b801_occupancy
entity_on: light.lower_bathroom_yeelight
entity_off: light.lower_bathroom_yeelight
sensor_type: zigbee2mqtt
after_sundown: True
turn_off_constraint_entities_on: binary_sensor.0x00158d0001fa464b_occupancy
delay: 300
```
### newWifiDeviceNotify
Actually a wrong name. This will send me a notification when any device_tracker component detects a new device.
I initally thought to use this as a security feature but found it quite useful when adding new Sonoff switches and such.
I get a notification if the setup was successfull.
**Version 1.2:**
Displays two buttons which let me control the internet access for the new device.
The configured fritzbox standard profile denies internet access.
With this I can easily allow my guests access to the internet without logging in to my fritzbox manually.
```yaml
newWifiDeviceNotify:
module: newWifiDeviceNotify
class: DeviceNotify
notify_name: group_notifications
message: "Unbekanntes Gerät entdeckt. Hostname: {}. MAC: {}."
#message: "Unknown device connected. Hostname: {}. MAC: {}"
```
### nextAppointmentLeaveNotifier
Send me a notification when it is time to leave for my next appointment based on my current location.
Inspired by [this](https://community.home-assistant.io/t/text-to-speech-notification-to-leave-for-appointment/8689)
blog post.
* Selectable travel mode (car/bus/walk/bike)
* Only for google calendar events which have a location
* Adjustable offset when to notify
* Includes a direct Google Maps Navigation Link in Notification Message
Saved my ass quite a few times
```yaml
nextAppointmentLeaveNotifier:
module: nextAppointmentLeaveNotifier
class: NextAppointmentLeaveNotifier
sensor: sensor.calc_leave_time
notify_input_boolean: input_boolean.announce_time_to_leave
notify_name: group_notifications
input_number: input_number.leave_time_offset
destination_name_sensor: sensor.cal_next_appointment_location
travel_time_sensor: sensor.travel_time_next_appointment_location
message: "Es ist Zeit loszufahren nach {}. Du brauchst {} Minuten. Hier ist ein Google Maps Link: {}"
#message: "It's time to leave to {}. It will take {} minutes. Here is a Google Maps Link: {}"
```


### notifyOfActionWhenAway
Notify me of any event for a list of entities when no one is at home.
For example a door being openend or a motion sensor triggered
```yaml
notifyOfActionWhenAway:
module: notifyOfActionWhenAway
class: NotifyOfActionWhenAway
app_switch: input_boolean.notify_of_action_when_away
sensor: "binary_sensor.door_window_sensor_158d000126a57b,binary_sensor.door_window_sensor_158d0001bb4d94,\
binary_sensor.door_window_sensor_158d0001bb4dc0,binary_sensor.door_window_sensor_158d000205b808,\
binary_sensor.door_window_sensor_158d000205b82e,binary_sensor.door_window_sensor_158d00020498b6,\
binary_sensor.door_window_sensor_158d000204ba26,binary_sensor.door_window_sensor_158d0002059ddf,\
binary_sensor.door_window_sensor_158d00020499ad,binary_sensor.door_window_sensor_158d0002048951,\
binary_sensor.door_window_sensor_158d00020455bf,binary_sensor.motion_sensor_158d00012aab97,\
binary_sensor.motion_sensor_158d0001fa464b,binary_sensor.motion_sensor_158d0002006cfa"
isHome: input_boolean.is_home
user_name: group_notifications
isHome_delay: 20
message: "Alarm: {} ist gewechselt auf {}"
#message: "Alarm: {} changed to {}"
```

### plantWateringNotifier
Remind us to water the plants in the morning when the precipiation propability is too low.
This uses a Telegram Chatbot. We can press a button in the notification to tell the App that we watered the plants.
If we don't do that we get reminded again in the evening.
```yaml
plantWateringNotifier:
module: plantWateringNotifier
class: PlantWateringNotifier
app_switch: input_boolean.plant_watering_notifier
rain_precip_sensor: sensor.dark_sky_precip_probability
rain_precip_intensity_sensor: sensor.dark_sky_precip_intensity
precip_type_sensor: sensor.dark_sky_precip
notify_name: group_notifications
user_id: secret! telegram_user_id
reminder_acknowledged_entity: input_boolean.persistence_plantwateringnotifier_reminder_acknowledged
message: "Die Regenwahrscheinlichkeit beträgt heute nur {}. Vergiss nicht die Pflanzen zu gießen!"
#message: "The Rain Propability is only {}. Don't forget to water the plants!"
message_not_needed: "Es wird heute mit einer Wahrscheinlichkeit von {} Prozent ungefähr {} Millimeter pro Stunde regnen. Du brauchst nicht selbst gießen."
#message_not_needed: "It will rain today {} millimeter per hour with a propability of {}. You don't have to water your plants"
message_evening: "Ich bin mir nicht sicher ob du vergessen hast die Pflanzen zu gießen, deswegen erinnere ich dich lieber noch einmal daran."
#message_evening: "I'm not sure whether you waterd your plants, so I thought I better remind you again"
```


### pollenNotifier
Notify in the morning if any monitored pollen level is above a threshold.
```yaml
roggenNotifier:
module: pollenNotifier
class: PollenNotifier
app_switch: input_boolean.roggen_notifier
pollen_sensor: sensor.pollen_101_roggen_today
pollen_name: Roggen
notify_name: group_notifications
notify_time: 08:00
notify_threshold: 1.0
message: "{} ist {} {} Belastung."
#message: "The {} intensity {} is {}."
message_no_data: "Ich habe {} leider keine Daten für {}."
#message_no_data: "{} I have no pollen data for {}."
```

### powerUsageNotification
Notify when the Washingmachine or Dishwasher started/finished. Using power measured by TP HS110 Plugs like[](https://www.amazon.de/dp/B017X72IES/ref=twister_B07CQBCZ5G)
```yaml
powerUsageNotification_Dishwasher:
module: powerUsageNotification
class: PowerUsageNotification
app_switch: input_boolean.power_usage_notification_dishwasher
sensor: sensor.dishwasher_power_usage
notify_name: group_notifications
delay: 1260 #21 minutes
threshold: 2
alternative_name: Die Spülmaschine
message: "{} ist gestartet."
#message: "{} just started."
message_off: "{} ist fertig."
#message_off: "{} just finished."
```

### seqSink
App which forwards all logs to seq.
Blogged about this app in [this](https://blog.kevineifinger.de/archive/2020/07/06/Log-Management-For-My-AppDaemon-Apps.html) post.
```yaml
seqSink:
module: seqSink
class: SeqSink
server_url: "http://seq:5341/"
```
### setThermostat
App which sets a thermostat to a target temperature for a specific duration
```yaml
warm_bath_before_wakeup:
module: setThermostat
class: SetThermostat
app_switch: input_boolean.warm_bath_before_wakeup
isHome: input_boolean.is_home
time_entity: sensor.alarm_time
upfront_time: 60
duration: 60
climat_entity: climate.bad_thermostat
target_entity: input_number.warm_bath_before_wakeup
message: "Ich habe {} auf {} gestellt"
#message: "I have set {} to {}"
notify_name: group_notifications
use_alexa: False
dependencies:
- Notifier
```
### setThermostatOnStateChange
App which sets a thermostat to a target temperature on state change.
```yaml
setBadObenThermostatWhenComingHome:
module: setThermostatOnStateChange
class: SetThermostatOnStateChange
app_switch: input_boolean.set_upper_bath_thermostat_when_coming_home
trigger_entity: input_boolean.is_home
trigger_state: "on"
climate_entity: climate.bad_oben_thermostat
target_entity: input_number.set_upper_bath_thermostat_when_coming_home
message: "Ich habe {} auf {} °C gestellt"
#message: "I have set {} to {}"
notify_name: group_notifications
use_alexa: False
dependencies:
- Notifier
```
### sleepModeHandler
Set an input_boolean on/off. Used as a trigger for other Apps.
Also controlled by ``Alexa, guten Morgen`` ``Alexa, gute Nacht``
Will watch room sensor of users
````yaml
sleepModeHandler:
module: sleepModeHandler
class: SleepModeHandler
app_switch: input_boolean.sleep_mode_handler
sleepmode: input_boolean.sleepmode
notify_name: group_notifications
message_sleeping: "Alle zu Hause sind im Bett"
#message_sleeping: "All home are in bed"
message_awake: "Alle zu Hause sind wach"
#message_awake: "All home are awake"
users:
- sleep_mode: input_boolean.user_one_sleep
isHome: input_boolean.user_one_home
- sleep_mode: input_boolean.user_two_sleep
isHome: input_boolean.user_two_home
dependencies:
- Notifier
````
````yaml
userSleepModeHandlerUserOne:
module: userSleepModeHandler
class: UserSleepModeHandler
app_switch: input_boolean.user_sleep_mode_handler_user_one
input_boolean: input_boolean.user_one_sleep
location_sensor: person.kevin
room: bedroom
duration: 600
userSleepModeHandlerUserTwo:
module: userSleepModeHandler
class: UserSleepModeHandler
app_switch: input_boolean.user_sleep_mode_handler_user_two
input_boolean: input_boolean.user_two_sleep
location_sensor: sensor.mqtt_room_user_two
room: bedroom
duration: 600
````
### turnFanOnWhenHot
Turns the Fan on when the temperature is above a configurable threshold and someone is in the room
([find3](https://github.com/schollz/find3))
```yaml
turnFanOnWhenHot:
module: turnFanOnWhenHot
class: TurnFanOnWhenHot
app_switch: input_boolean.turn_fan_on_when_hot
temp_sensor: sensor.large_lamp_temperature
threshold_entity: input_number.turn_fan_on_when_hot_threshold
location_sensors: sensor.location_user_one,sensor.location_user_two
room: Wohnzimmer
actor: switch.large_ventilator
delay: 120
```

### turnOffBarAfterRestart
As I sometimes restart HA when working on it from remote I turn the Bar lights to red with
[this script](https://github.com/eifinger/homeassistant-config/blob/master/updateHomeassistant.sh).
This way everyone can see HA is currently unavailable.
If it comes back up again this app will turn the light green and then off.
### notify
IN DEVELOPMENT
Centralizes messaging. Among other things, it will determine whether a user is at home and if yes in which room.
Then Alexa in that room will be used additionally to Telegram
```yaml
Notify:
module: notify
class: Notify
media_player: media_player.denon_avr_x1300w
source: CBL/SAT
alexa_media_player: media_player.kevins_echo_dot_oben
global_dependencies:
- globals
```
# Thanks
First of all thanks to the Homeassistant Team and [Andrew Cockburn](https://github.com/acockburn) for making Appdaemon
Some of the Apps are taken from the official examples and many based on or at least inspired by
[Rene Tode](https://github.com/ReneTode).
For example his absolutely fantastic [Alexa-Appdaemon-App](https://github.com/ReneTode/Alexa-Appdaemon-App).
================================================
FILE: ad-ench/ench.py
================================================
"""EnCh.
Entity Checker
@benleb / https://github.com/benleb/ad-ench
"""
__version__ = "0.9.0"
from datetime import datetime, timedelta
from fnmatch import fnmatch
from pprint import pformat
from sys import version_info
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import hassapi as hass
APP_NAME = "EnCh"
APP_ICON = "👩⚕️"
BATTERY_MIN_LEVEL = 20
INTERVAL_BATTERY_MIN = 180
INTERVAL_BATTERY = INTERVAL_BATTERY_MIN / 60
INTERVAL_UNAVAILABLE_MIN = 60
INTERVAL_UNAVAILABLE = INTERVAL_UNAVAILABLE_MIN / 60
MAX_UNAVAILABLE_MIN = 0
INTERVAL_STALE_MIN = 15
MAX_STALE_MIN = 60
INITIAL_DELAY = 60
RANDOMIZE_SEC: int = 15
SECONDS_PER_MIN: int = 60
EXCLUDE = ["binary_sensor.updater", "persistent_notification.config_entry_discovery"]
BAD_STATES = ["unavailable", "unknown"]
LEVEL_ATTRIBUTES = ["battery_level", "Battery Level"]
CHECKS = ["battery", "stale", "unavailable"]
ICONS: Dict[str, str] = dict(battery="🔋", unavailable="⁉️ ", unknown="❓", stale="⏰")
# version checks
py3_or_higher = version_info.major >= 3
py37_or_higher = py3_or_higher and version_info.minor >= 7
py38_or_higher = py3_or_higher and version_info.minor >= 8
def hl(text: Union[int, float, str]) -> str:
return f"\033[1m{text}\033[0m"
def hl_entity(entity: str) -> str:
if len(splitted := entity.split(".")) > 1:
return f"{splitted[0]}.{hl(splitted[1])}"
else:
return f"{hl(entity)}"
class EnCh(hass.Hass): # type: ignore
"""EnCh."""
def lg(self, msg: str, *args: Any, icon: Optional[str] = None, repeat: int = 1, **kwargs: Any) -> None:
kwargs.setdefault("ascii_encode", False)
message = f"{f'{icon} ' if icon else ' '}{msg}"
_ = [self.log(message, *args, **kwargs) for _ in range(repeat)]
async def initialize(self) -> None:
"""Register API endpoint."""
self.icon = APP_ICON
# python version check
if not py38_or_higher:
icon_alert = "⚠️"
self.lg("", icon=icon_alert)
self.lg("")
self.lg(f"please update to {hl('Python >= 3.8')}! 🤪", icon=icon_alert)
self.lg("")
self.lg("", icon=icon_alert)
if not py37_or_higher:
raise ValueError
self.cfg: Dict[str, Any] = dict()
self.cfg["show_friendly_name"] = bool(self.args.get("show_friendly_name", True))
self.cfg["init_delay_secs"] = int(self.args.get("initial_delay_secs", INITIAL_DELAY))
# home assistant sensor
hass_sensor: str
if hass_sensor := self.args.get("hass_sensor", "sensor.ench_entities"):
self.cfg["hass_sensor"] = hass_sensor if hass_sensor.startswith("sensor.") else f"sensor.{hass_sensor}"
self.sensor_state: int = 0
self.sensor_attrs: Dict[str, Any] = {check: [] for check in CHECKS}
self.sensor_attrs.update({"unit_of_measurement": "Entities", "should_poll": False})
# global notification
if "notify" in self.args:
self.cfg["notify"] = self.args.get("notify")
# initial wait to give all devices a chance to become available
init_delay = await self.datetime() + timedelta(seconds=self.cfg["init_delay_secs"])
# battery check
if "battery" in self.args:
config: Dict[str, Union[str, int]] = self.args.get("battery")
# store configuration
self.cfg["battery"] = dict(
interval_min=int(config.get("interval_min", INTERVAL_BATTERY_MIN)),
min_level=int(config.get("min_level", BATTERY_MIN_LEVEL)),
)
# no, per check or global notification
self.choose_notify_recipient("battery", config)
# schedule check
await self.run_every(
self.check_battery,
init_delay,
self.cfg["battery"]["interval_min"] * 60,
random_start=-RANDOMIZE_SEC,
random_end=RANDOMIZE_SEC,
)
# unavailable check
if "unavailable" in self.args:
config = self.args.get("unavailable")
# store configuration
self.cfg["unavailable"] = dict(
interval_min=int(config.get("interval_min", INTERVAL_UNAVAILABLE_MIN)),
max_unavailable_min=int(config.get("max_unavailable_min", MAX_UNAVAILABLE_MIN)),
)
# no, per check or global notification
self.choose_notify_recipient("unavailable", config)
# schedule check
self.run_every(
self.check_unavailable,
await self.datetime() + timedelta(seconds=self.cfg["init_delay_secs"]),
self.cfg["unavailable"]["interval_min"] * 60,
random_start=-RANDOMIZE_SEC,
random_end=RANDOMIZE_SEC,
)
# stale entities check
if "stale" in self.args:
config = self.args.get("stale", {})
interval_min = config.get("interval_min", INTERVAL_STALE_MIN)
max_stale_min = config.get("max_stale_min", MAX_STALE_MIN)
# store configuration
self.cfg["stale"] = dict(
interval_min=int(min([interval_min, max_stale_min])),
max_stale_min=int(max_stale_min),
)
self.cfg["stale"]["entities"] = config.get("entities", [])
# no, per check or global notification
self.choose_notify_recipient("stale", config)
# schedule check
self.run_every(
self.check_stale,
await self.datetime() + timedelta(seconds=self.cfg["init_delay_secs"]),
self.cfg["stale"]["interval_min"] * 60,
random_start=-RANDOMIZE_SEC,
random_end=RANDOMIZE_SEC,
)
# merge excluded entities
exclude = set(EXCLUDE)
exclude.update([e.lower() for e in self.args.get("exclude", set())])
self.cfg["exclude"] = sorted(list(exclude))
# set units
self.cfg.setdefault(
"_units",
dict(interval_min="min", max_stale_min="min", min_level="%"),
)
self.show_info(self.args)
async def check_battery(self, _: Any) -> None:
"""Handle scheduled checks."""
check_config = self.cfg["battery"]
results: List[Tuple[str, int]] = []
self.lg("Checking entities for low battery levels...", icon=APP_ICON, level="DEBUG")
states = await self.get_state()
entities = filter(
lambda entity: not any(fnmatch(entity, pattern) for pattern in self.cfg["exclude"]),
states,
)
for entity in sorted(entities):
battery_level = None
try:
# check entities which may be battery level sensors
if "battery_level" in entity or "battery" in entity:
# battery_level = int(await self.get_state(entity))
battery_level = int(states[entity]["state"])
# check entity attributes for battery levels
if not battery_level:
for attr in LEVEL_ATTRIBUTES:
# battery_level = int(await self.get_state(entity, attribute=attr))
battery_level = int(states[entity]["attributes"].get(attr))
break
except (TypeError, ValueError):
pass
if battery_level and battery_level <= check_config["min_level"]:
# results.append(entity)
results.append((entity, battery_level))
last_updated = (await self.last_update(entity)).time().isoformat(timespec="seconds")
self.lg(
f"{await self._name(entity)} has low "
# f"{hl(f'battery → {hl(int(battery_level))}')}%",
f"{hl(f'battery → {hl(int(battery_level))}')}% | " f"last update: {last_updated}",
# f"last update: {self.adu.last_update(entity)}",
icon=ICONS["battery"],
)
# send notification
notify = self.cfg.get("notify") or check_config.get("notify")
if notify and results:
await self.call_service(
str(notify).replace(".", "/"),
message=f"{ICONS['battery']} Battery low ({len(results)}): "
f"{', '.join([f'{str(await self._name(entity[0], notification=True))} {entity[1]}%' for entity in results])}", # noqa
)
# update hass sensor
if "hass_sensor" in self.cfg and self.cfg["hass_sensor"]:
await self.update_sensor("battery", [entity[0] for entity in results])
self._print_result("battery", [entity[0] for entity in results], "low battery levels")
async def check_unavailable(self, _: Any) -> None:
"""Handle scheduled checks."""
check_config = self.cfg["unavailable"]
results: List[str] = []
self.lg("Checking entities for unavailable/unknown state...", icon=APP_ICON, level="DEBUG")
entities = filter(
lambda entity: not any(fnmatch(entity, pattern) for pattern in self.cfg["exclude"]),
await self.get_state(),
)
for entity in sorted(entities):
state = await self.get_state(entity_id=entity)
if state in BAD_STATES and entity not in results:
last_update = await self.last_update(entity)
now: datetime = await self.datetime(aware=True)
unavailable_time: timedelta = now - last_update
max_unavailable_min = timedelta(minutes=self.cfg["unavailable"]["max_unavailable_min"])
if unavailable_time >= max_unavailable_min:
results.append(entity)
last_updated = (await self.last_update(entity)).time().isoformat(timespec="seconds")
self.lg(
f"{await self._name(entity)} is "
f"{hl(state)} since {hl(int(unavailable_time.seconds / 60))}min | "
f"last update: {last_updated}",
icon=ICONS[state],
)
# self.lg(
# f"{await self._name(entity)} is {hl(state)} | " f"last update: {last_updated}",
# icon=ICONS[state],
# )
# send notification
notify = self.cfg.get("notify") or check_config.get("notify")
if notify and results:
self.call_service(
str(notify).replace(".", "/"),
message=f"{APP_ICON} Unavailable entities ({len(results)}): "
f"{', '.join([str(await self._name(entity, notification=True)) for entity in results])}",
)
# update hass sensor
if "hass_sensor" in self.cfg and self.cfg["hass_sensor"]:
await self.update_sensor("unavailable", results)
self._print_result("unavailable", results, "unavailable/unknown state")
async def check_stale(self, _: Any) -> None:
check_config = self.cfg["stale"]
"""Handle scheduled checks."""
results: List[str] = []
self.lg("Checking for stale entities...", icon=APP_ICON, level="DEBUG")
if self.cfg["stale"]["entities"]:
all_entities = self.cfg["stale"]["entities"]
else:
all_entities = await self.get_state()
entities = filter(
lambda entity: not any(fnmatch(entity, pattern) for pattern in self.cfg["exclude"]),
all_entities,
)
for entity in sorted(entities):
attr_last_updated = await self.get_state(entity_id=entity, attribute="last_updated")
if not attr_last_updated:
self.lg(f"{await self._name(entity)} has no 'last_updated' attribute ¯\\_(ツ)_/¯", icon=ICONS["stale"])
continue
last_update = self.convert_utc(attr_last_updated)
last_updated = (await self.last_update(entity)).time().isoformat(timespec="seconds")
now: datetime = await self.datetime(aware=True)
stale_time: timedelta = now - last_update
max_stale_min = timedelta(minutes=self.cfg["stale"]["max_stale_min"])
if stale_time and stale_time >= max_stale_min:
results.append(entity)
self.lg(
f"{await self._name(entity)} is "
f"{hl(f'stale since {hl(int(stale_time.seconds / 60))}')}min | "
f"last update: {last_updated}",
icon=ICONS["stale"],
)
# send notification
notify = self.cfg.get("notify") or check_config.get("notify")
if notify and results:
self.call_service(
str(notify).replace(".", "/"),
message=f"{APP_ICON} Stalled entities ({len(results)}): "
f"{', '.join([str(await self._name(entity, notification=True)) for entity in results])}",
)
# update hass sensor
if "hass_sensor" in self.cfg and self.cfg["hass_sensor"]:
await self.update_sensor("stale", results)
self._print_result("stale", results, "stalled updates")
def choose_notify_recipient(self, check: str, config: Dict[str, Any]) -> None:
if "notify" in config and "notify" not in self.cfg:
self.cfg[check]["notify"] = config["notify"]
async def last_update(self, entity_id: str) -> Any:
return self.convert_utc(await self.get_state(entity_id=entity_id, attribute="last_updated"))
async def _name(self, entity: str, friendly_name: bool = False, notification: bool = False) -> Optional[str]:
name: Optional[str] = None
if self.cfg["show_friendly_name"]:
name = await self.friendly_name(entity)
else:
name = entity
if notification is False and name:
name = hl_entity(name)
return name
def _print_result(self, check: str, entities: List[str], reason: str) -> None:
if entities:
self.lg(f"{hl(f'{len(entities)} entities')} with {hl(reason)}!", icon=APP_ICON, level="DEBUG")
else:
self.lg(f"{hl(f'no entities')} with {hl(reason)}!", icon=APP_ICON)
async def update_sensor(self, check_name: str, entities: List[str]) -> None:
if check_name not in CHECKS:
self.lg(
f"Unknown check: {hl(f'no entities')} - {self.cfg['hass_sensor']} not updated!",
icon=APP_ICON,
level="ERROR",
)
if len(self.sensor_attrs[check_name]) != len(entities):
self.sensor_attrs[check_name] = entities
self.sensor_state = sum([len(self.sensor_attrs[check]) for check in CHECKS])
self.set_state(self.cfg["hass_sensor"], state=self.sensor_state, attributes=self.sensor_attrs)
self.lg(
f"{hl_entity(self.cfg['hass_sensor'])} -> {hl(self.sensor_state)} "
f"| {', '.join([f'{hl(k) if k == check_name else k}: {hl(len(v))}' for k, v in self.sensor_attrs.items() if type(v) == list])}",
icon=APP_ICON,
level="INFO",
)
def show_info(self, config: Optional[Dict[str, Any]] = None) -> None:
# check if a room is given
if config:
self.config = config
if not self.config:
self.lg("no configuration available", icon="‼️", level="ERROR")
return
room = ""
if "room" in self.config:
room = f" - {hl(self.config['room'].capitalize())}"
self.lg("")
self.lg(f"{hl(APP_NAME)} v{hl(__version__)}{room}", icon=self.icon)
self.lg("")
listeners = self.config.pop("listeners", None)
for key, value in self.config.items():
# hide "internal keys" when displaying config
if key in ["module", "class"] or key.startswith("_"):
continue
if isinstance(value, list):
self.print_collection(key, value, 2)
elif isinstance(value, dict):
self.print_collection(key, value, 2)
else:
self._print_cfg_setting(key, value, 2)
if listeners:
self.lg(" event listeners:")
for listener in sorted(listeners):
self.lg(f" - {hl(listener)}")
self.lg("")
def print_collection(self, key: str, collection: Iterable[Any], indentation: int = 2) -> None:
self.log(f"{indentation * ' '}{key}:")
indentation = indentation + 2
for item in collection:
indent = indentation * " "
if isinstance(item, dict):
if "name" in item:
self.print_collection(item.pop("name", ""), item, indentation)
else:
self.log(f"{indent}{hl(pformat(item, compact=True))}")
elif isinstance(collection, dict):
self._print_cfg_setting(item, collection[item], indentation)
else:
self.log(f"{indent}- {hl(item)}")
def _print_cfg_setting(self, key: str, value: Union[int, str], indentation: int) -> None:
unit = prefix = ""
indent = indentation * " "
# legacy way
if key == "delay" and isinstance(value, int):
unit = "min"
min_value = f"{int(value / 60)}:{int(value % 60):02d}"
self.log(f"{indent}{key}: {prefix}{hl(min_value)}{unit} ~≈ " f"{hl(value)}sec")
else:
if "_units" in self.config and key in self.config["_units"]:
unit = self.config["_units"][key]
if "_prefixes" in self.config and key in self.config["_prefixes"]:
prefix = self.config["_prefixes"][key]
self.log(f"{indent}{key}: {prefix}{hl(value)}{unit}")
================================================
FILE: alarmClock/alarmClock.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
import math
#
# Alarm Clock App
#
#
# Args:
# alarm_time: entity which holds the alarm time. example: sensor.alarm_time
# wakemeup: entity which enables the alarm. example: input_boolean.wakemeup
# naturalwakeup: entity which enables the natural wake up fade in. example: input_number.alarm_natural_wakeup_fade_in
# alarmweekday: entity which enables alarm only on weekdays. example: input_boolean.alarmweekday
# radiowakeup: entity which enables radio wake up. example: input_boolean.radiowakeup
# TODO radioplayer: entity which holds the information which radio player to select. example: input_select.wakeup_radioplayer
# wakeup_light: light to fade in. example: light.bedroom_yeelight
# isweekday: entity which holds the information whether today is a week day. example: binary_sensor.workday_today
# notify_name: Who to notify. example: group_notifications
# message: localized message to use in notification. e.g. "You left open {} Dummy."
#
# Release Notes
#
# Version 1.4:
# Catch unknown
#
# Version 1.3.1:
# Use consistent message variable
#
# Version 1.3:
# Use new formatted alarm_time
#
# Version 1.2:
# use Notify App
#
# Version 1.1:
# message now directly in own yaml instead of message module
#
# Version 1.0:
# Initial Version
class AlarmClock(hass.Hass):
def initialize(self):
self.timer_handle_list = []
self.listen_event_handle_list = []
self.listen_state_handle_list = []
self.alarm_time = self.args["alarm_time"]
self.wakemeup = self.args["wakemeup"]
self.naturalwakeup = self.args["naturalwakeup"]
self.alarmweekday = self.args["alarmweekday"]
self.radiowakeup = self.args["radiowakeup"]
self.isweekday = self.args["isweekday"]
self.notify_name = self.args["notify_name"]
self.wakeup_light = self.args["wakeup_light"]
self.fade_in_time_multiplicator = self.args["fade_in_time_multiplicator"]
self.message = self.args["message"]
self.button = self.args["button"]
self.notifier = self.get_app("Notifier")
self.brightness = 100
self.rgb_color = [255, 120, 0]
self.alarm_timer = None
self.cached_alarm_time = self.get_state(self.alarm_time)
self.cached_fade_in_time = self.get_state(self.naturalwakeup)
self.add_timer()
self.listen_state_handle_list.append(
self.listen_state(self.alarm_change, self.alarm_time)
)
self.listen_state_handle_list.append(
self.listen_state(self.naturalwakeup_change, self.naturalwakeup)
)
self.listen_event_handle_list.append(
self.listen_event(self.button_clicked, "xiaomi_aqara.click")
)
def alarm_change(self, entity, attributes, old, new, kwargs):
if new is not None and new != old and new != self.cached_alarm_time:
if self.alarm_timer is not None:
if self.alarm_timer in self.timer_handle_list:
self.timer_handle_list.remove(self.alarm_timer)
self.cancel_timer(self.alarm_timer)
self.log("Alarm time change: {}".format(new))
self.cached_alarm_time = new
self.add_timer()
def naturalwakeup_change(self, entity, attributes, old, new, kwargs):
if new is not None and new != old and new != self.cached_fade_in_time:
if self.alarm_timer is not None:
if self.alarm_timer in self.timer_handle_list:
self.timer_handle_list.remove(self.alarm_timer)
self.cancel_timer(self.alarm_timer)
self.log("Fade-In time change: {}".format(new))
self.cached_fade_in_time = new
self.add_timer()
def add_timer(self):
self.log("cached_alarm_time: {}".format(self.cached_alarm_time))
self.log("cached_fade_in_time: {}".format(self.cached_fade_in_time))
offset = self.cached_fade_in_time.split(".", 1)[0]
if (
self.cached_alarm_time is not None
and self.cached_alarm_time != ""
and self.cached_alarm_time != "unknown"
):
run_datetime = datetime.datetime.strptime(
self.cached_alarm_time, "%Y-%m-%d %H:%M:%S"
)
event_time = run_datetime - datetime.timedelta(minutes=int(offset))
try:
self.alarm_timer = self.run_at(self.trigger_alarm, event_time)
self.timer_handle_list.append(self.alarm_timer)
self.log("Alarm will trigger at {}".format(event_time))
except ValueError:
self.log("New trigger time would be in the past: {}".format(event_time))
def trigger_alarm(self, kwargs):
if self.get_state(self.wakemeup) == "on":
if self.get_state(self.alarmweekday) == "off" or (
self.get_state(self.alarmweekday) == "on"
and self.get_state(self.isweekday) == "on"
):
if float(self.cached_fade_in_time) > 0:
self.log(
"Turning on {}".format(self.friendly_name(self.wakeup_light))
)
self.call_service(
"light/turn_on", entity_id=self.wakeup_light, brightness_pct=1
)
transition = int(
float(self.cached_fade_in_time)
* int(self.fade_in_time_multiplicator)
)
self.log(
"Transitioning light in over {} seconds".format(transition)
)
self.timer_handle_list.append(
self.run_in(
self.run_fade_in, 1, transition=transition, brightness_pct=1
)
)
self.timer_handle_list.append(
self.run_in(self.run_alarm, float(self.cached_fade_in_time))
)
def button_clicked(self, event_name, data, kwargs):
"""Extra callback method to trigger the wakeup light on demand by pressing a Xiaomi Button"""
if data["entity_id"] == self.button:
if data["click_type"] == "single":
if float(self.cached_fade_in_time) > 0:
self.log(
"Turning on {}".format(self.friendly_name(self.wakeup_light))
)
self.call_service(
"light/turn_on", entity_id=self.wakeup_light, brightness_pct=1
)
transition = int(
float(self.cached_fade_in_time)
* int(self.fade_in_time_multiplicator)
)
self.log(
"Transitioning light in over {} seconds".format(transition)
)
self.timer_handle_list.append(
self.run_in(
self.run_fade_in, 1, transition=transition, brightness_pct=1
)
)
def run_fade_in(self, kwargs):
"""
Callback / recursion style because the transition feature does not seem to work well with Yeelight for
transition values greater than 10s.
:param kwargs:
:return:
"""
wait_factor = 1
transition = kwargs["transition"]
brightness_pct = kwargs["brightness_pct"]
pct_increase = 1 / transition
self.log("pct_increase: {}".format(pct_increase), level="DEBUG")
if pct_increase < 0.01:
wait_factor = math.ceil(0.01 / pct_increase)
pct_increase = 0.01
self.log(
"pct_increase smaller than 1% next run_in in {} seconds".format(
wait_factor
),
level="DEBUG",
)
brightness_pct_old = brightness_pct
self.log("brightness_pct_old: {}".format(brightness_pct_old), level="DEBUG")
brightness_pct_new = int((brightness_pct_old + pct_increase * 100))
self.log("brightness_pct_new: {}".format(brightness_pct_new), level="DEBUG")
if brightness_pct_new < 100:
self.call_service(
"light/turn_on",
entity_id=self.wakeup_light,
rgb_color=self.rgb_color,
brightness_pct=brightness_pct_new,
)
self.timer_handle_list.append(
self.run_in(
self.run_fade_in,
wait_factor,
transition=transition,
brightness_pct=brightness_pct_new,
)
)
def run_alarm(self, kwargs):
self.notifier.notify(self.notify_name, self.message)
# TODO radio
def terminate(self):
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
for listen_event_handle in self.listen_event_handle_list:
self.cancel_listen_event(listen_event_handle)
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: alarmClock/alarmClock.yaml
================================================
# Alarm Clock App
# alarmClock:
# module: alarmClock
# class: AlarmClock
# alarm_time: sensor.alarm_time
# wakemeup: input_boolean.wakemeup
# naturalwakeup: input_number.alarm_natural_wakeup_fade_in
# alarmweekday: input_boolean.alarmweekday
# radiowakeup: input_boolean.radiowakeup
# #TODO radioplayer: input_select.wakeup_radioplayer
# wakeup_light: light.bedroom_yeelight
# fade_in_time_multiplicator: 60
# isweekday: binary_sensor.workday_today
# notify_name: group_notifications
# message: "Guten Morgen!"
# #message: "Good Morning!"
# button: binary_sensor.switch_158d000215aa28
# dependencies:
# - Notifier
================================================
FILE: alexa/README.md
================================================
# Alexa Intents
Intents for [Alexa-Appdaemon-App](https://github.com/ReneTode/Alexa-Appdaemon-App) from [Rene Tode](https://github.com/ReneTode).
To set it up for yourself follow [this](https://github.com/ReneTode/Alexa-Appdaemon-App/blob/master/alexa%20skill%20tutorial.md) tutorial
## listService
Supply friendly names and known entities for other alexa skills.
This is needed as this is a custom Alexa App and has nothing to do with HA Cloud / Alexa integration
## turnEntityOffInX
Ask Alexa to turn something off in a set amount of minutes.
Only works with entities defined under *switchable* in [listService.yaml](https://github.com/eifinger/appdaemon-scripts/blob/master/alexa/listService/listService.yaml)
``Alexa tell Home Assistant to turn off Ventilator in 10 Minutes``
```yaml
turnEntityOffInXIntent:
module: turnEntityOffInXIntent
class: TurnEntityOffInXIntent
language: DE
textLine: "Okay Homeassistant schaltet {{device}} in"
Error: <p>Ich habe nicht richtig verstanden welches geraet soll ich ausschalten?</p>
unreadableState: "unlesbar fuer mich"
dependencies:
- listService
```
## windowsOpen
Will tell you if any windows / doors are open and/or tilted
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)
``Alexa ask Home Assistant whether all windows are closed``
```yaml
windowsOpenIntent:
module: windowsOpenIntent
class: WindowsOpenIntent
language: DE
textLineClosed: "Alle Fenster und Türen sind zu"
#textLineClosed: "All windows and doors are closed"
textLineWindowOpen: "Folgende Fenster sind noch offen"
#textLineWindowOpen: "The following windows are stil open..."
textLineDoorOpen: "Folgende Türen sind noch offen"
#textLineDoorOpen: "The following doors are still open"
textLineDoorTilted: "Die folgenden Türen sind noch gekippt"
#textLineDoorTilted: "The following doors are tilted"
Error: <p>Ich habe dich nicht richtig verstanden</p>
unreadableState: "unlesbar fuer mich"
dependencies:
- listService
```
## nextBusIntent
Will tell you the next departure of a bus/train of a [RMV](https://www.home-assistant.io/components/sensor.rmvtransport/) sensor
``Alexa ask Home Assistant when the next bus departs``
```yaml
nextBusIntent:
module: nextBusIntent
class: nextBusIntent
textLine: "Linie {} fährt in {} Minuten"
#textLine: "Line {} departs in {} minutes"
Error: <p>Ich habe nicht richtig verstanden was du meinst</p>
sensor: sensor.nach_bruckenkopf
global_dependencies:
- globals
```
## remindMeOfXWhenZoneIntent
CURRENTLY DOES NOT WORK BECAUSE ALEXA DOES NOT ALLOW INTENTS CONTAINING REMINDERS
Will send you a reminder over the notification service when you leave/enter a zone.
``Alexa tell Home Assistant to remind me of <> when entering work``
```yaml
remindMeOfXWhenZoneIntent:
module: remindMeOfXWhenZoneIntent
class: RemindMeOfXWhenZoneIntent
device_tracker: person.kevin
notify_name: group_notifications
Error: <p>Es ist etwas schief gegangen</p>
textLine: "Okay ich erinnere dich an {{reminder}} wenn du {{zone}} "
textEnter: "erreichst"
textLeave: "verlässt"
remindMessageSkeleton: "Ich sollte dich erinnern an "
zoneMapping:
arbeit: work
hause: home
elmo: elmo
dependencies:
- Notifier
global_dependencies:
- globals
```
================================================
FILE: alexa/alexa.yaml
================================================
alexa_api: # appdaemon skill
module: alexa_api
class: alexa_api
cardTitle: Your Card Title
devices:
unknownDevice: ein unbekannte platze
logfile: /conf/logs/alexaAD.log
responselogfile: /conf/logs/alexaResponse.log
language: DE
temperatureUnit: "Grad"
logging: "True"
launchRequest:
- <p><say-as interpret-as='interjection'>bonjour</say-as>, bist du wieder in {{device}}?</p> <p>wie kann ich dir helfen?</p>
- <p><say-as interpret-as='interjection'>aloha</say-as>, wie gehts es in {{device}}?</p> <p>kann ich etwas fuer dich tun?</p>
- <p><say-as interpret-as='interjection'>hey</say-as>, du bist wieder in {{device}}.</p> <p>was kan ich machen?</p>
- <p><say-as interpret-as='interjection'>moin</say-as>. Schoen das du wieder im {{device}} bist,</p> <p>was ist dein wunsch?</p>
- <say-as interpret-as='interjection'>mazzel tov</say-as>. <p>Ich sage hallo zu alle in {{device}}</p><p>Kann ich etwas fuer dich tun?</p>
nextConversationQuestion:
- <p>Was kann ich als naechstes fuer dich tun?</p>
- <p>Mit was willst du das ich dir helfe? </p>
intentEnd:
- <p>Kann ich noch etwas fuer dich machen?</p>
- <p>Willst du das ich noch etwas anderes mache?</p>
- <p>Kann ich dir mit noch etwas helfen?</p>
- <p>Gibt es noch etwas was ich fuer dich tun kann?</p>
conversationEnd:
- Bis zum naechsten mal
- <say-as interpret-as='interjection'>arrivederci</say-as>
- <say-as interpret-as='interjection'>bis bald</say-as>
- <say-as interpret-as='interjection'>bis dann</say-as>
- <say-as interpret-as='interjection'>mach's gut</say-as>
- <say-as interpret-as='interjection'>tschoe</say-as>
- <say-as interpret-as='interjection'>viel glueck</say-as>
- Schoen mit dir gesprochen zu haben
responseError:
- Es tut mir leit aber etwas ist falsch gegangen
- Leider meldet home assistant einen Fehler
================================================
FILE: alexa/alexa_api.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
import datetime
class alexa_api(hass.Hass):
def initialize(self):
self.register_endpoint(self.api_call)
self.tempCardContent = ""
def api_call(self, data):
self.my_alexa_interpret_data(data)
response = ""
dialogDelegate = False
endsession = False
speech = None
card = None
logtext = ""
if self.dialog == None:
if self.requesttype == "LaunchRequest":
speech = self.random_arg(self.args["launchRequest"])
endSession = False
dialogDelegate = False
card = None
self.alexalog("Launch request (activate command)", 100, "*")
elif self.requesttype == "IntentRequest":
speech = None
endSession = False
dialogDelegate = True
card = None
self.alexalog("start dialog", 100, "*")
elif self.requesttype == "SessionEndedRequest":
speech = None
endSession = False
dialogDelegate = False
card = None
self.alexalog("The session has ended", 100, "*")
else:
self.alexalog("Strange state", 100, "*")
elif self.dialog == "STARTED":
############################################
# conversation started, just response dialogDelegate
############################################
speech = None
endSession = False
dialogDelegate = True
card = None
self.alexalog("dialog has been started", 100, "*")
elif self.dialog == "IN_PROGRESS":
speech = None
endSession = False
dialogDelegate = True
card = None
self.alexalog("dialog is in progress", 100, "*")
elif self.dialog == "COMPLETED":
try:
intentResponse = self.getIntentResponse()
except:
intentResponse = self.random_arg(self.args["responseError"])
if intentResponse == "stop":
############################################
# user used stop intent, stop conversation
############################################
speech = self.random_arg(self.args["conversationEnd"])
endSession = True
dialogDelegate = False
card = True
cardContent = self.tempCardContent
self.tempCardContent = ""
self.alexalog(
"dialog has been completed, Dialog stopped by user", 100, "*"
)
elif intentResponse == "next":
############################################
# user just responded yes, so just a question
############################################
speech = self.random_arg(self.args["nextConversationQuestion"])
endSession = False
dialogDelegate = False
card = None
self.tempCardContent = self.tempCardContent + self.intentname + "\n"
self.alexalog(
"dialog has been completed, User just responded yes", 100, "*"
)
else:
############################################
# Send the response from the Intent + question for next action
############################################
speech = intentResponse
endSession = True
dialogDelegate = False
card = None
self.tempCardContent = self.tempCardContent + self.intentname + "\n"
self.alexalog("dialog has been completed, Response " + speech, 100, "*")
if speech != None:
speech = self.cleanup_text(speech)
if card:
response = self.my_alexa_response(
EndSession=endSession,
DialogDelegate=dialogDelegate,
speech=speech,
card=True,
title=self.args["cardTitle"],
content=cardContent,
)
self.alexalog(" ", 100, "X")
self.alexalog(" ", 100, "X")
self.alexalog(" ", 100, "X")
else:
response = self.my_alexa_response(
EndSession=endSession,
DialogDelegate=dialogDelegate,
speech=speech,
card=None,
)
if speech != None:
if self.intentname == None:
self.alexaresponselog("No Intent : " + speech)
else:
self.alexaresponselog(self.intentname + " : " + speech)
return response, 200
def my_alexa_interpret_data(self, data):
############################################
# create vars from the data
############################################
self.allslots = {}
self.slots = {}
self.dialog = self.my_alexa_dialog_state(data)
self.requesttype = self.my_alexa_request_type(data)
self.alexa_error = self.my_alexa_error(data)
self.intentname = self.my_alexa_intent_name(data)
if self.intentname != None:
if "." in self.intentname:
splitintent = self.intentname.split(".")
self.intentname = splitintent[1]
device = data["context"]["System"]["device"]["deviceId"]
self.devicename = self.args["devices"]["unknownDevice"]
for devicename, deviceid in self.args["devices"].items():
if deviceid == device:
self.devicename = devicename
if self.devicename == self.args["devices"]["unknownDevice"]:
self.log(device)
############################################
# get slots out of the data
############################################
if (
"request" in data
and "intent" in data["request"]
and "slots" in data["request"]["intent"]
):
self.allslots = data["request"]["intent"]["slots"]
slottext = "slots: "
for slot, slotvalue in self.allslots.items():
if "value" in slotvalue:
self.slots[slot] = slotvalue["value"].lower()
else:
self.slots[slot] = ""
if self.intentname == "searchYoutubeIntent":
self.slots["search"] = (
self.slots["searchfielda"]
+ self.slots["searchfieldb"]
+ self.slots["searchfieldc"]
+ self.slots["searchfieldd"]
)
############################################
# log that data came in
############################################
self.alexalog("data came in.", 100, "#")
self.alexalog(
"dialogstate = "
+ str(self.dialog)
+ " and requesttype = "
+ str(self.requesttype)
)
self.alexalog("intent = " + str(self.intentname))
slottext = "slots: "
for slot, slotvalue in self.slots.items():
slottext = slottext + slot + "= " + str(slotvalue) + ", "
self.alexalog(slottext)
self.alexalog("error = " + str(self.alexa_error))
self.alexalog(" ", 100, "#")
def my_alexa_intent_name(self, data):
############################################
# find the intent name in the data
############################################
if (
"request" in data
and "intent" in data["request"]
and "name" in data["request"]["intent"]
):
return data["request"]["intent"]["name"]
else:
return None
def my_alexa_dialog_state(self, data):
############################################
# find the dialog state in the data
############################################
if "request" in data and "dialogState" in data["request"]:
return data["request"]["dialogState"]
else:
return None
def my_alexa_intent(self, data):
############################################
# find the requesttype in the data
############################################
if "request" in data and "intent" in data["request"]:
return data["request"]["intent"]
else:
return None
def my_alexa_request_type(self, data):
############################################
# find the requesttype in the data
############################################
if "request" in data and "type" in data["request"]:
return data["request"]["type"]
else:
return None
def my_alexa_error(self, data):
############################################
# get an error out of the data
############################################
if (
"request" in data
and "error" in data["request"]
and "message" in data["request"]["error"]
):
return data["request"]["error"]["message"]
else:
return None
def my_alexa_slot_value(self, data, slot):
############################################
# get a slot value from the data
############################################
if (
"request" in data
and "intent" in data["request"]
and "slots" in data["request"]["intent"]
and slot in data["request"]["intent"]["slots"]
and "value" in data["request"]["intent"]["slots"][slot]
):
return data["request"]["intent"]["slots"][slot]["value"]
else:
return ""
def my_alexa_response(
self,
EndSession=False,
DialogDelegate=False,
speech=None,
card=None,
title=None,
content=None,
):
############################################
# put the speechfield from the response toghether
############################################
response = {"shouldEndSession": EndSession}
if DialogDelegate:
response["directives"] = [
{"type": "Dialog.Delegate", "updatedIntent": None}
]
if speech is not None:
response["outputSpeech"] = {
"type": "SSML",
"ssml": "<speak>" + speech + "</speak>",
}
if card is not None:
response["card"] = {"type": "Simple", "title": title, "content": content}
speech = {"version": "1.0", "response": response, "sessionAttributes": {}}
return speech
def random_arg(self, argName):
############################################
# pick a random text from a list
############################################
if isinstance(argName, list):
text = random.choice(argName)
else:
text = argname
return text
def floatToStr(self, myfloat):
############################################
# replace . with , for better speech
############################################
floatstr = str(myfloat)
floatstr = floatstr.replace(".", ",")
return floatstr
def cleanup_text(self, text):
############################################
# replace some text like temperary slots with its value
############################################
# self.log(text)
for slotname, slotvalue in self.slots.items():
text = text.replace("{{" + slotname + "}}", slotvalue)
text = text.replace("{{device}}", self.devicename)
text = text.replace("_", " ")
text = text.replace("...", "<break time='2s'/>")
return text
def alexalog(self, logtext, repeat=0, surrounding=""):
############################################
# put an entry in the alexa log
############################################
if self.args["logging"] == "true" or self.args["logging"] == "True":
runtime = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S.%f")
if repeat > 0:
surrounding = surrounding * repeat
try:
log = open(self.args["logfile"], "a")
if logtext == " ":
log.write(runtime + "; " + surrounding + "\n")
else:
if surrounding != "":
log.write(runtime + "; " + surrounding + "\n")
log.write(runtime + "; " + logtext + "\n")
if surrounding != "":
log.write(runtime + "; " + surrounding + "\n")
log.close()
except:
self.log("ALEXA LOGFILE CANT BE WRITTEN!!")
def alexaresponselog(self, logtext):
############################################
# put an entry in the alexa responses log
############################################
if self.args["logging"] == "true" or self.args["logging"] == "True":
runtime = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S.%f")
try:
log = open(self.args["responselogfile"], "a")
log.write(runtime + "; " + logtext + "\n")
log.close()
except:
self.log("ALEXA RESPONSELOGFILE CANT BE WRITTEN!!")
def getIntentResponse(self):
############################################
# perform the intent end get the response back
# from the intent apps
############################################
if self.intentname == "StopIntent":
return "stop"
if self.intentname == "yesIntent":
return "next"
alexa = self.get_app(self.intentname)
return alexa.getIntentResponse(self.slots, self.devicename)
================================================
FILE: alexa/custom_skill.json
================================================
{
"interactionModel": {
"languageModel": {
"invocationName": "home assistant",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "temperatureStateIntent",
"slots": [
{
"name": "location",
"type": "room",
"samples": [
"Vom Schlafzimmer",
"Vom Wohnzimmer",
"Wohnzimmer"
]
}
],
"samples": [
"wie viel grad ist es in {location}",
"was ist die temperatur in {location}"
]
},
{
"name": "lightStateIntent",
"slots": [
{
"name": "device",
"type": "device",
"samples": [
"Von {device}",
"{device}"
]
}
],
"samples": [
"Ist {device} aus",
"Ist {device} an",
"Was ist der Status von {device}"
]
},
{
"name": "turnEntityOffInXIntent",
"slots": [
{
"name": "device",
"type": "device",
"samples": [
"die {device}",
"der {device}",
"Das {device}",
"{device}"
]
},
{
"name": "duration",
"type": "AMAZON.DURATION",
"samples": [
"In {duration}"
]
}
],
"samples": [
"In {duration} {device} ausschalten",
"{device} in {duration} ausschalten",
"er soll {device} in {duration} ausschalten",
"es soll {device} in {duration} ausschalten",
"Schalte {device} in {duration} aus"
]
},
{
"name": "windowsOpenIntent",
"slots": [],
"samples": [
"ob alles zu ist",
"ob noch etwas offen ist",
"ist noch etwas offen",
"ist alles zu",
"sind alle türen zu",
"ob noch türen offen sind",
"ob alle türen zu sind",
"ob alle fenster zu sind",
"ob noch Fenster offen sind",
"ob die Fenster zu sind",
"Sind die Fenster zu",
"Sind alle Fenster zu",
"Welche Fenster sind offen"
]
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
}
],
"types": [
{
"name": "room",
"values": [
{
"name": {
"value": "oben"
}
},
{
"name": {
"value": "küche"
}
},
{
"name": {
"value": "schlafzimmer"
}
},
{
"name": {
"value": "wohnzimmer"
}
}
]
},
{
"name": "device",
"values": [
{
"name": {
"value": "Deckenlampe"
}
},
{
"name": {
"value": "Ventilator"
}
},
{
"name": {
"value": "Wohnzimmer Temperatur"
}
}
]
}
]
},
"dialog": {
"intents": [
{
"name": "temperatureStateIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "location",
"type": "room",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.748362018084.1289245209680"
}
}
]
},
{
"name": "lightStateIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "device",
"type": "device",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.314395877784.987687730608"
}
}
]
},
{
"name": "turnEntityOffInXIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "device",
"type": "device",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.208682348144.1260609041167"
}
},
{
"name": "duration",
"type": "AMAZON.DURATION",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.208682348144.1375070683830"
}
}
]
}
]
},
"prompts": [
{
"id": "Elicit.Slot.748362018084.1289245209680",
"variations": [
{
"type": "PlainText",
"value": "Von welchem Raum willst du die Temperatur wissen?"
}
]
},
{
"id": "Elicit.Slot.314395877784.987687730608",
"variations": [
{
"type": "PlainText",
"value": "Von was willst du den Status wissen?"
},
{
"type": "PlainText",
"value": "Entschuldigung. Welches Gerät meintest du?"
}
]
},
{
"id": "Elicit.Slot.208682348144.1260609041167",
"variations": [
{
"type": "PlainText",
"value": "Entschuldigung. Welches Gerät soll ich ausschalten?"
},
{
"type": "PlainText",
"value": "Welches Gerät meinst du?"
}
]
},
{
"id": "Elicit.Slot.208682348144.1375070683830",
"variations": [
{
"type": "PlainText",
"value": "Entschuldigung. Wann soll ich {device} ausschalten?"
},
{
"type": "PlainText",
"value": "Wann soll ich {device} ausschalten?"
}
]
}
]
}
}
================================================
FILE: alexa/lightState/lightStateIntent-utterances_DE.csv
================================================
Ist {device} aus
Ist {device} an
Was ist der Status von {device}
================================================
FILE: alexa/lightState/lightStateIntent-utterances_EN.csv
================================================
Is {device} off
Is {device} on
What is the state of {device}
================================================
FILE: alexa/lightState/lightStateIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
class lightStateIntent(hass.Hass):
def initialize(self):
return
def getIntentResponse(self, slots, devicename):
############################################
# an Intent to give back the state from a light.
# but it also can be any other kind of entity
############################################
try:
entityname = self.args["entities"][slots["device"]]
state = self.get_state(entityname)
if isinstance(state, float):
if self.args["language"] == "DE":
state = self.floatToStr(state)
else:
state = str(state)
elif isinstance(state, str):
state = state
else:
state = self.args["unreadableState"]
text = self.random_arg(self.args["textLine"]) + state
except:
text = self.random_arg(self.args["Error"])
return text
def floatToStr(self, myfloat):
############################################
# replace . with , for better speech
############################################
floatstr = str(myfloat)
floatstr = floatstr.replace(".", ",")
return floatstr
================================================
FILE: alexa/lightState/lightStateIntent.yaml
================================================
lightStateIntent:
module: lightStateIntent
class: lightStateIntent
language: DE
temperatureUnit: "Grad"
textLine:
- "Der Status von {{device}} ist "
- "{{device}} ist in diesem Moment "
- "{{device}} ist "
Error: <p>Ich habe nicht richtig verstanden welches geraet oder sensor du wissen wolltest</p>
unreadableState: "unlesbar fuer mich"
entities:
wohnzimmer temperatur: sensor.large_lamp_temperature
deckenlampe: light.livingroom_yeelight
ventilator: switch.ventilator
================================================
FILE: alexa/listService/listService.py
================================================
import appdaemon.plugins.hass.hassapi as hass
#
# Provide the list of HA entities for Alexa Apps
#
#
# Args:
#
# switchable: dict of switchable entities
# temperature: dict of temperature sensors
# door: dict of reed sensors showing if the door is completely open
# door_tilted: dict of reed sensors showing if a door is partially/leaning open
# window: dict of reed sensors showing if a window is open
#
#
# Release Notes
#
# Version 1.0:
# Initial Version
class ListService(hass.Hass):
def initialize(self):
return
def getSwitchable(self):
return self.args["switchable"]
def getTemperature(self):
return self.args["temperature"]
def getDoor(self):
return self.args["door"]
def getWindow(self):
return self.args["window"]
def getDoorTilted(self):
return self.args["door_tilted"]
================================================
FILE: alexa/listService/listService.yaml
================================================
listService:
module: listService
class: ListService
switchable:
große lampe: switch.large_lamp
kleine lampe: switch.small_lamp
deckenlampe: light.livingroom_yeelight
ventilator: switch.ventilator
großer ventilator: switch.large_ventilator
markise: switch.markise
fernseher: switch.tv
schlafzimmer receiver: switch.bedroom_receiver
snowboard: switch.snowboard
receiver: media_player.denon_avr_x1300w
wohnzimmer: group.livingroom
wohnung: group.all
schlafzimmer lampe: light.bedroom_yeelight
schlafzimmer: light.bedroom_yeelight
bar: light.bar_table
bar licht: light.bar_table
treppe unten: light.stairs_lower_yeelight
treppe oben: light.treppe_oben
leselampe: light.reading_lamp_yeelight
lese lampe: light.reading_lamp_yeelight
runde lampe: light.reading_lamp_yeelight
garderobe: light.lobby_yeelight
garderoben licht: light.lobby_yeelight
garderobenlicht: light.lobby_yeelight
flur: switch.lobby
flur licht: switch.lobby
bad: light.lower_bathroom_yeelight
bad licht: light.lower_bathroom_yeelight
treppe: group.stair_lights
treppen licht: group.stair_lights
treppenlicht: group.stair_lights
temperature:
große lampe thermostat: sensor.large_lamp_temperature
kleine lampe thermostat: sensor.small_lamp_temperature
ventilator thermostat: sensor.ventilator_temperature
großer ventilator thermostat: sensor.large_ventilator_temperature
door:
wohnungstuer: binary_sensor.contact_door
terassentuer schlafzimmer: binary_sensor.contact_bedroom_door
terassentuer: binary_sensor.contact_terrace_door
terassentuer arbeitszimmer: binary_sensor.contact_studyroom_door
window:
kuechenfenster: binary_sensor.contact_kitchen_window
badfenster oben: binary_sensor.contact_upper_bathroom_window
badfenster unten: binary_sensor.contact_lower_bathroom_window
gaestezimmerfenster: binary_sensor.contact_guestroom_window
door_tilted:
kuechenfenster gekippt: binary_sensor.contact_kitchen_window_tilted
terassentuer arbeitszimmer gekippt: binary_sensor.contact_studyroom_door_tilted
terassentuer schlafzimmer gekippt: binary_sensor.contact_bedroom_door_tilted
terrassentuer gekippt: binary_sensor.contact_terrace_door_tilted
badfenster unten gekippt: binary_sensor.contact_lower_bathroom_window_tilted
badfenster oben gekippt: binary_sensor.contact_upper_bathroom_window_tilted
gaestezimmerfenster gekippt: binary_sensor.contact_guestroom_window_tilted
================================================
FILE: alexa/nextBus/nextBusIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
class nextBusIntent(hass.Hass):
def initialize(self):
self.sensor = self.args["sensor"]
self.textLine = self.args["textLine"]
self.error = self.args["error"]
def getIntentResponse(self, slots, devicename):
############################################
# give next bus departure
############################################
try:
state = self.get_state(self.sensor, attribute="all")
line = state["attributes"]["line"]
minutes = state["attributes"]["minutes"]
text = self.textLine.format(line, minutes)
except:
text = self.error
return text
================================================
FILE: alexa/nextBus/nextBusIntent.yaml
================================================
nextBusIntent:
module: nextBusIntent
class: nextBusIntent
textLine: "Linie {} fährt in {} Minuten"
#textLine: "Line {} departs in {} minutes"
error: <p>Ich habe nicht richtig verstanden was du meinst</p>
sensor: sensor.nach_bruckenkopf
================================================
FILE: alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
__ZONE_ACTION_ENTER__ = "kommen"
__ZONE_ACTION_LEAVE__ = "verlassen"
class RemindMeOfXWhenZoneIntent(hass.Hass):
def initialize(self):
self.timer_handle_list = []
self.listen_state_handle_list = []
self.device_tracker = self.args["device_tracker"]
self.notify_name = self.args["notify_name"]
self.remindMessageSkeleton = self.args["remindMessageSkeleton"]
self.notifier = self.get_app("Notifier")
return
def getIntentResponse(self, slots, devicename):
############################################
# an Intent to give back the state from a light.
# but it also can be any other kind of entity
############################################
try:
# get zone_name for friendly name used when talking to alexa
zone_name = None
for key, value in self.args["zoneMapping"].items():
if key == slots["zone"].lower():
zone_name = value
# listen to a state change of the zone
if zone_name == None:
raise Exception(
"Could not find zonemapping for: {}".format(slots["zone"].lower())
)
else:
self.listen_state_handle_list.append(
self.listen_state(
self.remind_callback,
self.device_tracker,
zone=slots["zone"],
zoneAction=slots["zoneAction"],
reminder=slots["reminder"],
)
)
# set correct zoneAction response
if slots["zoneAction"] == __ZONE_ACTION_ENTER__:
text = self.args["textLine"] + self.args["textEnter"]
else:
text = self.args["textLine"] + self.args["textLeave"]
except Exception as e:
self.log("Exception: {}".format(e))
self.log("slots: {}".format(slots))
text = self.random_arg(self.args["Error"])
return text
def remind_callback(self, entity, attribute, old, new, kwargs):
if kwargs["zoneAction"] == __ZONE_ACTION_ENTER__:
if new != old and new == kwargs["zone"]:
self.log("Notifying")
self.notifier.notify(
self.notify_name,
self.remindMessageSkeleton + kwargs["reminder"],
useAlexa=False,
)
elif kwargs["zoneAction"] == __ZONE_ACTION_LEAVE__:
if new != old and old == kwargs["zone"]:
self.log("Notifying")
self.notifier.notify(
self.notify_name,
self.remindMessageSkeleton + kwargs["reminder"],
useAlexa=False,
)
def terminate(self):
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.yaml
================================================
remindMeOfXWhenZoneIntent:
module: remindMeOfXWhenZoneIntent
class: RemindMeOfXWhenZoneIntent
device_tracker: person.kevin
notify_name: group_notifications
Error: <p>Es ist etwas schief gegangen</p>
textLine: "Okay ich erinnere dich an {{reminder}} wenn du {{zone}} "
textEnter: "erreichst"
textLeave: "verlässt"
remindMessageSkeleton: "Ich sollte dich erinnern an "
zoneMapping:
arbeit: work
hause: home
elmo: elmo
dependencies:
- Notifier
================================================
FILE: alexa/temperatureState/temperatureStateIntent-utterances_DE.csv
================================================
wie viel grad ist es in {location}
was ist die temperatur in {location}
================================================
FILE: alexa/temperatureState/temperatureStateIntent-utterances_EN.csv
================================================
how many degree is it in {location}
what is the temperture in {location}
================================================
FILE: alexa/temperatureState/temperatureStateIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
class temperatureStateIntent(hass.Hass):
def initialize(self):
return
def getIntentResponse(self, slots, devicename):
############################################
# give temperature for a list of temperature sensors
############################################
try:
if self.args["language"] == "DE":
temp = (
self.floatToStr(
self.get_state(self.args["sensors"][slots["location"]])
)
+ self.args["temperatureUnit"]
)
else:
temp = (
str(self.get_state(self.args["sensors"][slots["location"]]))
+ self.args["temperatureUnit"]
)
text = self.random_arg(self.args["textLine"]) + temp
except:
text = self.random_arg(self.args["Error"])
return text
def floatToStr(self, myfloat):
############################################
# replace . with , for better speech
############################################
floatstr = str(myfloat)
floatstr = floatstr.replace(".", ",")
return floatstr
def random_arg(self, argName):
############################################
# pick a random text from a list
############################################
if isinstance(argName, list):
text = random.choice(argName)
else:
text = argName
return text
================================================
FILE: alexa/temperatureState/temperatureStateIntent.yaml
================================================
temperatureStateIntent:
module: temperatureStateIntent
class: temperatureStateIntent
language: DE
temperatureUnit: "Grad"
textLine:
- "Die Temperatur im {{location}} ist "
- "In diesem Moment ist es im {{location}} "
- "Im Moment ist die Temperatur "
Error: <p>Ich habe nicht richtig verstanden welche Temperatur du wissen wolltest</p>
sensors:
wohnzimmer: sensor.large_lamp_temperature
================================================
FILE: alexa/turnEntityOffInX/requirements.txt
================================================
isodate
================================================
FILE: alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_DE.csv
================================================
In {duration} {device} ausschalten
{device} in {duration} ausschalten
er soll {device} in {duration} ausschalten
es soll {device} in {duration} ausschalten
Schalte {device} in {duration} aus
================================================
FILE: alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_EN.csv
================================================
In {duration} turn off {device}
turn off {device} in {duration}
it should turn off {device} in {duration}
================================================
FILE: alexa/turnEntityOffInX/turnEntityOffInXIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
import isodate
import datetime
class TurnEntityOffInXIntent(hass.Hass):
def initialize(self):
self.timer_handle_list = []
self.listService = self.get_app("listService")
return
def getIntentResponse(self, slots, devicename):
############################################
# an Intent to give back the state from a light.
# but it also can be any other kind of entity
############################################
try:
entityname = self.listService.getSwitchable()[slots["device"]]
# to upper because of https://github.com/gweis/isodate/issues/52
duration = isodate.parse_duration(slots["duration"].upper())
self.timer_handle_list.append(
self.run_in(
self.turn_off_callback,
duration.total_seconds(),
entityname=entityname,
)
)
minutes, seconds = divmod(duration.total_seconds(), 60)
minutes = int(minutes)
seconds = int(seconds)
if minutes == 0:
if seconds == 1:
timeText = " einer Sekunde"
else:
timeText = " {} Sekunden".format(seconds)
elif minutes == 1:
if seconds == 1:
timeText = " einer Minute und einer Sekunde"
elif seconds == 0:
timeText = " einer Minute"
else:
timeText = " einer Minute und {} Sekunden".format(seconds)
else:
if seconds == 1:
timeText = " {} Minuten und einer Sekunde".format(minutes)
elif seconds == 0:
timeText = " {} Minuten".format(minutes)
else:
timeText = " {} Minuten und {} Sekunden".format(minutes, seconds)
text = self.args["textLine"] + timeText + " ab."
except Exception as e:
self.log("Exception: {}".format(e))
self.log("slots: {}".format(slots))
text = self.random_arg(self.args["Error"])
return text
def turn_off_callback(self, kwargs):
entityname = kwargs["entityname"]
self.log("Turning off {}".format(entityname))
self.turn_off(entityname)
def random_arg(self, argName):
############################################
# pick a random text from a list
############################################
if isinstance(argName, list):
text = random.choice(argName)
else:
text = argName
return text
def terminate(self):
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: alexa/turnEntityOffInX/turnEntityOffInXIntent.yaml
================================================
turnEntityOffInXIntent:
module: turnEntityOffInXIntent
class: TurnEntityOffInXIntent
language: DE
textLine: "Okay Homeassistant schaltet {{device}} in"
Error: <p>Ich habe nicht richtig verstanden welches geraet soll ich ausschalten?</p>
unreadableState: "unlesbar fuer mich"
dependencies:
- listService
================================================
FILE: alexa/windowsOpen/windowsOpenIntent-utterances_DE.csv
================================================
ob alles zu ist
ob noch etwas offen ist
ist noch etwas offen
ist alles zu
sind alle türen zu
ob noch türen offen sind
ob alle türen zu sind
ob alle fenster zu sind
ob noch Fenster offen sind
ob die Fenster zu sind
Sind die Fenster zu
Sind alle Fenster zu
Welche Fenster sind offen
================================================
FILE: alexa/windowsOpen/windowsOpenIntent-utterances_EN.csv
================================================
whether everything is closed
whether something is still open
is something still open
is everthing closed
are all doors closed
whether some doors are still open
whether all doors are closed
whether all windows are closed
whether some windows are open
whether the windows are closed
Are the Windows closed
Are all windows closed
Which Windows are open
================================================
FILE: alexa/windowsOpen/windowsOpenIntent.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import random
import isodate
import datetime
class WindowsOpenIntent(hass.Hass):
def initialize(self):
self.listService = self.get_app("listService")
return
def getIntentResponse(self, slots, devicename):
############################################
# an Intent to give back the state of windows
############################################
try:
windows_dict = self.listService.getWindow()
doors_dict = self.listService.getDoor()
doors_tilted_dict = self.listService.getDoorTilted()
window_open_list = []
door_open_list = []
door_tilted_list = []
# iterate over all window entities
for key, value in windows_dict.items():
# if a window is open ("on") add it to the window_open_list
if self.get_state(value) == "on":
window_open_list.append(value)
# iterate over all door entities
for key, value in doors_dict.items():
# if a door is open ("on") add it to the door_open_list
if self.get_state(value) == "on":
door_open_list.append(value)
# iterate over all door_tilted entities
for key, value in doors_tilted_dict.items():
# if a door is tilted ("on") add it to the door_tilted_list
if self.get_state(value) == "on":
door_tilted_list.append(value)
text = ""
# add open windows to response
if len(window_open_list) > 0:
if text != "":
text = text + ' <break strength="weak"/>'
text = text + self.args["textLineWindowOpen"]
for entity in window_open_list:
text = (
text + ' <break strength="weak"/>' + self.friendly_name(entity)
)
# add open doors to response
if len(door_open_list) > 0:
if text != "":
text = text + ' <break strength="weak"/>'
text = text + self.args["textLineDoorOpen"]
for entity in door_open_list:
text = (
text + ' <break strength="weak"/>' + self.friendly_name(entity)
)
# add tilted doors to reponse
if len(door_tilted_list) > 0:
if text != "":
text = text + ' <break strength="weak"/>'
text = text + self.args["textLineDoorTilted"]
for entity in door_tilted_list:
friendly_name = self.friendly_name(entity)
# remove "gekippt" (german for tilted) from the friendly name
friendly_name = friendly_name.replace(" gekippt", "")
friendly_name = friendly_name.replace(" Gekippt", "")
text = text + ' <break strength="weak"/>' + friendly_name
# if all closed response
if text == "":
text = self.args["textLineClosed"]
except Exception as e:
self.log("Exception: {}".format(e))
self.log("slots: {}".format(slots))
text = self.random_arg(self.args["Error"])
return text
def random_arg(self, argName):
############################################
# pick a random text from a list
############################################
if isinstance(argName, list):
text = random.choice(argName)
else:
text = argName
return text
================================================
FILE: alexa/windowsOpen/windowsOpenIntent.yaml
================================================
windowsOpenIntent:
module: windowsOpenIntent
class: WindowsOpenIntent
language: DE
textLineClosed: "Alle Fenster und Türen sind zu"
#textLineClosed: "All windows and doors are closed"
textLineWindowOpen: "Folgende Fenster sind noch offen"
#textLineWindowOpen: "The following windows are stil open..."
textLineDoorOpen: "Folgende Türen sind noch offen"
#textLineDoorOpen: "The following doors are still open"
textLineDoorTilted: "Die folgenden Türen sind noch gekippt"
#textLineDoorTilted: "The following doors are tilted"
Error: <p>Ich habe dich nicht richtig verstanden</p>
unreadableState: "unlesbar fuer mich"
dependencies:
- listService
================================================
FILE: alexaSpeakerConnector/alexaSpeakerConnector.py
================================================
import appdaemon.plugins.hass.hassapi as hass
#
# App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers
#
# Args:
#
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# alexa_entity: the alexa media player entity. example: media_player.kevins_echo_dot_oben
# alexa_entity_source: source to set alexa to. example: Denon AVR-X1300W
# receiver: Receiver to turn on. example: media_player.denon_avr_x1300w
# receiver_source: source to set receiver to. example: Bluetooth
#
# Release Notes
#
# Version 1.2.0:
# Introduce INITIAL_VOLUME
#
# Version 1.1.1:
# Fix WAITING_TIME
#
# Version 1.1:
# Introduce WAITING_TIME
#
# Version 1.0:
# Initial Version
WAITING_TIME = 10
INITIAL_VOLUME = 30
class AlexaSpeakerConnector(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.timer_handle_list = []
self.app_switch = self.args["app_switch"]
self.alexa_entity = self.args["alexa_entity"]
self.alexa_entity_source = self.args["alexa_entity_source"]
self.receiver = self.args["receiver"]
self.receiver_source = self.args["receiver_source"]
self.listen_state_handle_list.append(
self.listen_state(self.state_change, self.alexa_entity)
)
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new.lower() == "playing" and old.lower() != "playing":
self.log("{} changed to {}".format(self.alexa_entity, new))
# Only trigger when the receiver is off. Otherwise its probably playing something
if self.get_state(self.receiver) == "off":
self.log(
"Setting source of {} to: {}".format(
self.receiver, self.receiver_source
)
)
self.call_service(
"media_player/select_source",
entity_id=self.receiver,
source=self.receiver_source,
)
self.log(f"Setting volume of {self.receiver} to: {INITIAL_VOLUME}")
self.call_service(
"media_player/volume_set",
entity_id=self.receiver,
volume_level=INITIAL_VOLUME,
)
self.timer_handle_list.append(
self.run_in(self.run_in_callback, WAITING_TIME)
)
def run_in_callback(self, kwargs):
"""
Callback method to introduce a waiting time for the receiver to come 'online'
:return:
"""
self.log(
"Setting source of {} to: {}".format(
self.alexa_entity, self.alexa_entity_source
)
)
self.call_service(
"media_player/select_source",
entity_id=self.alexa_entity,
source=self.alexa_entity_source,
)
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: alexaSpeakerConnector/alexaSpeakerConnector.yaml
================================================
#App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers
# alexaSpeakerConnector:
# module: alexaSpeakerConnector
# class: AlexaSpeakerConnector
# app_switch: input_boolean.alexa_speaker_connector
# alexa_entity: media_player.kevins_echo_dot_oben
# alexa_entity_source: Denon AVR-X1300W
# receiver: media_player.denon_avr_x1300w
# receiver_source: Bluetooth
================================================
FILE: appWatcher/appWatcher.py
================================================
import appdaemon.plugins.hass.hassapi as hass
#
# App which listens on the log for App crashes and notifies via telegram
#
# Args:
#
# Release Notes
#
# Version 2.0:
# Updates for Appdaemon Version 4.0.3
#
# Version 1.0:
# Initial Version
class AppWatcher(hass.Hass):
def initialize(self):
self.notify_name = self.args["notify_name"]
self.notify_message = self.args["notify_message"]
try:
self.exclude_apps = self.args["exclude_apps"].split(",")
except KeyError:
self.exclude_apps = None
# App dependencies
self.notifier = self.get_app("Notifier")
self.handle = self.listen_log(self.log_message_callback)
def log_message_callback(self, app_name, ts, level, log_type, message, kwargs):
if level == "WARNING" or level == "ERROR" or level == "CRITICAL":
if app_name == "AppDaemon":
if "Unexpected error" in message:
self.notifier.notify(
self.notify_name,
self.notify_message.format(message),
useAlexa=False,
)
def terminate(self):
self.cancel_listen_log(self.handle)
================================================
FILE: appWatcher/appWatcher.yaml
================================================
appWatcher:
module: appWatcher
class: AppWatcher
notify_name: kevin
notify_message: "AppDaemon error: {}"
#notify_message: "Appdaemon reported an error: {}"
dependencies:
- Notifier
================================================
FILE: apps.yaml
================================================
#################################################################
## Global
#################################################################
global_modules:
- globals
================================================
FILE: buttonClicked/buttonClicked.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
#
# App which toggles entities for single/double presses of xiaomi buttons
#
# Args:
#
# sensor: sensor to monitor e.g. sensor.upstairs_smoke
# actor_single: actor to toggle on single click
# actor_double: actor to toggle on double click
# actor_hold: actor to dim on hold
# Release Notes
#
# Version 1.2:
# All actors optional
#
# Version 1.1:
# added actor_hold
#
# Version 1.0:
# Initial Version
class ButtonClicked(hass.Hass):
def initialize(self):
self.listen_event_handle_list = []
self.timer_handle_list = []
self.actor_single = self.args.get("actor_single")
self.actor_double = self.args.get("actor_double")
self.actor_hold = self.args.get("actor_hold")
self.dimmer_timer_handle = None
self.listen_event_handle_list.append(
self.listen_event(self.event_detected, "xiaomi_aqara.click")
)
def event_detected(self, event_name, data, kwargs):
if data["entity_id"] == self.args["sensor"]:
if data["click_type"] == "single" and self.actor_single != None:
self.log("ButtonClicked: {}".format(data["entity_id"]))
# Is on
if self.get_state(self.actor_single) == "on":
self.log("Turning {} off".format(self.actor_single))
# Workaround for Yeelight see https://community.home-assistant.io/t/transition-for-turn-off-service-doesnt-work-for-yeelight-lightstrip/25333/4
if self.actor_single.startswith("light"):
self.call_service(
"light/turn_on",
entity_id=self.actor_single,
transition=1,
brightness_pct=1,
)
self.timer_handle_list.append(
self.run_in(self.turn_off_workaround, 2)
)
else:
self.turn_off(self.actor_single)
# Is off
if self.get_state(self.actor_single) == "off":
self.log("Turning {} on".format(self.actor_single))
if self.actor_single.startswith("light"):
self.call_service(
"light/turn_on",
entity_id=self.actor_single,
transition=1,
brightness_pct=100,
)
else:
self.turn_on(self.actor_single)
if data["click_type"] == "double" and self.actor_double != None:
self.log("Double Button Click: {}".format(data["entity_id"]))
self.log("Toggling {}".format(self.actor_double))
# Is on
if self.get_state(self.actor_double) == "on":
# Workaround for Yeelight see https://community.home-assistant.io/t/transition-for-turn-off-service-doesnt-work-for-yeelight-lightstrip/25333/4
if self.actor_single.startswith("light"):
self.call_service(
"light/turn_on",
entity_id=self.actor_single,
transition=1,
brightness_pct=1,
)
self.timer_handle_list.append(
self.run_in(self.turn_off_workaround, 2)
)
else:
self.turn_off(self.actor_single)
# Is off
if self.get_state(self.actor_double) == "off":
self.log("Turning {} on".format(self.actor_single))
if self.actor_single.startswith("light"):
self.call_service(
"light/turn_on",
entity_id=self.actor_single,
transition=1,
brightness_pct=100,
)
else:
self.turn_on(self.actor_single)
if data["click_type"] == "long_click_press" and self.actor_hold != None:
self.log("Long Button Click: {}".format(data["entity_id"]))
self.log("Starting Dimmer")
self.dimmer_timer_handle = self.run_every(
self.dimmer_callback,
datetime.datetime.now(),
0.5,
entity_id=self.actor_hold,
)
self.timer_handle_list.append(self.dimmer_timer_handle)
if data["click_type"] == "hold" and self.actor_hold != None:
self.log("Button Release: {}".format(data["entity_id"]))
self.log("Stopping Dimmer")
if self.dimmer_timer_handle != None:
self.cancel_timer(self.dimmer_timer_handle)
def dimmer_callback(self, kwargs):
"""Dimm the by 10% light. If it would dim above 100% start again at 10%"""
brightness_pct_old = (
int(
self.get_state(self.actor_hold, attribute="all")["attributes"][
"brightness"
]
)
/ 255
)
brightness_pct_new = brightness_pct_old + 0.1
if brightness_pct_new > 1:
brightness_pct_new = 0.1
self.call_service(
"light/turn_on",
entity_id=kwargs["entity_id"],
brightness_pct=brightness_pct_new * 100,
)
def turn_off_workaround(self, *kwargs):
self.call_service("light/turn_off", entity_id=self.actor_single)
def terminate(self):
for listen_event_handle in self.listen_event_handle_list:
self.cancel_listen_event(listen_event_handle)
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: buttonClicked/buttonClicked.yaml
================================================
# App which toggles entities for single/double presses of xiaomi buttons
# xiaomiroundButtonBedroomClicked:
# module: buttonClicked
# class: ButtonClicked
# sensor: binary_sensor.switch_158d0001b12a12
# actor_single: light.bedroom_yeelight
# actor_double: group.all
# actor_hold: light.bedroom_yeelight
# dependencies:
# - Notifier
# xiaomisquareButtonLobbyClicked:
# module: buttonClicked
# class: ButtonClicked
# sensor: binary_sensor.switch_158d00021329bc
# actor_single: switch.lobby
# actor_double: switch.lobby
# dependencies:
# - Notifier
# xiaomiroundButtonBathroomClicked:
# module: buttonClicked
# class: ButtonClicked
# sensor: sensor.0x00158d00012db9e5_click
# actor_single: light.lower_bathroom_yeelight
# actor_double: light.lower_bathroom_yeelight
# actor_hold: light.lower_bathroom_yeelight
# dependencies:
# - Notifier
================================================
FILE: comingHome/comingHome.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
#
# App to Turn on Lobby Lamp when Door openes and no one is Home
#
# Args:
#
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# sensor: door sensor
# isHome: input_boolean which shows if someone is home eg input_boolean.isHome
# actor (optional): actor to turn on. example: script.receiver_set_source_bluetooth
# service (optional): service to call. example: media_player.volume_set
# service_data (optional): dictionary of attributes for the service call.
# after_sundown (optional): whether to only trigger after sundown. example: True
# Release Notes
#
# Version 1.4.2:
# unwrap service_data
#
# Version 1.4.1:
# fix duplicate line for self.actor
#
# Version 1.4:
# Add service and service_data and make actor optional
#
# Version 1.3.2:
# Check for new != old
#
# Version 1.3.1:
# Actually implement isHome
#
# Version 1.3:
# Added app_switch
#
# Version 1.2:
# Added after_sundown
#
# Version 1.1:
# Using globals
#
# Version 1.0:
# Initial Version
class ComingHome(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.app_switch = self.args["app_switch"]
self.sensor = self.args["sensor"]
self.isHome = self.args["isHome"]
self.actor = self.args.get("actor")
self.service = self.args.get("service")
self.service_data = self.args.get("service_data")
self.after_sundown = self.args.get("after_sundown")
self.delay = 2
self.listen_state_handle_list.append(
self.listen_state(self.state_change, self.sensor)
)
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new != "" and new != old:
isHome_attributes = self.get_state(self.isHome, attribute="all")
isHome_state = isHome_attributes["state"]
last_changed = self.convert_utc(isHome_attributes["last_changed"])
if isHome_state == "off" or (
datetime.datetime.now(datetime.timezone.utc) - last_changed
<= datetime.timedelta(seconds=self.delay)
):
if self.after_sundown is not None and self.after_sundown:
if self.sun_down():
self.turn_on_actor(self.actor, entity, new)
self.my_call_service(
self.service, self.service_data, entity, new
)
else:
self.turn_on_actor(self.actor, entity, new)
self.my_call_service(
self.service, self.service_data, entity, new
)
def turn_on_actor(self, actor, entity, new):
if self.actor is not None:
self.log("{} changed to {}".format(self.friendly_name(entity), new))
self.turn_on(actor)
def my_call_service(self, service, service_data, entity, new):
if self.service is not None:
if self.service_data is not None:
self.log("{} changed to {}".format(self.friendly_name(entity), new))
self.call_service(service, **service_data)
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: comingHome/comingHome.yaml
================================================
#Switch on Lobby lamp when the first person is coming home and the sun is down
# comingHomeYeelight:
# module: comingHome
# class: ComingHome
# app_switch: input_boolean.coming_home_yeelight
# sensor: binary_sensor.contact_door
# isHome: input_boolean.is_home
# actor: switch.large_lamp
# after_sundown: True
# comingHomeSetVolume:
# module: comingHome
# class: ComingHome
# app_switch: input_boolean.coming_home_set_volume
# sensor: binary_sensor.contact_door
# isHome: input_boolean.is_home
# service: media_player/volume_set
# service_data:
# entity_id: media_player.denon_avr_x1300w
# volume_level: 0.3
================================================
FILE: deconz_xiaomi_button/deconz_xiaomi_button.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
#
# App which toggles entities for single/double/hold presses of Xiaomi buttons connected via deconz
#
# Args:
#
# id: id of the xiaomi button
# actor_single: actor to toggle on single click
# actor_double: actor to toggle on double click
# actor_hold: actor to dim on hold
#
# Release Notes
#
# Version 2.0.2
# use else when toggling
#
# Version 2.0.1
# use elif when toggling
#
# Version 2.0:
# Removed unneeded workaround for yeelight
#
# Version 1.0:
# Initial Version
class DeconzXiaomiButton(hass.Hass):
def initialize(self):
self.listen_event_handle_list = []
self.timer_handle_list = []
self.actor_single = self.args.get("actor_single")
self.actor_double = self.args.get("actor_double")
self.actor_hold = self.args.get("actor_hold")
self.id = self.args["id"]
self.dimmer_timer_handle = None
self.listen_event_handle_list.append(
self.listen_event(self.event_detected, "deconz_event")
)
def event_detected(self, event_name, data, kwargs):
if data["id"] == self.id:
if data["event"] == 1002 and self.actor_single is not None:
self.log("ButtonClicked: {}".format(data["id"]))
self.log("Toggling {}".format(self.actor_double))
# Is on
if self.get_state(self.actor_single) == "on":
self.log("Turning {} off".format(self.actor_single))
self.turn_off(self.actor_single)
# Is off
else:
self.log("Turning {} on".format(self.actor_single))
self.turn_on(self.actor_single)
if data["event"] == 1004 and self.actor_double is not None:
self.log("Double Button Click: {}".format(data["id"]))
self.log("Toggling {}".format(self.actor_double))
# Is on
if self.get_state(self.actor_double) == "on":
self.turn_off(self.actor_double)
# Is off
else:
self.log("Turning {} on".format(self.actor_double))
self.turn_on(self.actor_double)
if data["event"] == 1001 and self.actor_hold is not None:
self.log("Long Button Click: {}".format(data["id"]))
self.log("Starting Dimmer")
self.dimmer_timer_handle = self.run_every(
self.dimmer_callback,
datetime.datetime.now(),
0.5,
entity_id=self.actor_hold,
)
self.timer_handle_list.append(self.dimmer_timer_handle)
if data["event"] == 1003 and self.actor_hold is not None:
self.log("Button Release: {}".format(data["id"]))
self.log("Stopping Dimmer")
if self.dimmer_timer_handle is not None:
self.cancel_timer(self.dimmer_timer_handle)
def dimmer_callback(self, kwargs):
"""Dimm the by 10% light. If it would dim above 100% start again at 10%"""
brightness_pct_old = (
int(
self.get_state(self.actor_hold, attribute="all")["attributes"][
"brightness"
]
)
/ 255
)
brightness_pct_new = brightness_pct_old + 0.1
if brightness_pct_new > 1:
brightness_pct_new = 0.1
self.call_service(
"light/turn_on",
entity_id=kwargs["entity_id"],
brightness_pct=brightness_pct_new * 100,
)
def terminate(self):
for listen_event_handle in self.listen_event_handle_list:
self.cancel_listen_event(listen_event_handle)
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: deconz_xiaomi_button.yaml
================================================
DeconzXiaomiButtonBedroom:
module: deconz_xiaomi_button
class: DeconzXiaomiButton
id: round_button_schlafzimmer
actor_single: light.bedroom_yeelight
actor_double: group.all
actor_hold: light.bedroom_yeelight
DeconzXiaomiButtonLobby:
module: deconz_xiaomi_button
class: DeconzXiaomiButton
id: flur_switch
actor_single: switch.lobby
actor_double: switch.lobby
DeconzXiaomiButtonLobby:
module: deconz_xiaomi_button
class: DeconzXiaomiButton
id: flur_switch
actor_single: switch.lobby
actor_double: switch.lobby
DeconzXiaomiButtonBathroom:
module: deconz_xiaomi_button
class: DeconzXiaomiButton
id: round_button_bad
actor_single: light.lower_bathroom_yeelight
actor_double: light.lower_bathroom_yeelight
actor_hold: light.lower_bathroom_yeelight
================================================
FILE: detectWrongState/detectWrongState.py
================================================
import appdaemon.plugins.hass.hassapi as hass
#
# App which notifies of wrong states based on a state change
#
# Args:
#
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# entities_on (optional): list of entities which should be on
# entities_off (optional): list of entities which should off
# trigger_entity: entity which triggers this app. example: input_boolean.is_home
# trigger_state: new state of trigger_entity which triggers this app. example: "off"
# after_sundown (optional): Only trigger after sundown. example: True
# message_<LANG>: message to use in notification
# message_off_<LANG>: message to use in notification
# message_reed_<LANG>: message to use in notification
# message_reed_off_<LANG>: message to use in notification
# notify_name: who to notify. example: group_notifications
# use_alexa: use alexa for notification. example: False
#
# Release Notes
#
# Version 2.1:
# More off_states to support alexa_media
#
# Version 2.0:
# Renamed to detectWrongState, notification optional
#
# Version 1.9:
# check unavailable when using get_state
#
# Version 1.8:
# check None when using get_state
#
# Version 1.7:
# check for != off instead of == on
#
# Version 1.6.1:
# fix wrong key access for attributes
#
# Version 1.6:
# garage_door to device_classes of reed sensors
#
# Version 1.5:
# distinguish normal and reed switches by device_class
#
# Version 1.4.1:
# fix wrong assignment of app_switch
#
# Version 1.4:
# Generalize to detectWrongState
#
# Version 1.3:
# use Notify App
#
# Version 1.2:
# message now directly in own yaml instead of message module
#
# Version 1.1:
# Using globals and app_switch
#
# Version 1.0:
# Initial Version
class DetectWrongState(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.app_switch = self.args["app_switch"]
try:
self.entities_on = self.args["entities_on"].split(",")
except KeyError:
self.entities_on = []
try:
self.entities_off = self.args["entities_off"].split(",")
except KeyError:
self.entities_off = []
self.after_sundown = self.args.get("after_sundown")
self.trigger_entity = self.args["trigger_entity"]
self.trigger_state = self.args["trigger_state"]
self.message = self.args.get("message")
self.message_off = self.args.get("message_off")
self.message_reed = self.args.get("message_reed")
self.message_reed_off = self.args.get("message_reed_off")
self.notify_name = self.args.get("notify_name")
self.use_alexa = self.args.get("use_alexa")
self.notifier = self.get_app("Notifier")
self.listen_state_handle_list.append(
self.listen_state(self.state_change, self.trigger_entity)
)
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new != "" and new == self.trigger_state:
if self.after_sundown is None or (
(self.after_sundown and self.sun_down())
or self.after_sundown is not False
):
self.check_entities_should_be_off()
self.check_entities_should_be_on()
def check_entities_should_be_off(self):
off_states = ["off", "unavailable", "paused", "standby"]
for entity in self.entities_off:
state = self.get_state(entity)
self.log(f"entity: {entity}")
if state is not None and state not in off_states:
if self.is_entity_reed_contact(entity):
message = self.message_reed
else:
self.turn_off(entity)
message = self.message
self.send_notification(message, entity)
def check_entities_should_be_on(self):
for entity in self.entities_on:
state = self.get_state(entity)
if state == "off":
if self.is_entity_reed_contact(entity):
message = self.message_reed_off
else:
self.turn_on(entity)
message = self.message_on
self.send_notification(message, entity)
def is_entity_reed_contact(self, entity):
reed_types = ["window", "door", "garage_door"]
full_state = self.get_state(entity, attribute="all")
if full_state is not None:
attributes = full_state["attributes"]
self.log("full_state: {}".format(full_state), level="DEBUG")
if attributes.get("device_class") in reed_types:
return True
return False
def send_notification(self, message, entity):
if message is not None:
formatted_message = message.format(self.friendly_name(entity))
self.log(formatted_message)
if self.notify_name is not None:
self.notifier.notify(
self.notify_name, formatted_message, useAlexa=self.use_alexa,
)
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: detectWrongState/detectWrongState.yaml
================================================
# detectWrongStateWhenLeaving:
# module: detectWrongState
# class: DetectWrongState
# app_switch: input_boolean.detect_wrong_state_when_leaving
# entities_off: "binary_sensor.contact_bedroom_door,\
# binary_sensor.contact_bedroom_door_tilted,binary_sensor.contact_door,binary_sensor.contact_guest_window,\
# binary_sensor.contact_kitchen_window,binary_sensor.contact_studyroom_door,\
# binary_sensor.contact_studyroom_door_tilted,binary_sensor.contact_terrace_door,\
# binary_sensor.contact_terrace_door_tilted,binary_sensor.contact_upper_bathroom_window,\
# media_player.denon_avr_x1300w,switch.large_lamp,switch.small_lamp,switch.snowboard,\
# light.bedroom_yeelight,light.bar_table,light.lobby_yeelight,light.reading_lamp_yeelight,\
# light.upper_stairs_yeelight,light.stairs_lower_yeelight,switch.ventilator,light.livingroom_yeelight,\
# switch.tv,switch.weihnachtslichter,switch.bedroom_receiver,light.lower_bathroom_yeelight,\
# media_player.kevin_s_echo_dot_unten,media_player.kevins_echo,media_player.kevins_echo_dot,\
# media_player.kevins_echo_dot_oben,binary_sensor.contact_upper_bathroom_window_tilted,\
# binary_sensor.contact_badfenster"
# trigger_entity: input_boolean.is_home
# trigger_state: "off"
# message: "Du hast {} angelassen. Ich habe es für dich ausgemacht."
# #message: "You left on {}. I turned it off for you"
# message_off: "Du hast {} vergessen anzumachen. Ich habe es für dich angemacht."
# #message_off: "You forgot to turn on {}. I turned it on for you"
# message_reed: "Du hast {} offen gelassen."
# #message_reed: "You left open {} Dummy."
# message_reed_off: "Du hast {} zu gelassen."
# #message_reed_off: "You left {} closed Dummy."
# notify_name: group_notifications
# use_alexa: False
# log_level: DEBUG
# dependencies:
# - Notifier
# detectWindowsOpenWhenGoingToBed:
# module: detectWrongState
# class: DetectWrongState
# app_switch: input_boolean.detect_windows_open_when_going_to_bed
# entities_off: "binary_sensor.contact_bathroom_window_tilted,binary_sensor.contact_bedroom_door,\
# binary_sensor.contact_bedroom_door_tilted,binary_sensor.contact_door,binary_sensor.contact_guest_window,\
# binary_sensor.contact_kitchen_window,binary_sensor.contact_studyroom_door,\
# binary_sensor.contact_studyroom_door_tilted,binary_sensor.contact_terrace_door,\
# binary_sensor.contact_terrace_door_tilted,binary_sensor.contact_upper_bathroom_window,\
# binary_sensor.contact_upper_bathroom_window_tilted,binary_sensor.contact_badfenster"
# after_sundown: True
# trigger_entity: input_boolean.sleepmode
# trigger_state: "on"
# message: "Du hast {} angelassen. Ich habe es für dich ausgemacht."
# #message: "You left on {}. I turned it off for you"
# message_off: "Du hast {} vergessen anzumachen. Ich habe es für dich angemacht."
# #message_off: "You forgot to turn on {}. I turned it on for you"
# message_reed: "Du hast {} offen gelassen."
# #message_reed: "You left open {} Dummy."
# message_reed_off: "Du hast {} zu gelassen."
# #message_reed_off: "You left {} closed Dummy."
# notify_name: group_notifications
# use_alexa: True
# log_level: DEBUG
# dependencies:
# - Notifier
# detectDevicesOnWhenGoingToBed:
# module: detectWrongState
# class: DetectWrongState
# app_switch: input_boolean.detect_devices_on_when_going_to_bed
# entities_off: "media_player.denon_avr_x1300w,switch.large_lamp,\
# switch.small_lamp,switch.snowboard,light.bedroom_yeelight,light.bar_table,light.lobby_yeelight,\
# light.reading_lamp_yeelight,light.upper_stairs_yeelight,light.stairs_lower_yeelight,switch.ventilator,light.livingroom_yeelight,\
# switch.tv,switch.weihnachtslichter,switch.bedroom_receiver,switch.tv,light.bar_table,light.lower_bathroom_yeelight,\
# switch.markise, switch.coffee_machine_plug_relay"
# trigger_entity: input_boolean.sleepmode
# trigger_state: "on"
# log_level: DEBUG
# dependencies:
# - Notifier
================================================
FILE: ench.yaml
================================================
---
ench:
module: ench
class: EnCh
notify: "notify.kevin"
exclude:
- device_tracker.venue_8*
- person.kevin
- device_tracker.sonoff_large_ventilator_7998
- device_tracker.sonoff_ventilator_8133
- device_tracker.android*
- device_tracker.astrids_mbp
- device_tracker.franzi_s_iphone
- device_tracker.galaxy*
- device_tracker.iphone*
- device_tracker.oneplus*
- device_tracker.unifi*
- light.group_0
- media_player.kevin_s*
- sensor.192_168_1_39*
- sensor.192_168_1_48*
- sensor.large_ventilator*
- sensor.ventilator*
- switch.ventilator
- switch.large_ventilator
- sensor.travel_time_next*
- sensor.glances*_temp
- sensor.publish_ip_on_boot
- sensor.*odroid*
- media_player.55pus7304_12
- media_player.fernseher
- sensor.consumption_31
- sensor.power_30
- sensor.openweathermap*
- light.bar_table
- sensor.*_nachster_wecker
- switch.xiaomi_plug
- media_player.55pus7304_12_*
# Alexa Media Player
- sensor.*next_alarm
- sensor.*next_reminder
- sensor.*next_timer
- sensor.this_device*
- switch.*_repeat_switch*
- switch.*_shuffle_switch*
- switch.*do_not_disturb_switch
battery:
interval_min: 180
min_level: 20
unavailable:
interval_min: 60
max_unavailable_min: 15
================================================
FILE: eventMonitor/eventMonitor.py
================================================
import appdaemon.plugins.hass.hassapi as hass
"""
Monitor events and output changes to the verbose_log. Nice for debugging purposes.
Arguments:
- events: List of events to monitor
"""
class Monitor(hass.Hass):
def initialize(self):
self.listen_event_handle_list = []
events = self.args["events"]
if events != None:
for event in self.split_device_list(self.args["events"]):
self.log('watching event "{}" for state changes'.format(event))
self.listen_event_handle_list.append(
self.listen_event(self.changed, event)
)
if len(self.listen_event_handle_list) == 0:
self.log("watching all events for state changes")
self.listen_event_handle_list.append(self.listen_event(self.changed))
def changed(self, event_name, data, kwargs):
self.log(event_name + ": " + str(data))
def terminate(self):
for listen_event_handle in self.listen_event_handle_list:
self.cancel_listen_event(listen_event_handle)
================================================
FILE: eventMonitor/eventMonitor.yaml
================================================
#eventMonitor:
# module: eventMonitor
# class: Monitor
# events:
================================================
FILE: faceRecognitionBot/faceRecognitionBot.py
================================================
import json
from json import JSONDecodeError
import appdaemon.plugins.hass.hassapi as hass # pylint: disable=import-error
import shutil
import os
import time
import datetime
import requests
#
# App which runs face detection and notifies the user with the result
#
#
# Args:
#
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# sensor: binary sensor to use as trigger
# button: xiaomi button to use as a trigger
# camera : camera entity. example: camera.ip_webcam
# local_file_camera: local file camera entity. example: camera.saved_image
# notify_name: Who to notify. example: group_notifications
# wol_switch: Wake on Lan switch which turns on the facebox server. example: switch.facebox_wol
# user_id: The user_id of the telegram user to ask whether he knows an unknown face
# number_of_images: Number of images to take. example: 10
# waitBeforeSnapshot: How many seconds to wait before triggering the inital snapshot. example: 2.5
# message_face_identified: Message to send if a face got identified.
# message_unkown_face: Message to send if a face is unknown
# message_provide_name
# message_name_provided
# message_name_provided_callback
# ip: Ip of facerec_service. example: 192.168.0.1
# port: port of facerec_service. example: 8080
#
# Release Notes
#
# Version 1.3.beta:
# Fully working
#
# Version 1.2:
# Rework to FaceRecognitionBot
#
# Version 1.1:
# Take Snapshot before sending WoL
#
# Version 1.0:
# Initial Version
CLASSIFIER = "faces"
TIMEOUT = 20
PROVIDE_NAME_TIMEOUT = 5
IDENTIFIER_DELIMITER = "_"
FILENAME_DELIMITER = "-"
MAXIMUM_DISTANCE = 0.40
UNKNOWN_FACE_NAME = "unkown"
class FaceRecognitionBot(hass.Hass):
def initialize(self):
# handle lists
self.timer_handle_list = []
self.listen_event_handle_list = []
self.listen_state_handle_list = []
# args
self.app_switch = self.args["app_switch"]
self.sensor = self.args["sensor"]
self.button = self.args["button"]
self.camera = self.args["camera"]
self.local_file_camera = self.args["local_file_camera"]
self.filename = self.args["filename"]
self.notify_name = self.args["notify_name"]
self.wol_switch = self.args["wol_switch"]
self.user_id = self.args["user_id"]
self.number_of_images = self.args["number_of_images"]
self.waitBeforeSnapshot = self.args["waitBeforeSnapshot"]
self.message_face_identified = self.args["message_face_identified"]
self.message_unkown_face = self.args["message_unkown_face"]
self.message_unkown_face_with_known = self.args[
"message_unkown_face_with_known"
]
self.message_provide_name = self.args["message_provide_name"]
self.message_name_provided = self.args["message_name_provided"]
self.message_name_provided_callback = self.args[
"message_name_provided_callback"
]
self.facebox_healthcheck_filename = self.args["facebox_healthcheck_filename"]
self.healthcheck_face_name = self.args["healthcheck_face_name"]
self.ip = self.args["ip"]
self.port = self.args["port"]
# optional args
self.facebox_source_directory = self.args["facebox_source_directory"]
if not self.facebox_source_directory.endswith("/"):
self.facebox_source_directory = self.facebox_source_directory + "/"
self.facebox_unknown_directory = self.args["facebox_unknown_directory"]
if not self.facebox_unknown_directory.endswith("/"):
self.facebox_unknown_directory = self.facebox_unknown_directory + "/"
self.facebox_noface_directory = self.args["facebox_noface_directory"]
if not self.facebox_noface_directory.endswith("/"):
self.facebox_noface_directory = self.facebox_noface_directory + "/"
self.facebox_known_faces_directory = self.args["facebox_known_faces_directory"]
if not self.facebox_known_faces_directory.endswith("/"):
self.facebox_known_faces_directory = (
self.facebox_known_faces_directory + "/"
)
# App dependencies
self.notifier = self.get_app("Notifier")
# Subscribe to sensors
self.listen_state_handle_list.append(
self.listen_state(self.triggered, self.sensor)
)
# Subscribe to custom triggers
self.listen_event_handle_list.append(
self.listen_event(self.button_clicked, "xiaomi_aqara.click")
)
self.listen_event_handle_list.append(
self.listen_event(self.learn_faces_event_callback, "eifinger_learn_faces")
)
# subscribe to telegram events
self.listen_event_handle_list.append(
self.listen_event(self.receive_telegram_callback, "telegram_callback")
)
self.listen_event_handle_list.append(
self.listen_event(self.receive_telegram_text, "telegram_text")
)
# Teach periodic run
self.timer_handle_list.append(self.run_in(self.check_health_callback, 5))
# custom variables
self.valid_filetypes = (".jpg", ".png", ".jpeg")
self.teach_url = "http://{}:{}/faces".format(self.ip, self.port)
self.health_url = "http://{}:{}/faces".format(self.ip, self.port)
self.check_url = "http://{}:{}".format(self.ip, self.port)
self.run_in_initial_delay = 43200
self.run_in_delay = self.run_in_initial_delay
self.run_in_error_delay = 60
self.check_health_timeout = 10
self.exclude_folders = (
"healthcheck",
"multiple",
"noface",
"tmp",
"unknown",
"new",
)
self.provide_name_timeout_start = None
self.last_identifier = None
self.last_message_id = None
self.last_from_first = None
###############################################################
# Teacher
###############################################################
def check_health_callback(self, kwargs):
"""Check health.
Runs repeatedly until it is veryfied that the classifier is healthy and faces are trained.
If it is healthy and trained it will check again after run_in_initial_delay"""
try:
if self.check_classifier_health():
self.check_if_trained(None)
self.timer_handle_list.append(
self.run_in(self.check_health_callback, self.run_in_delay)
)
except requests.exceptions.HTTPError as exception:
self.log(
"Error trying to turn on entity. Will try again in 1s. Error: {}".format(
exception
),
level="WARNING",
)
self.timer_handle_list.append(self.run_in(self.check_health_callback, 1))
def learn_faces_event_callback(self, event_name, data, kwargs):
"""Callback function for manual trigger of face learning"""
self.log("Event received. Triggering Face Learning")
self.teach_faces(self.facebox_known_faces_directory, self.exclude_folders)
def teach_name_by_file(self, teach_url, name, file_path):
"""Teach the classifier a single name using a single file."""
file_name = file_path.split("/")[-1]
file = {"file": open(file_path, "rb")}
teach_url = teach_url + "?id=" + name
response = requests.post(teach_url, files=file)
if response.status_code == 200:
self.log("File: {} taught with name: {}".format(file_name, name))
return True
elif response.status_code == 400:
self.log(
"Teaching of file: {} failed with message: {}".format(
file_name, response.text
)
)
return False
def check_classifier_health(self):
"""Check if classifier is reachable under health_url and returns HTTP 200"""
try:
response = requests.get(self.health_url, timeout=self.check_health_timeout)
if response.status_code == 200:
self.log("Health-check passed")
self.run_in_delay = self.run_in_initial_delay
self.log("Setting run_in_delay to {}".format(self.run_in_delay))
return True
else:
self.log("Health-check failed")
self.log(response.status_code)
# check for recurring error
if self.run_in_delay < self.run_in_initial_delay:
self.run_in_delay = self.run_in_delay * 2
else:
self.run_in_delay = self.run_in_error_delay
return False
except requests.exceptions.Timeout as timeout_exception:
self.log("Health-Check timed out")
self.check_health_timeout = self.check_health_timeout * 1.5
return self.check_classifier_health()
except requests.exceptions.RequestException as exception:
self.log("Server is unreachable", level="WARNING")
self.log(exception, level="WARNING")
# check for recurring error
if self.run_in_delay < self.run_in_initial_delay:
self.run_in_delay = self.run_in_delay * 2
else:
self.run_in_delay = self.run_in_error_delay
self.log("Setting run_in_delay to {}".format(self.run_in_delay))
def check_if_trained(self, kwargs):
"""Check if faces are trained. If not train them.
Checks for a picture with a known result if the classifier returns the correct result
"""
response = self.post_image(self.check_url, self.facebox_healthcheck_filename)
if (
response
and response.status_code == 200
and len(response.json()["faces"]) > 0
and response.json()["faces"][0]["id"] == self.healthcheck_face_name
):
self.log("Faces are still taught")
else:
self.log("Faces are not taught")
self.teach_faces(self.facebox_known_faces_directory, self.exclude_folders)
def teach_faces(self, folderpath, exclude_folders=[]):
"""Teach faces.
Will iterate over all subdirectories of 'folderpath' and teach the name within
that subdirectory with the name of the subdirectory"""
self.log("Teaching faces")
for folder_name in self.list_folders(folderpath):
if not folder_name in exclude_folders:
folder_path = os.path.join(folderpath, folder_name)
for file in os.listdir(folder_path):
if file.endswith(self.valid_filetypes):
file_path = os.path.join(folder_path, file)
self.teach_name_by_file(self.teach_url, folder_name, file_path)
def teach_name_by_directory(self, name, folderpath):
"""Teach faces in a directory for a given anme"""
self.log("Teaching faces in dir: {}".format(folderpath))
for file in os.listdir(folderpath):
if file.endswith(self.valid_filetypes):
file_path = os.path.join(folderpath, file)
self.teach_name_by_file(self.teach_url, name, file_path)
###############################################################
# Classifier
###############################################################
def button_clicked(self, event_name, data, kwargs):
"""Extra callback method to trigger the face detection on demand by pressing a Xiaomi Button"""
if data["entity_id"] == self.button:
if data["click_type"] == "single":
self.timer_handle_list.append(
self.run_in(self.takeSnapshots, self.waitBeforeSnapshot)
)
def triggered(self, entity, attribute, old, new, kwargs):
"""State Callback to start the face detection process"""
if self.get_state(self.app_switch) == "on":
if new == "on":
self.timer_handle_list.append(
self.run_in(self.takeSnapshots, self.waitBeforeSnapshot)
)
def takeSnapshots(self, kwargs):
"""Take a snapshot. Save to a file."""
file_locations = []
timestamp = time.strftime("%Y%m%d%H%M%S")
directory = self.facebox_source_directory + "new/" + timestamp
if not os.path.exists(directory):
os.makedirs(directory)
for i in range(0, self.number_of_images):
filename = (
directory + "/" + timestamp + FILENAME_DELIMITER + str(i) + ".jpg"
)
self.log("Calling camera/snapshot and saving it to: {}".format(filename))
self.call_service(
"camera/snapshot", entity_id=self.camera, filename=filename
)
file_locations.append(filename)
self.timer_handle_list.append(
self.run_in(self.processImages, 0, file_locations=file_locations)
)
def processImages(self, kwargs):
"""Trigger image processing for all images and process the results
Get the classifier result for each image
store it in a dictionary in the following format
{filename:
{"count":int,
"faces":{
[
{"dist":float,
"id":name}
]
},
"matchedFacesCount":int}
}
"""
result_dict_dict = {}
for filename in kwargs["file_locations"]:
response = self.post_image(self.check_url, filename)
if response is not None:
result_dict = {}
self.log("response is: {}".format(response.text))
try:
response_json = response.json()
result_dict["count"] = response_json["count"]
result_dict["faces"] = response_json["faces"]
result_dict["matchedFacesCount"] = len(response_json["faces"])
result_dict_dict[filename] = result_dict
except JSONDecodeError:
self.log("JSONDecodeError. Skipping response")
# get the maximum number of faces detected in one image
maxCount = self._getMaxCountFromResult(result_dict_dict)
# get a list of distinct recognized face names
faceNames = self._getFaceNamesFromResult(result_dict_dict)
self.log("Number of distinct faces: {}".format(len(faceNames)))
if maxCount > 1:
self.log("At least one time detected more than one face")
# check if it contains an unknown face
if UNKNOWN_FACE_NAME in faceNames:
self._notifyUnkownFaceFound(result_dict_dict)
else:
for faceName in faceNames:
if faceName in self._getKnownFaces():
self.log(self.message_face_identified.format(faceName))
self.notifier.notify(
self.notify_name,
self.message_face_identified.format(faceName),
)
# copy file to saved image to display in HA
shutil.copy(filename, self.filename)
elif maxCount == 1:
self.log("Always detected one face")
# check if always the same face
if len(faceNames) > 1:
self.log("Not always the same face")
# TODO test!
# at least one time detected a known face
# notify of who was detected
for faceName in faceNames:
if faceName in self._getKnownFaces():
self.log(self.message_face_identified.format(faceName))
self.notifier.notify(
self.notify_name,
self.message_face_identified.format(faceName),
)
# copy file to saved image to display in HA
shutil.copy(filename, self.filename)
# process the unknown faces
if UNKNOWN_FACE_NAME in faceNames:
self._processUnkownFaceFound(result_dict_dict)
else:
self.log("Always the same face")
# Is it a known face?
if len(faceNames) > 0 and faceNames[0] in self._getKnownFaces():
# always identified the same known person
self.log(self.message_face_identified.format(faceNames[0]))
self.notifier.notify(
self.notify_name,
self.message_face_identified.format(faceNames[0]),
)
# Move files to known face subdirectory
for filename in result_dict_dict:
# at this time we know it is at most 1 and it is always the same known face
if result_dict_dict[filename]["count"] == 1:
directory = (
self.facebox_known_faces_directory
+ result_dict_dict[filename]["faces"][0]["id"]
)
new_filename = os.path.join(
directory, os.path.split(filename)[1]
)
# copy file to saved image to display in HA
shutil.copy(filename, self.filename)
self.log(
"Move file from {} to {}".format(filename, new_filename)
)
shutil.move(filename, new_filename)
# trigger teaching
self.teach_name_by_file(
self.teach_url,
result_dict_dict[filename]["faces"][0]["id"],
new_filename,
)
else:
# unknown face
self._processUnkownFaceFound(result_dict_dict)
else:
self.log("Detected no faces")
# get directory of images and post that in telegram
def _processUnkownFaceFound(self, result_dict_dict):
"""Store the faces for later use and ask the user if he knows the unkown face"""
# TODO check if the faces are similar
# create a temp identifier, compare and delete identifier again
# self._determineIfSameUnkownFace(result_dict_dict)
# get a file where the unknown face was detected and send it
filename = self._getFileWithUnknownFaceFromResult(result_dict_dict)
# copy file to saved image to display in HA
shutil.copy(filename, self.filename)
# move all files where a face was detected to the unkown folder
identifier = self._moveFilesToUnknown(result_dict_dict)
filename_without_path = os.path.split(filename)[1]
# send photo
unknown_filename = self.facebox_unknown_directory + filename_without_path
self.log(f"Sending photo with filename: {unknown_filename}")
self.call_service("telegram_bot/send_photo", file=unknown_filename, target=self.user_id)
if identifier == "":
self.log("Identifier is empty", level="ERROR")
else:
self.ask_for_name(identifier)
def _notifyUnkownFaceFound(self, result_dict_dict):
"""Notify of an unkown face in a image where a known face was detected"""
# get a file where the unknown face was detected and send it
filename = self._getFileWithUnknownFaceFromResult(result_dict_dict)
# copy file to saved image to display in HA
shutil.copy(filename, self.filename)
# send photo
self.call_service("telegram_bot/send_photo", file=filename, target=self.user_id)
self.log(self.message_unkown_face_with_known)
self.notifier.notify(self.notify_name, self.message_unkown_face_with_known)
def _getMaxCountFromResult(self, result_dict_dict):
"""Get the maximum number of faces found in the pictures"""
count_list = [d["count"] for d in result_dict_dict.values()]
return max(count_list)
def _getFaceNamesFromResult(self, result_dict_dict):
"""Return a list of names for the identified faces"""
try:
id_list = []
for d in result_dict_dict.values():
# check for unknown face
if len(d["faces"]) == 0 and d["count"] == 1:
id_list.append(UNKNOWN_FACE_NAME)
else:
for face in d["faces"]:
if face["dist"] < MAXIMUM_DISTANCE:
id_list.append(face["id"])
# if distance(similarity) too large, mark as unknown
else:
self.log(
"Similary distance of {} is larger than maximum threshold of {}".format(
face["dist"], MAXIMUM_DISTANCE
)
)
id_list.append(UNKNOWN_FACE_NAME)
face["id"] = UNKNOWN_FACE_NAME
self.log("FacesNames: {}".format(list(set(id_list))))
return list(set(id_list))
except TypeError:
return []
def _getFileWithUnknownFaceFromResult(self, result_dict_dict):
"""Get the first file from the result which has an unmatched face"""
for filename in result_dict_dict.keys():
if (
result_dict_dict[filename]["count"] > 0
and result_dict_dict[filename]["faces"][0]["id"] == UNKNOWN_FACE_NAME
):
return filename
def _determineIfSameUnkownFace(self, result_dict_dict):
"""Determine if the unkown face which was detected several times is the same unknown face"""
# get all files with unknown faces
unkown_faces = []
for filename in result_dict_dict.keys():
if (
result_dict_dict[filename]["count"] == 1
and result_dict_dict[filename]["faces"][0]["id"] == UNKNOWN_FACE_NAME
):
unkown_faces.append(filename)
# iterate over all files
for k, filename in enumerate(unkown_faces):
for i, filename in enumerate(unkown_faces):
if i < k:
pass
elif i == k:
# teach the first face
filename_without_path = os.path.split(filename)[1]
self.teach_name_by_file(
self.teach_url, filename_without_path, filename
)
else:
response = self.post_image(self.check_url, filename)
response_json = response.json()
if response_json["count"] > 0:
if (
response_json["faces"][0]["id"] == filename_without_path
and response_json["faces"][0]["dist"] < MAXIMUM_DISTANCE
):
# same face remove it from the list
unkown_faces.remove(filename)
def _moveFilesToUnknown(self, result_dict_dict):
"""Copy all files where the unknown face was detected to the unknown folder.
Returns the timestamp under which all files can be identified"""
identifier = ""
for filename in result_dict_dict.keys():
if result_dict_dict[filename]["count"] > 0 and (
len(result_dict_dict[filename]["faces"]) == 0
or result_dict_dict[filename]["faces"][0]["id"] == UNKNOWN_FACE_NAME
):
filename_without_path = os.path.split(filename)[1]
# get the timestamp as identifier, strip everything after "-""
identifier = filename_without_path.split(FILENAME_DELIMITER)[0]
self.log("Identifier is: {}".format(identifier), level="DEBUG")
new_filename = self.facebox_unknown_directory + filename_without_path
self.log("Move file from {} to {}".format(filename, new_filename))
shutil.move(filename, new_filename)
return identifier
def _moveFilesFromUnkownToDirectoryByIdentifier(self, directory, identifier):
"""Copy all files in the unknown folder which belong to an identifier (a timestamp) to a new directory"""
if not os.path.exists(directory):
os.makedirs(directory)
for file in os.listdir(self.facebox_unknown_directory):
if identifier in file:
filename = os.path.join(self.facebox_unknown_directory, file)
new_filename = os.path.join(directory, file)
self.log("Move file from {} to {}".format(filename, new_filename))
shutil.move(filename, new_filename)
def _getKnownFaces(self):
"""Return a list of known face names.
Iterates over the subdirectory names of facebox_known_faces_directory"""
return self.list_folders(self.facebox_known_faces_directory)
def list_folders(self, directory):
"""Returns a list of folders
These are not full paths, just the folder."""
folders = [
dir
for dir in os.listdir(directory)
if os.path.isdir(os.path.join(directory, dir))
and not dir.startswith(directory)
and not dir.startswith(".")
]
folders.sort(key=str.lower)
return folders
def post_image(self, url, image):
"""Post an image to the classifier."""
try:
response = requests.post(
url, files={"file": open(image, "rb")}, timeout=self.check_health_timeout
)
return response
except requests.exceptions.ConnectionError:
self.log("ConnectionError")
except requests.exceptions.ReadTimeout:
self.log("ReadTimeout")
self.check_health_timeout = self.check_health_timeout * 1.5
self.log(f"Setting Health Check Timeout to {self.check_health_timeout}")
###############################################################
# Telegram Bot
###############################################################
def ask_for_name(self, identifier):
"""Asks the user if he knows the face in the photo.
The identifier is needed to link the user reply back to this message"""
self.log("Asking for name")
keyboard = [[("Unbekannt", "/unkown" + IDENTIFIER_DELIMITER + identifier)]]
for face in self._getKnownFaces():
keyboard.append([(face, "/" + face + IDENTIFIER_DELIMITER + identifier)])
self.log("keyboard is: {}".format(keyboard), level="DEBUG")
self.call_service(
"telegram_bot/send_message",
target=self.user_id,
message=self.message_unkown_face,
inline_keyboard=keyboard,
)
def receive_telegram_callback(self, event_name, data, kwargs):
"""Event listener for Telegram callback queries."""
self.log("callback data: {}".format(data))
data_callback = data["data"]
callback_id = data["id"]
chat_id = data["chat_id"]
message_id = data["message"]["message_id"]
text = data["message"]["text"]
from_first = data["from_first"]
for face in self._getKnownFaces():
if data_callback.startswith("/" + face + IDENTIFIER_DELIMITER):
self.log("Received Telegram Callback for {}".format(face))
self.call_service(
"telegram_bot/answer_callback_query",
message="Dankeschön!",
callback_query_id=callback_id,
)
self.call_service(
"telegram_bot/edit_message",
chat_id=chat_id,
message_id=message_id,
message=self.message_name_provided_callback.format(
from_first, face
),
inline_keyboard=[],
)
identifier = data_callback.split(IDENTIFIER_DELIMITER)[1]
directory = self.facebox_known_faces_directory + face
self._moveFilesFromUnkownToDirectoryByIdentifier(directory, identifier)
self.teach_name_by_directory(face, directory)
if data_callback.startswith("/unkown"):
# Answer callback query
self.call_service(
"telegram_bot/answer_callback_query",
message="Dankeschön!",
callback_query_id=callback_id,
)
self.call_service(
"telegram_bot/edit_message",
chat_id=chat_id,
message_id=message_id,
message=text,
inline_keyboard=[],
)
self.notifier.notify(
self.notify_name,
self.message_provide_name.format(PROVIDE_NAME_TIMEOUT),
useAlexa=False,
)
self.provide_name_timeout_start = datetime.datetime.now()
self.last_identifier = data_callback.split(IDENTIFIER_DELIMITER)[1]
self.last_message_id = message_id
self.last_from_first = from_first
def receive_telegram_text(self, event_name, data, kwargs):
"""Telegram text listener"""
self.log("callback data: {}".format(data), level="DEBUG")
chat_id = data["chat_id"]
text = data["text"]
if self.provide_name_timeout_start != None and (
datetime.datetime.now() - self.provide_name_timeout_start
< datetime.timedelta(minutes=PROVIDE_NAME_TIMEOUT)
):
# Edit the last ask_for_name message
self.call_service(
"telegram_bot/edit_message",
chat_id=chat_id,
message_id=self.last_message_id,
message=self.message_name_provided_callback.format(
self.last_from_first, text
),
inline_keyboard=[],
)
# Say thanks
self.notifier.notify(
self.notify_name,
self.message_name_provided.format(text),
useAlexa=False,
)
# Copy files to new directory
directory = self.facebox_known_faces_directory + text
self._moveFilesFromUnkownToDirectoryByIdentifier(
directory, self.last_identifier
)
self.teach_name_by_directory(text, directory)
else:
self.log("PROVIDE_NAME_TIMEOUT exceeded")
def terminate(self):
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
for listen_event_handle in self.listen_event_handle_list:
self.cancel_listen_event(listen_event_handle)
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: faceRecognitionBot/faceRecognitionBot.yaml
================================================
# faceRecognitionBot:
# module: faceRecognitionBot
# class: FaceRecognitionBot
# app_switch: input_boolean.facebox_notifier
# sensor: binary_sensor.contact_door
# button: binary_sensor.switch_158d000215aa28
# camera: camera.android_ip_webcam_door
# local_file_camera: camera.saved_image
# filename: !secret facebox_notifier_filename
# image_processing: image_processing.facebox
# notify_name: group_notifications
# wol_switch: switch.facebox_wol
# user_id: !secret telegram_user_id
# facebox_source_directory: !secret facebox_folderpath
# facebox_unknown_directory: !secret facebox_unknown_directory
# facebox_noface_directory: !secret facebox_noface_directory
# facebox_known_faces_directory: !secret facebox_known_faces_directory
# facebox_healthcheck_filename: !secret facebox_healthcheck_filename
# healthcheck_face_name: Kevin
# number_of_images: 10
# waitBeforeSnapshot: 1
# ip: !secret facebox_ip
# port: !secret facebox_port
# message_face_identified: "Ich habe {} erkannt"
# #message_face_identified: "I have recognized {}."
# message_unkown_face: "Ich habe dieses Gesicht nicht erkannt. Kennst du es?"
# #message_unkown_face: "I have not recognized this face. Do you know it?"
# message_unkown_face_with_known: "Ich habe auch ein unbekanntes Gesicht entdeckt."
# #message_unkown_face_with_known: "I have also discovered an unknown face."
# 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!"
# #message_provide_name: "If you know the face you can write the name to me within the next {} minutes. I will remember it!"
# message_name_provided: "Okay. Ich merke mir, dass das {} ist"
# #message_name_provided: "Okay. I will remember that this is {}"
# message_name_provided_callback: "{} sagte, dass dies {} ist."
# #message_name_provided_callback: "{} said that this is {}"
# dependencies:
# - Notifier
================================================
FILE: globals.py
================================================
import random
def random_arg(argList):
############################################
# pick a random text from a list
############################################
if isinstance(argList, list):
text = random.choice(argList)
else:
text = argList
return text
================================================
FILE: heartbeat/heartbeat.py
================================================
import appdaemon.plugins.hass.hassapi as hass
from requests.exceptions import HTTPError
#
# App which sets a homeassistant entity as a heartbeat to check for threadstarvation etc
#
# Args:
# sensor: sensor.appdaemon_heartbeat
#
# Release Notes
#
# Version 1.1:
# Set start to None run_minutely will run after 1 minute
#
# Version 1.0:
# Initial Version
class Heartbeat(hass.Hass):
def initialize(self):
self.timer_handle_list = []
self.sensor = self.args["sensor"]
self.heartbeat(None)
self.timer_handle_list.append(self.run_minutely(self.heartbeat, start=None))
def heartbeat(self, kwargs):
try:
self.set_state(self.sensor, state=str(self.time()))
self.log("Heartbeat", level="DEBUG")
except HTTPError as exception:
self.log(
"Error trying to set entity. Will try again in 5s. Error: {}".format(
exception
),
level="WARNING",
)
self.timer_handle_list.append(self.run_in(self.heartbeat, 5))
def terminate(self):
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: heartbeat/heartbeat.yaml
================================================
heartbeat:
module: heartbeat
class: Heartbeat
sensor: sensor.appdaemon_heartbeat
================================================
FILE: homeArrivalNotifier/homeArrivalNotifier.py
================================================
import appdaemon.plugins.hass.hassapi as hass
#
# App to send a notification if someone arrives at home
#
# Args:
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# input_boolean: input boolean which holds the information of someone is home or not
# notify_name: Who to notify
# user_name: name to use in notification message
# zone_name: Name of the zone
# message: message to use in notification
# Release Notes
#
# Version 1.4.1:
# Use consistent message variable
#
# Version 1.4:
# use Notify App
#
# Version 1.3:
# message now directly in own yaml instead of message module
#
# Version 1.2:
# Added app_switch
#
# Version 1.1:
# Added user_name
#
# Version 1.0:
# Initial Version
class HomeArrivalNotifier(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.app_switch = self.args["app_switch"]
self.zone_name = self.args["zone_name"]
self.input_boolean = self.args["input_boolean"]
self.notify_name = self.args["notify_name"]
self.user_name = self.args["user_name"]
self.message = self.args["message"]
self.notifier = self.get_app("Notifier")
self.listen_state_handle_list.append(
self.listen_state(self.state_change, self.input_boolean)
)
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new != "" and new != old:
self.log("{} changed from {} to {}".format(entity, old, new))
if new == "on":
self.log(
"{} arrived at {}".format(self.notify_name, self.zone_name)
)
self.notifier.notify(
self.notify_name, self.message.format(self.user_name)
)
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: homeArrivalNotifier/homeArrivalNotifier.yaml
================================================
#Notification if user one arrives at home
# homeArrivalNotifierUserOne:
# module: homeArrivalNotifier
# class: HomeArrivalNotifier
# app_switch: input_boolean.home_arrival_notifier_user_one
# input_boolean: input_boolean.user_one_home
# notify_name: group_notifications
# user_name: Kevin
# zone_name: Home
# message: "Willkommen zu Hause {}."
# #message: "Welcome Home {}."
# dependencies:
# - Notifier
# #Notification if user two arrives at home
# homeArrivalNotifierUserTwo:
# module: homeArrivalNotifier
# class: HomeArrivalNotifier
# app_switch: input_boolean.home_arrival_notifier_user_two
# input_boolean: input_boolean.user_two_home
# notify_name: group_notifications
# user_name: Sina
# zone_name: Home
# message: "Willkommen zu Hause {}."
# #message: "Welcome Home {}."
# dependencies:
# - Notifier
================================================
FILE: isHomeDeterminer/isHomeDeterminer.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import globals
#
# App to
#
# Args:
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# input_booleans: list of input boolean which determine if a user is home
# ishome: input boolean which determins if someone is home
# message: message to use in notification
# Release Notes
#
# Version 1.3:
# message now a list
#
# Version 1.2:
# message now directly in own yaml instead of message module
#
# Version 1.1:
# Added app_switch
#
# Version 1.0:
# Initial Version
class IsHomeDeterminer(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.app_switch = self.args["app_switch"]
self.ishome = self.args["ishome"]
self.input_booleans = self.args["input_booleans"].split(",")
self.message = self.args["message"]
if self.get_state(self.app_switch) == "on":
for input_boolean in self.input_booleans:
self.log(
"{} is {}".format(input_boolean, self.get_state(input_boolean))
)
self.listen_state_handle_list.append(
self.listen_state(self.state_change, input_boolean)
)
if (
self.get_state(input_boolean) == "on"
and self.get_state(self.ishome) == "off"
):
self.turn_on(self.ishome)
self.log("Setting {} to on".format(self.ishome))
if (
self.get_state(input_boolean) == "off"
and self.get_state(self.ishome) == "on"
):
if self.are_others_away(input_boolean):
self.turn_off(self.ishome)
self.log("Setting {} to off".format(self.ishome))
notify_message = globals.random_arg(self.message)
self.log("notify_messsage: {}".format(notify_message))
self.call_service(
"notify/group_notifications", message=notify_message
)
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new != "" and new != old:
self.log("{} changed from {} to {}".format(entity, old, new))
if new == "on":
self.turn_on(self.ishome)
self.log("Setting {} to on".format(self.ishome))
if new == "off":
if self.are_others_away(entity):
self.turn_off(self.ishome)
self.log("Setting {} to off".format(self.ishome))
notify_message = globals.random_arg(self.message)
self.log("notify_messsage: {}".format(notify_message))
self.call_service(
"notify/group_notifications", message=notify_message
)
def are_others_away(self, entity):
self.log("Entity: {}".format(entity))
for input_boolean in self.input_booleans:
self.log("{} is {}".format(input_boolean, self.get_state(input_boolean)))
if input_boolean == entity:
pass
elif self.get_state(input_boolean) == "on":
self.log("{} is still at on".format(input_boolean))
return False
self.log("Everybody not home")
return True
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
================================================
FILE: isHomeDeterminer/isHomeDeterminer.yaml
================================================
# #Control the isHome state. Determines if someone is home or all persons are away
# isHomeDeterminer:
# module: isHomeDeterminer
# class: IsHomeDeterminer
# app_switch: input_boolean.is_home_determiner
# ishome: input_boolean.is_home
# input_booleans: input_boolean.user_one_home,input_boolean.user_two_home
# message:
# - "Es ist keiner mehr zu Hause."
# - "Keiner mehr da? Panda Party!"
# - "Ich passe auf die Wohnung auf, einen schönen Tag"
# - "Tschüss, bis nachher"
# #message: "Everyone left home. Setting isHome to off"
# global_dependencies:
# - globals
# - secrets
================================================
FILE: isUserHomeDeterminer/isUserHomeDeterminer.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
from requests.exceptions import HTTPError
#
# App to toggle an input boolean when a person enters or leaves home.
# This is determined based on a combination of a GPS device tracker and the door sensor.
#
# - If the door sensor opens and the device_tracker changed to "home" in the last self.delay minutes
# this means someone got home
# - If the door sensor opens and the device_tracker changes to "not_home" after that
# this means someone left home
#
# Args:
#
# app_switch: on/off switch for this app. example: input_boolean.turn_fan_on_when_hot
# input_boolean: input_boolean which shows if someone is home e.g. input_boolean.isHome
# device_tracker: device tracker or person of the user to track e.g. device_tracker.simon
# door_sensor: Door sensor which indicated the front door opened e.g. binary_sensor.door_window_sensor_158d000126a57b
#
# Release Notes
#
# Version 1.5:
# Wait to leave home until door is opened again
#
# Version 1.4.3:
# check for listen_state_callback == None before triggering again
#
# Version 1.4.2:
# cancel listen callback only when its not None
#
# Version 1.4.1:
# fix for 503, fix for listen callback not being cancelled correctly
#
# Version 1.4:
# message now directly in own yaml instead of message module
#
# Version 1.3:
# Added app_switch
#
# Version 1.2:
# Change checking after a delay to a event based system
#
# Version 1.1:
# Set when initializing (also when HA restarts)
#
# Version 1.0:
# Initial Version
class IsUserHomeDeterminer(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.timer_handle_list = []
self.delay = 600
self.app_switch = self.args["app_switch"]
self.input_boolean = self.args["input_boolean"]
self.device_tracker = self.args["device_tracker"]
self.door_sensor = self.args["door_sensor"]
device_tracker_state = self.get_state(self.device_tracker, attribute="all")
if self.get_state(self.app_switch) == "on":
if device_tracker_state["state"] == "home":
self.log("User is home")
self.timer_handle_list.append(
self.run_in(
self.turn_on_callback, 0, turn_on_entity=self.input_boolean
)
)
else:
self.log("User is not home")
self.timer_handle_list.append(
self.run_in(
self.turn_off_callback, 0, turn_off_entity=self.input_boolean
)
)
self.listen_state_handle_list.append(
self.listen_state(self.state_change, self.door_sensor)
)
self.listen_state_handle = None
def state_change(self, entity, attribute, old, new, kwargs):
if self.get_state(self.app_switch) == "on":
if new != "" and new != old:
self.log("{} changed from {} to {}".format(entity, old, new))
if new == "on" and old == "off":
self.cancel_listen_state_callback(None)
device_tracker_state = self.get_state(
self.device_tracker, attribute="all"
)
self.log("device_tracker_state: {}".format(device_tracker_state),)
last_changed = device_tracker_state["last_changed"]
self.log("last_changed: {}".format(last_changed))
# User got home: Device tracker changed to home before door sensor triggered
if device_tracker_state["state"] == "home" and (
(
datetime.datetime.now(datetime.timezone.utc)
- self.convert_utc(last_changed)
)
< datetime.timedelta(seconds=self.delay)
):
self.log("User got home")
self.turn_on(self.input_boolean)
# User got home: Device tracker is still not home.
# Wait if it changes to home in the next self.delay seconds
elif device_tracker_state["state"] != "home":
self.log("Wait for device tracker to change to 'home'")
self.listen_state_handle = self.listen_state(
self.check_if_user_got_home, self.device_tracker
)
self.listen_state_handle_list.append(self.listen_state_handle)
self.timer_handle_list.append(
self.run_in(self.cancel_listen_state_callback, self.delay)
)
# User left home: Device tracker is still home.
# Wait if it changes to not_home
elif device_tracker_state["state"] == "home":
self.log("Wait for device tracker to change to 'not_home'")
self.listen_state_handle = self.listen_state(
self.check_if_user_left_home, self.device_tracker
)
self.listen_state_handle_list.append(self.listen_state_handle)
def cancel_listen_state_callback(self, kwargs):
if self.listen_state_handle is not None:
self.log(
"Timeout while waiting for user to get/leave home. Cancel listen_state"
)
if self.listen_state_handle in self.listen_state_handle_list:
self.listen_state_handle_list.remove(self.listen_state_handle)
self.cancel_listen_state(self.listen_state_handle)
self.listen_state_handle = None
def check_if_user_left_home(self, entity, attribute, old, new, kwargs):
if new != "home":
self.log("User left home")
if self.listen_state_handle in self.listen_state_handle_list:
self.listen_state_handle_list.remove(self.listen_state_handle)
if self.listen_state_handle != None:
self.cancel_listen_state(self.listen_state_handle)
self.listen_state_handle = None
self.timer_handle_list.append(
self.run_in(
self.turn_off_callback, 1, turn_off_entity=self.input_boolean
)
)
def check_if_user_got_home(self, entity, attribute, old, new, kwargs):
if new == "home":
self.log("User got home")
if self.listen_state_handle in self.listen_state_handle_list:
self.listen_state_handle_list.remove(self.listen_state_handle)
if self.listen_state_handle is not None:
self.cancel_listen_state(self.listen_state_handle)
self.listen_state_handle = None
self.timer_handle_list.append(
self.run_in(
self.turn_on_callback, 1, turn_on_entity=self.input_boolean
)
)
def turn_on_callback(self, kwargs):
"""This is needed because the turn_on command can result in a HTTP 503 when homeassistant is restarting"""
try:
self.turn_on(kwargs["turn_on_entity"])
except HTTPError as exception:
self.log(
"Error trying to turn on entity. Will try again in 1s. Error: {}".format(
exception
),
level="WARNING",
)
self.timer_handle_list.append(
self.run_in(
self.turn_on_callback, 1, turn_on_entity=kwargs["turn_on_entity"]
)
)
def turn_off_callback(self, kwargs):
"""This is needed because the turn_off command can result in a HTTP 503 when homeassistant is restarting"""
try:
self.turn_off(kwargs["turn_off_entity"])
except HTTPError as exception:
self.log(
"Error trying to turn off entity. Will try again in 1s. Error: {}".format(
exception
),
level="WARNING",
)
self.timer_handle_list.append(
self.run_in(
self.turn_off_callback, 1, turn_off_entity=kwargs["turn_off_entity"]
)
)
def terminate(self):
for listen_state_handle in self.listen_state_handle_list:
self.cancel_listen_state(listen_state_handle)
for timer_handle in self.timer_handle_list:
self.cancel_timer(timer_handle)
================================================
FILE: isUserHomeDeterminer/isUserHomeDeterminer.yaml
================================================
# #Determine if user one gets/leaves home
# isUserHomeDeterminerUserOne:
# module: isUserHomeDeterminer
# class: IsUserHomeDeterminer
# app_switch: input_boolean.is_user_home_determiner_user_one
# input_boolean: input_boolean.user_one_home
# device_tracker: person.kevin
# door_sensor: binary_sensor.contact_door
# #Determine if user two gets/leaves home
# isUserHomeDeterminerUserTwo:
# module: isUserHomeDeterminer
# class: IsUserHomeDeterminer
# app_switch: input_boolean.is_user_home_determiner_user_two
# input_boolean: input_boolean.user_two_home
# device_tracker: person.sina
# door_sensor: binary_sensor.contact_door
================================================
FILE: leavingZoneNotifier/leavingZoneNotifier.py
================================================
import appdaemon.plugins.hass.hassapi as hass
import datetime
#
# App to notify if user_one is leaving a zone.
# User had to be in that zone 3 minutes before
# in order for the notification to be triggered
#
# Args:
# app_switch: on/off switch for this app.
# example: input_boolean.turn_fan_on_when_hot
# device: Device to track
# user_name: Name of the user used in the notification message
# delay: seconds to wait before notifying. Maybe user returns to zone.
# This should be too small to avoid false positives from your tracker.
# I am using GPS Logger on Android and sometimes my device switches
# from work to home and 2 minutes later back. example: 120
# lingering_time: time a user has to be in a zone to trigger this app.
# example: 3600
# zone: zone name from which the user is leaving
# notify_name: Who to notify. example: group_notifications
# message: localized message to use in notification
# travel_time_sensor (optional): Sensor showing the travel time home.
# example: sensor.travel_time_home_user_one
# travel_time_sensor_message (optional): Additional notify message.
#
# Release Notes
#
# Version 1.11:
# Catch new state might be None
#
# Version 1.10:
# Catch old state might be None during startup
#
# Version 1.9:
# PEP8 style and log message when updating travel_time_sensor
#
# Version 1.8:
# Add travel time in notification message
#
# Version 1.7.1:
# Fix delay in notify message. Only input the minutes not the tuple
#
# Version 1.7:
# use Notify App
#
# Version 1.6:
# notify message includes delay
#
# Version 1.5:
# message now directly in own yaml instead of message module
#
# Version 1.4:
# additional note for delay and better handling of zone_entered for
# false positives
#
# Version 1.3:
# delay and lingering_time now as args
#
# Version 1.2:
# Added app_switch
#
# Version 1.1:
# Rework without proximity
#
# Version 1.0:
# Initial Version
class LeavingZoneNotifier(hass.Hass):
def initialize(self):
self.listen_state_handle_list = []
self.timer_handle_list = []
self.app_switch = self.args["app_switch"]
self.user_name = self.args["user_name"]
self.zone = self.args["zone"]
self.notify_name = self.args["notify_name"]
self.device = self.args["device"]
# 'lingering_time' the time a user has to stay in a zone
# for this app to trigger
self.lingering_time = self.args["lingering_time"]
self.delay = self.args["delay"]
self.message = self.args["message"]
self.travel_time_sensor = self.args.get("travel_time_sensor")
self.travel_time_sensor_message = self.args.get("travel_time_sensor_message")
self.user_entered_zone = None
self.false_positive = False
self.notifier = self.get_app("Notifier")
self.listen_state_handle_list.append(
self.listen_state(self.zone_state_change, self.device, attribute="all")
)
def zone_state_change(self, entity, attributes, old, new, kwargs):
"""Check if user entered or left a zone."""
if self.get_state(self.app_switch) == "on":
if new is not None:
last_changed = self.convert_utc(new["last_changed"])
gitextract_92xa7u3p/
├── .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/
│ └── deconz_xiaomi_button.py
├── deconz_xiaomi_button.yaml
├── 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
├── 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
SYMBOL INDEX (276 symbols across 41 files)
FILE: ad-ench/ench.py
function hl (line 51) | def hl(text: Union[int, float, str]) -> str:
function hl_entity (line 55) | def hl_entity(entity: str) -> str:
class EnCh (line 62) | class EnCh(hass.Hass): # type: ignore
method lg (line 65) | def lg(self, msg: str, *args: Any, icon: Optional[str] = None, repeat:...
method initialize (line 70) | async def initialize(self) -> None:
method check_battery (line 190) | async def check_battery(self, _: Any) -> None:
method check_unavailable (line 249) | async def check_unavailable(self, _: Any) -> None:
method check_stale (line 300) | async def check_stale(self, _: Any) -> None:
method choose_notify_recipient (line 356) | def choose_notify_recipient(self, check: str, config: Dict[str, Any]) ...
method last_update (line 360) | async def last_update(self, entity_id: str) -> Any:
method _name (line 363) | async def _name(self, entity: str, friendly_name: bool = False, notifi...
method _print_result (line 376) | def _print_result(self, check: str, entities: List[str], reason: str) ...
method update_sensor (line 382) | async def update_sensor(self, check_name: str, entities: List[str]) ->...
method show_info (line 404) | def show_info(self, config: Optional[Dict[str, Any]] = None) -> None:
method print_collection (line 443) | def print_collection(self, key: str, collection: Iterable[Any], indent...
method _print_cfg_setting (line 463) | def _print_cfg_setting(self, key: str, value: Union[int, str], indenta...
FILE: alarmClock/alarmClock.py
class AlarmClock (line 43) | class AlarmClock(hass.Hass):
method initialize (line 44) | def initialize(self):
method alarm_change (line 83) | def alarm_change(self, entity, attributes, old, new, kwargs):
method naturalwakeup_change (line 93) | def naturalwakeup_change(self, entity, attributes, old, new, kwargs):
method add_timer (line 103) | def add_timer(self):
method trigger_alarm (line 124) | def trigger_alarm(self, kwargs):
method button_clicked (line 153) | def button_clicked(self, event_name, data, kwargs):
method run_fade_in (line 177) | def run_fade_in(self, kwargs):
method run_alarm (line 218) | def run_alarm(self, kwargs):
method terminate (line 222) | def terminate(self):
FILE: alexa/alexa_api.py
class alexa_api (line 6) | class alexa_api(hass.Hass):
method initialize (line 7) | def initialize(self):
method api_call (line 11) | def api_call(self, data):
method my_alexa_interpret_data (line 123) | def my_alexa_interpret_data(self, data):
method my_alexa_intent_name (line 184) | def my_alexa_intent_name(self, data):
method my_alexa_dialog_state (line 197) | def my_alexa_dialog_state(self, data):
method my_alexa_intent (line 206) | def my_alexa_intent(self, data):
method my_alexa_request_type (line 215) | def my_alexa_request_type(self, data):
method my_alexa_error (line 224) | def my_alexa_error(self, data):
method my_alexa_slot_value (line 237) | def my_alexa_slot_value(self, data, slot):
method my_alexa_response (line 252) | def my_alexa_response(
method random_arg (line 280) | def random_arg(self, argName):
method floatToStr (line 290) | def floatToStr(self, myfloat):
method cleanup_text (line 298) | def cleanup_text(self, text):
method alexalog (line 311) | def alexalog(self, logtext, repeat=0, surrounding=""):
method alexaresponselog (line 333) | def alexaresponselog(self, logtext):
method getIntentResponse (line 346) | def getIntentResponse(self):
FILE: alexa/lightState/lightStateIntent.py
class lightStateIntent (line 5) | class lightStateIntent(hass.Hass):
method initialize (line 6) | def initialize(self):
method getIntentResponse (line 9) | def getIntentResponse(self, slots, devicename):
method floatToStr (line 31) | def floatToStr(self, myfloat):
FILE: alexa/listService/listService.py
class ListService (line 22) | class ListService(hass.Hass):
method initialize (line 23) | def initialize(self):
method getSwitchable (line 26) | def getSwitchable(self):
method getTemperature (line 29) | def getTemperature(self):
method getDoor (line 32) | def getDoor(self):
method getWindow (line 35) | def getWindow(self):
method getDoorTilted (line 38) | def getDoorTilted(self):
FILE: alexa/nextBus/nextBusIntent.py
class nextBusIntent (line 5) | class nextBusIntent(hass.Hass):
method initialize (line 6) | def initialize(self):
method getIntentResponse (line 11) | def getIntentResponse(self, slots, devicename):
FILE: alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.py
class RemindMeOfXWhenZoneIntent (line 8) | class RemindMeOfXWhenZoneIntent(hass.Hass):
method initialize (line 9) | def initialize(self):
method getIntentResponse (line 20) | def getIntentResponse(self, slots, devicename):
method remind_callback (line 57) | def remind_callback(self, entity, attribute, old, new, kwargs):
method terminate (line 75) | def terminate(self):
FILE: alexa/temperatureState/temperatureStateIntent.py
class temperatureStateIntent (line 5) | class temperatureStateIntent(hass.Hass):
method initialize (line 6) | def initialize(self):
method getIntentResponse (line 9) | def getIntentResponse(self, slots, devicename):
method floatToStr (line 31) | def floatToStr(self, myfloat):
method random_arg (line 39) | def random_arg(self, argName):
FILE: alexa/turnEntityOffInX/turnEntityOffInXIntent.py
class TurnEntityOffInXIntent (line 7) | class TurnEntityOffInXIntent(hass.Hass):
method initialize (line 8) | def initialize(self):
method getIntentResponse (line 13) | def getIntentResponse(self, slots, devicename):
method turn_off_callback (line 59) | def turn_off_callback(self, kwargs):
method random_arg (line 64) | def random_arg(self, argName):
method terminate (line 74) | def terminate(self):
FILE: alexa/windowsOpen/windowsOpenIntent.py
class WindowsOpenIntent (line 7) | class WindowsOpenIntent(hass.Hass):
method initialize (line 8) | def initialize(self):
method getIntentResponse (line 12) | def getIntentResponse(self, slots, devicename):
method random_arg (line 78) | def random_arg(self, argName):
FILE: alexaSpeakerConnector/alexaSpeakerConnector.py
class AlexaSpeakerConnector (line 32) | class AlexaSpeakerConnector(hass.Hass):
method initialize (line 33) | def initialize(self):
method state_change (line 47) | def state_change(self, entity, attribute, old, new, kwargs):
method run_in_callback (line 73) | def run_in_callback(self, kwargs):
method terminate (line 89) | def terminate(self):
FILE: appWatcher/appWatcher.py
class AppWatcher (line 17) | class AppWatcher(hass.Hass):
method initialize (line 18) | def initialize(self):
method log_message_callback (line 31) | def log_message_callback(self, app_name, ts, level, log_type, message,...
method terminate (line 41) | def terminate(self):
FILE: buttonClicked/buttonClicked.py
class ButtonClicked (line 25) | class ButtonClicked(hass.Hass):
method initialize (line 26) | def initialize(self):
method event_detected (line 40) | def event_detected(self, event_name, data, kwargs):
method dimmer_callback (line 121) | def dimmer_callback(self, kwargs):
method turn_off_workaround (line 140) | def turn_off_workaround(self, *kwargs):
method terminate (line 143) | def terminate(self):
FILE: comingHome/comingHome.py
class ComingHome (line 46) | class ComingHome(hass.Hass):
method initialize (line 47) | def initialize(self):
method state_change (line 64) | def state_change(self, entity, attribute, old, new, kwargs):
method turn_on_actor (line 86) | def turn_on_actor(self, actor, entity, new):
method my_call_service (line 91) | def my_call_service(self, service, service_data, entity, new):
method terminate (line 97) | def terminate(self):
FILE: deconz_xiaomi_button/deconz_xiaomi_button.py
class DeconzXiaomiButton (line 29) | class DeconzXiaomiButton(hass.Hass):
method initialize (line 30) | def initialize(self):
method event_detected (line 45) | def event_detected(self, event_name, data, kwargs):
method dimmer_callback (line 87) | def dimmer_callback(self, kwargs):
method terminate (line 106) | def terminate(self):
FILE: detectWrongState/detectWrongState.py
class DetectWrongState (line 66) | class DetectWrongState(hass.Hass):
method initialize (line 67) | def initialize(self):
method state_change (line 95) | def state_change(self, entity, attribute, old, new, kwargs):
method check_entities_should_be_off (line 105) | def check_entities_should_be_off(self):
method check_entities_should_be_on (line 118) | def check_entities_should_be_on(self):
method is_entity_reed_contact (line 129) | def is_entity_reed_contact(self, entity):
method send_notification (line 139) | def send_notification(self, message, entity):
method terminate (line 148) | def terminate(self):
FILE: eventMonitor/eventMonitor.py
class Monitor (line 10) | class Monitor(hass.Hass):
method initialize (line 11) | def initialize(self):
method changed (line 26) | def changed(self, event_name, data, kwargs):
method terminate (line 29) | def terminate(self):
FILE: faceRecognitionBot/faceRecognitionBot.py
class FaceRecognitionBot (line 57) | class FaceRecognitionBot(hass.Hass):
method initialize (line 58) | def initialize(self):
method check_health_callback (line 162) | def check_health_callback(self, kwargs):
method learn_faces_event_callback (line 183) | def learn_faces_event_callback(self, event_name, data, kwargs):
method teach_name_by_file (line 188) | def teach_name_by_file(self, teach_url, name, file_path):
method check_classifier_health (line 208) | def check_classifier_health(self):
method check_if_trained (line 240) | def check_if_trained(self, kwargs):
method teach_faces (line 257) | def teach_faces(self, folderpath, exclude_folders=[]):
method teach_name_by_directory (line 271) | def teach_name_by_directory(self, name, folderpath):
method button_clicked (line 282) | def button_clicked(self, event_name, data, kwargs):
method triggered (line 290) | def triggered(self, entity, attribute, old, new, kwargs):
method takeSnapshots (line 298) | def takeSnapshots(self, kwargs):
method processImages (line 318) | def processImages(self, kwargs):
method _processUnkownFaceFound (line 428) | def _processUnkownFaceFound(self, result_dict_dict):
method _notifyUnkownFaceFound (line 451) | def _notifyUnkownFaceFound(self, result_dict_dict):
method _getMaxCountFromResult (line 462) | def _getMaxCountFromResult(self, result_dict_dict):
method _getFaceNamesFromResult (line 467) | def _getFaceNamesFromResult(self, result_dict_dict):
method _getFileWithUnknownFaceFromResult (line 493) | def _getFileWithUnknownFaceFromResult(self, result_dict_dict):
method _determineIfSameUnkownFace (line 502) | def _determineIfSameUnkownFace(self, result_dict_dict):
method _moveFilesToUnknown (line 534) | def _moveFilesToUnknown(self, result_dict_dict):
method _moveFilesFromUnkownToDirectoryByIdentifier (line 552) | def _moveFilesFromUnkownToDirectoryByIdentifier(self, directory, ident...
method _getKnownFaces (line 563) | def _getKnownFaces(self):
method list_folders (line 569) | def list_folders(self, directory):
method post_image (line 582) | def post_image(self, url, image):
method ask_for_name (line 600) | def ask_for_name(self, identifier):
method receive_telegram_callback (line 615) | def receive_telegram_callback(self, event_name, data, kwargs):
method receive_telegram_text (line 671) | def receive_telegram_text(self, event_name, data, kwargs):
method terminate (line 706) | def terminate(self):
FILE: globals.py
function random_arg (line 4) | def random_arg(argList):
FILE: heartbeat/heartbeat.py
class Heartbeat (line 19) | class Heartbeat(hass.Hass):
method initialize (line 20) | def initialize(self):
method heartbeat (line 29) | def heartbeat(self, kwargs):
method terminate (line 42) | def terminate(self):
FILE: homeArrivalNotifier/homeArrivalNotifier.py
class HomeArrivalNotifier (line 34) | class HomeArrivalNotifier(hass.Hass):
method initialize (line 35) | def initialize(self):
method state_change (line 51) | def state_change(self, entity, attribute, old, new, kwargs):
method terminate (line 63) | def terminate(self):
FILE: isHomeDeterminer/isHomeDeterminer.py
class IsHomeDeterminer (line 27) | class IsHomeDeterminer(hass.Hass):
method initialize (line 28) | def initialize(self):
method state_change (line 63) | def state_change(self, entity, attribute, old, new, kwargs):
method are_others_away (line 80) | def are_others_away(self, entity):
method terminate (line 92) | def terminate(self):
FILE: isUserHomeDeterminer/isUserHomeDeterminer.py
class IsUserHomeDeterminer (line 51) | class IsUserHomeDeterminer(hass.Hass):
method initialize (line 52) | def initialize(self):
method state_change (line 86) | def state_change(self, entity, attribute, old, new, kwargs):
method cancel_listen_state_callback (line 128) | def cancel_listen_state_callback(self, kwargs):
method check_if_user_left_home (line 138) | def check_if_user_left_home(self, entity, attribute, old, new, kwargs):
method check_if_user_got_home (line 152) | def check_if_user_got_home(self, entity, attribute, old, new, kwargs):
method turn_on_callback (line 166) | def turn_on_callback(self, kwargs):
method turn_off_callback (line 183) | def turn_off_callback(self, kwargs):
method terminate (line 200) | def terminate(self):
FILE: leavingZoneNotifier/leavingZoneNotifier.py
class LeavingZoneNotifier (line 70) | class LeavingZoneNotifier(hass.Hass):
method initialize (line 71) | def initialize(self):
method zone_state_change (line 98) | def zone_state_change(self, entity, attributes, old, new, kwargs):
method notify_user (line 136) | def notify_user(self, kwargs):
method notify_user_callback (line 161) | def notify_user_callback(self, kwargs):
method terminate (line 173) | def terminate(self):
FILE: motionTrigger/motionTrigger.py
class MotionTrigger (line 67) | class MotionTrigger(hass.Hass):
method initialize (line 68) | def initialize(self):
method delay_changed (line 138) | def delay_changed(self, entity, attribute, old, new, kwargs):
method motion_event_detected (line 142) | def motion_event_detected(self, event_name, data, kwargs):
method state_changed (line 147) | def state_changed(self, entity, attribute, old, new, kwargs):
method turn_on_callback (line 152) | def turn_on_callback(self, kwargs):
method turn_off_callback (line 188) | def turn_off_callback(self, kwargs):
method reset_timer (line 214) | def reset_timer(self):
method terminate (line 223) | def terminate(self):
FILE: newWifiDeviceNotify/newWifiDeviceNotify.py
class DeviceNotify (line 52) | class DeviceNotify(hass.Hass):
method initialize (line 53) | def initialize(self):
method entityRegistryUpdatedCallback (line 87) | def entityRegistryUpdatedCallback(self, event_name, data, kwargs):
method handleNewRegistryEntity (line 97) | def handleNewRegistryEntity(self, kwargs):
method newDeviceCallback (line 113) | def newDeviceCallback(self, event_name, data, kwargs):
method notifyNewDeviceAdded (line 121) | def notifyNewDeviceAdded(self, host_name, mac):
method askForProfileChange (line 126) | def askForProfileChange(self, host_name):
method receiveTelegramCallback (line 148) | def receiveTelegramCallback(self, event_name, data, kwargs):
method allowDevice (line 198) | def allowDevice(self, host_name):
method terminate (line 235) | def terminate(self):
FILE: nextAppointmentLeaveNotifier/nextAppointmentLeaveNotifier.py
class NextAppointmentLeaveNotifier (line 43) | class NextAppointmentLeaveNotifier(hass.Hass):
method initialize (line 44) | def initialize(self):
method state_change (line 70) | def state_change(self, entity, attributes, old, new, kwargs):
method set_timer_handle (line 79) | def set_timer_handle(self):
method notify_user (line 99) | def notify_user(self, *kwargs):
method terminate (line 127) | def terminate(self):
FILE: notifier/notifier.py
class Notifier (line 45) | class Notifier(hass.Hass):
method initialize (line 46) | def initialize(self):
method notify (line 55) | def notify(self, notify_name, message, useAlexa=True, useTelegram=True):
method notify_callback (line 72) | def notify_callback(self, kwargs):
method getAlexaDeviceForUserLocation (line 81) | def getAlexaDeviceForUserLocation(self, notify_name):
method terminate (line 96) | def terminate(self):
FILE: notifyOfActionWhenAway/notifyOfActionWhenAway.py
class NotifyOfActionWhenAway (line 31) | class NotifyOfActionWhenAway(hass.Hass):
method initialize (line 32) | def initialize(self):
method state_change (line 50) | def state_change(self, entity, attribute, old, new, kwargs):
method notify_if_no_one_home (line 74) | def notify_if_no_one_home(self, kwargs):
method terminate (line 89) | def terminate(self):
FILE: plantWateringNotifier/plantWateringNotifier.py
class PlantWateringNotifier (line 45) | class PlantWateringNotifier(hass.Hass):
method initialize (line 46) | def initialize(self):
method run_morning_callback (line 85) | def run_morning_callback(self, kwargs):
method run_evening_callback (line 125) | def run_evening_callback(self, kwargs):
method receive_telegram_callback (line 134) | def receive_telegram_callback(self, event_name, data, kwargs):
method terminate (line 165) | def terminate(self):
FILE: pollenNotifier/pollenNotifier.py
class PollenNotifier (line 37) | class PollenNotifier(hass.Hass):
method initialize (line 38) | def initialize(self):
method run_daily_callback (line 83) | def run_daily_callback(self, kwargs):
method terminate (line 115) | def terminate(self):
FILE: powerUsageNotification/powerUsageNotification.py
class PowerUsageNotification (line 44) | class PowerUsageNotification(hass.Hass):
method initialize (line 45) | def initialize(self):
method state_change (line 87) | def state_change(self, entity, attribute, old, new, kwargs):
method notify_device_off (line 135) | def notify_device_off(self, kwargs):
method terminate (line 152) | def terminate(self):
FILE: reminder/reminder.py
class Reminder (line 27) | class Reminder(hass.Hass):
method initialize (line 28) | def initialize(self):
method run_morning_callback (line 62) | def run_morning_callback(self, kwargs):
method run_evening_callback (line 77) | def run_evening_callback(self, kwargs):
method receive_telegram_callback (line 86) | def receive_telegram_callback(self, event_name, data, kwargs):
method terminate (line 115) | def terminate(self):
FILE: seqSink/seqSink.py
class SeqSink (line 21) | class SeqSink(hass.Hass):
method initialize (line 22) | def initialize(self):
method log_message_callback (line 37) | def log_message_callback(self, app_name, ts, level, log_type, message,...
method terminate (line 73) | def terminate(self):
FILE: setThermostat/setThermostat.py
class SetThermostat (line 45) | class SetThermostat(hass.Hass):
method initialize (line 46) | def initialize(self):
method schedule_trigger (line 75) | def schedule_trigger(self, entity, attribute, old, new, kwargs):
method trigger_thermostat (line 97) | def trigger_thermostat(self, kwargs):
method reset_thermostat (line 134) | def reset_thermostat(self, kwargs):
method terminate (line 157) | def terminate(self):
FILE: setThermostatOnStateChange/setThermostatOnStateChange.py
class SetThermostatOnStateChange (line 29) | class SetThermostatOnStateChange(hass.Hass):
method initialize (line 30) | def initialize(self):
method state_change (line 52) | def state_change(self, entity, attribute, old, new, kwargs):
method terminate (line 78) | def terminate(self):
FILE: sleepModeHandler/sleepModeHandler.py
class SleepModeHandler (line 24) | class SleepModeHandler(hass.Hass):
method initialize (line 25) | def initialize(self):
method state_change (line 47) | def state_change(self, entity, attribute, old, new, kwargs):
method are_all_that_are_home_sleeping (line 71) | def are_all_that_are_home_sleeping(self):
method are_all_that_are_home_awake (line 78) | def are_all_that_are_home_awake(self):
method terminate (line 85) | def terminate(self):
FILE: sleepModeHandler/userSleepModeHandler.py
class UserSleepModeHandler (line 43) | class UserSleepModeHandler(hass.Hass):
method initialize (line 44) | def initialize(self):
method home_state_change (line 67) | def home_state_change(self, entity, attribute, old, new, kwargs):
method state_change (line 76) | def state_change(self, entity, attribute, old, new, kwargs):
method awake (line 103) | def awake(self, kwargs):
method asleep (line 119) | def asleep(self, kwargs):
method insert_room_state_change (line 135) | def insert_room_state_change(self, entity, attribute, old, new, kwargs):
method calculate_room_presence (line 139) | def calculate_room_presence(self, kwargs):
method terminate (line 148) | def terminate(self):
FILE: travelTimeNotifier/travelTimeNotifier.py
class TravelTimeNotifier (line 44) | class TravelTimeNotifier(hass.Hass):
method initialize (line 45) | def initialize(self):
method state_change (line 69) | def state_change(self, entity, attributes, old, new, kwargs) -> None:
method notify_user (line 89) | def notify_user(self, address: str) -> None:
method parse_duration_in_traffic_minutes (line 95) | def parse_duration_in_traffic_minutes(self, state) -> Optional[int]:
method parse_destination_address (line 113) | def parse_destination_address(self, state) -> Optional[str]:
method parse_duration_minutes (line 128) | def parse_duration_minutes(self, state) -> Optional[int]:
method terminate (line 141) | def terminate(self) -> None:
FILE: turnFanOnWhenHot/turnFanOnWhenHot.py
class TurnFanOnWhenHot (line 31) | class TurnFanOnWhenHot(hass.Hass):
method initialize (line 32) | def initialize(self):
method state_change (line 56) | def state_change(self, entity, attribute, old, new, kwargs):
method turn_off_callback (line 94) | def turn_off_callback(self, kwargs):
method terminate (line 101) | def terminate(self):
FILE: turnOffBarAfterRestart/turnOffBarAfterRestart.py
class TurnOffBarAfterRestart (line 18) | class TurnOffBarAfterRestart(hass.Hass):
method initialize (line 19) | def initialize(self):
method turn_off_callback (line 29) | def turn_off_callback(self, kwargs):
method turn_green_callback (line 43) | def turn_green_callback(self, kwargs):
method terminate (line 63) | def terminate(self):
Condensed preview — 100 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (325K chars).
[
{
"path": ".gitignore",
"chars": 1285,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2018 Kevin Eifinger\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.md",
"chars": 22925,
"preview": "<h1 align=\"center\">\n <a name=\"logo\" href=\"\"><img src=\"images/logo-round-192x192.png\" alt=\"Home Assistant Logo\" width=\"1"
},
{
"path": "ad-ench/ench.py",
"chars": 18058,
"preview": "\"\"\"EnCh.\n Entity Checker\n\n @benleb / https://github.com/benleb/ad-ench\n\"\"\"\n\n__version__ = \"0.9.0\"\n\nfrom datetime impo"
},
{
"path": "alarmClock/alarmClock.py",
"chars": 9320,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\nimport math\n\n\n#\n# Alarm Clock App\n#\n#\n# Args:\n# alarm_tim"
},
{
"path": "alarmClock/alarmClock.yaml",
"chars": 650,
"preview": "# Alarm Clock App\n# alarmClock:\n# module: alarmClock\n# class: AlarmClock\n# alarm_time: sensor.alarm_time\n# wakem"
},
{
"path": "alexa/README.md",
"chars": 3428,
"preview": "# Alexa Intents\n\nIntents for [Alexa-Appdaemon-App](https://github.com/ReneTode/Alexa-Appdaemon-App) from [Rene Tode](htt"
},
{
"path": "alexa/alexa.yaml",
"chars": 1898,
"preview": "alexa_api: # appdaemon skill\n module: alexa_api\n class: alexa_api\n cardTitle: Your Card Title\n devices:\n unknownD"
},
{
"path": "alexa/alexa_api.py",
"chars": 14154,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\nimport datetime\n\n\nclass alexa_api(hass.Hass):\n def initia"
},
{
"path": "alexa/custom_skill.json",
"chars": 9714,
"preview": "{\n \"interactionModel\": {\n \"languageModel\": {\n \"invocationName\": \"home assistant\",\n \"inte"
},
{
"path": "alexa/lightState/lightStateIntent-utterances_DE.csv",
"chars": 65,
"preview": "Ist {device} aus\nIst {device} an\nWas ist der Status von {device}"
},
{
"path": "alexa/lightState/lightStateIntent-utterances_EN.csv",
"chars": 61,
"preview": "Is {device} off\nIs {device} on\nWhat is the state of {device}\n"
},
{
"path": "alexa/lightState/lightStateIntent.py",
"chars": 1323,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\n\n\nclass lightStateIntent(hass.Hass):\n def initialize(self"
},
{
"path": "alexa/lightState/lightStateIntent.yaml",
"chars": 509,
"preview": "lightStateIntent:\n module: lightStateIntent\n class: lightStateIntent\n language: DE\n temperatureUnit: \"Grad\"\n textLi"
},
{
"path": "alexa/listService/listService.py",
"chars": 862,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# Provide the list of HA entities for Alexa Apps\n#\n#\n# Args:\n#\n# switch"
},
{
"path": "alexa/listService/listService.yaml",
"chars": 2548,
"preview": "listService:\n module: listService\n class: ListService\n switchable:\n große lampe: switch.large_lamp\n kleine lamp"
},
{
"path": "alexa/nextBus/nextBusIntent.py",
"chars": 740,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\n\n\nclass nextBusIntent(hass.Hass):\n def initialize(self):\n"
},
{
"path": "alexa/nextBus/nextBusIntent.yaml",
"chars": 247,
"preview": "nextBusIntent:\n module: nextBusIntent\n class: nextBusIntent\n textLine: \"Linie {} fährt in {} Minuten\"\n #textLine: \"L"
},
{
"path": "alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.py",
"chars": 3185,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n__ZONE_ACTION_ENTER__ = \"kommen\"\n__ZONE_ACTION_LEAVE__ = "
},
{
"path": "alexa/remindMeOfXWhenZone/remindMeOfXWhenZoneIntent.yaml",
"chars": 479,
"preview": "remindMeOfXWhenZoneIntent:\n module: remindMeOfXWhenZoneIntent\n class: RemindMeOfXWhenZoneIntent\n device_tracker: pers"
},
{
"path": "alexa/temperatureState/temperatureStateIntent-utterances_DE.csv",
"chars": 72,
"preview": "wie viel grad ist es in {location}\nwas ist die temperatur in {location}"
},
{
"path": "alexa/temperatureState/temperatureStateIntent-utterances_EN.csv",
"chars": 73,
"preview": "how many degree is it in {location}\nwhat is the temperture in {location}\n"
},
{
"path": "alexa/temperatureState/temperatureStateIntent.py",
"chars": 1618,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\n\n\nclass temperatureStateIntent(hass.Hass):\n def initializ"
},
{
"path": "alexa/temperatureState/temperatureStateIntent.yaml",
"chars": 416,
"preview": "temperatureStateIntent:\n module: temperatureStateIntent\n class: temperatureStateIntent\n language: DE\n temperatureUni"
},
{
"path": "alexa/turnEntityOffInX/requirements.txt",
"chars": 7,
"preview": "isodate"
},
{
"path": "alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_DE.csv",
"chars": 191,
"preview": "In {duration} {device} ausschalten\n{device} in {duration} ausschalten\ner soll {device} in {duration} ausschalten\nes sol"
},
{
"path": "alexa/turnEntityOffInX/turnEntityOffInXIntent-utterances_EN.csv",
"chars": 106,
"preview": "In {duration} turn off {device}\nturn off {device} in {duration}\nit should turn off {device} in {duration}\n"
},
{
"path": "alexa/turnEntityOffInX/turnEntityOffInXIntent.py",
"chars": 2871,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\nimport isodate\nimport datetime\n\n\nclass TurnEntityOffInXInten"
},
{
"path": "alexa/turnEntityOffInX/turnEntityOffInXIntent.yaml",
"chars": 319,
"preview": "turnEntityOffInXIntent:\n module: turnEntityOffInXIntent\n class: TurnEntityOffInXIntent\n language: DE\n textLine: \"Oka"
},
{
"path": "alexa/windowsOpen/windowsOpenIntent-utterances_DE.csv",
"chars": 281,
"preview": "ob alles zu ist\nob noch etwas offen ist\nist noch etwas offen\nist alles zu\nsind alle türen zu\nob noch türen offen sind\no"
},
{
"path": "alexa/windowsOpen/windowsOpenIntent-utterances_EN.csv",
"chars": 350,
"preview": "whether everything is closed\nwhether something is still open\nis something still open\nis everthing closed\nare all doors c"
},
{
"path": "alexa/windowsOpen/windowsOpenIntent.py",
"chars": 3720,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport random\nimport isodate\nimport datetime\n\n\nclass WindowsOpenIntent(has"
},
{
"path": "alexa/windowsOpen/windowsOpenIntent.yaml",
"chars": 671,
"preview": "windowsOpenIntent:\n module: windowsOpenIntent\n class: WindowsOpenIntent\n language: DE\n textLineClosed: \"Alle Fenster"
},
{
"path": "alexaSpeakerConnector/alexaSpeakerConnector.py",
"chars": 3344,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App to Turn on Receiver Bluetooth when Alexa is playing something so "
},
{
"path": "alexaSpeakerConnector/alexaSpeakerConnector.yaml",
"chars": 413,
"preview": "#App to Turn on Receiver Bluetooth when Alexa is playing something so it plays on the big speakers\n# alexaSpeakerConnect"
},
{
"path": "appWatcher/appWatcher.py",
"chars": 1224,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App which listens on the log for App crashes and notifies via telegra"
},
{
"path": "appWatcher/appWatcher.yaml",
"chars": 197,
"preview": "appWatcher:\n module: appWatcher\n class: AppWatcher\n notify_name: kevin\n notify_message: \"AppDaemon error: {}\"\n #not"
},
{
"path": "apps.yaml",
"chars": 169,
"preview": "#################################################################\n## Global\n############################################"
},
{
"path": "buttonClicked/buttonClicked.py",
"chars": 6060,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which toggles entities for single/double presses "
},
{
"path": "buttonClicked/buttonClicked.yaml",
"chars": 897,
"preview": "# App which toggles entities for single/double presses of xiaomi buttons\n# xiaomiroundButtonBedroomClicked:\n# module: "
},
{
"path": "comingHome/comingHome.py",
"chars": 3482,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App to Turn on Lobby Lamp when Door openes and no one"
},
{
"path": "comingHome/comingHome.yaml",
"chars": 643,
"preview": "#Switch on Lobby lamp when the first person is coming home and the sun is down\n# comingHomeYeelight:\n# module: comingH"
},
{
"path": "deconz_xiaomi_button/deconz_xiaomi_button.py",
"chars": 3928,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which toggles entities for single/double/hold pre"
},
{
"path": "deconz_xiaomi_button.yaml",
"chars": 788,
"preview": "DeconzXiaomiButtonBedroom:\n module: deconz_xiaomi_button\n class: DeconzXiaomiButton\n id: round_button_schlafzimmer\n "
},
{
"path": "detectWrongState/detectWrongState.py",
"chars": 5281,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App which notifies of wrong states based on a state change\n#\n# Args:\n"
},
{
"path": "detectWrongState/detectWrongState.yaml",
"chars": 3989,
"preview": "# detectWrongStateWhenLeaving:\n# module: detectWrongState\n# class: DetectWrongState\n# app_switch: input_boolean.de"
},
{
"path": "ench.yaml",
"chars": 1351,
"preview": "---\nench:\n module: ench\n class: EnCh\n notify: \"notify.kevin\"\n exclude:\n - device_tracker.venue_8*\n - person.ke"
},
{
"path": "eventMonitor/eventMonitor.py",
"chars": 1074,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n\"\"\"\nMonitor events and output changes to the verbose_log. Nice for debugg"
},
{
"path": "eventMonitor/eventMonitor.yaml",
"chars": 68,
"preview": "#eventMonitor:\n# module: eventMonitor\n# class: Monitor\n# events: "
},
{
"path": "faceRecognitionBot/faceRecognitionBot.py",
"chars": 31702,
"preview": "import json\nfrom json import JSONDecodeError\n\nimport appdaemon.plugins.hass.hassapi as hass # pylint: disable=import-er"
},
{
"path": "faceRecognitionBot/faceRecognitionBot.yaml",
"chars": 1986,
"preview": "# faceRecognitionBot:\n# module: faceRecognitionBot\n# class: FaceRecognitionBot\n# app_switch: input_boolean.facebox"
},
{
"path": "globals.py",
"chars": 297,
"preview": "import random\n\n\ndef random_arg(argList):\n ############################################\n # pick a random text from "
},
{
"path": "heartbeat/heartbeat.py",
"chars": 1208,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nfrom requests.exceptions import HTTPError\n\n#\n# App which sets a homeassist"
},
{
"path": "heartbeat/heartbeat.yaml",
"chars": 86,
"preview": "heartbeat:\n module: heartbeat\n class: Heartbeat\n sensor: sensor.appdaemon_heartbeat"
},
{
"path": "homeArrivalNotifier/homeArrivalNotifier.py",
"chars": 2022,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App to send a notification if someone arrives at home\n#\n# Args:\n# ap"
},
{
"path": "homeArrivalNotifier/homeArrivalNotifier.yaml",
"chars": 859,
"preview": "#Notification if user one arrives at home\n# homeArrivalNotifierUserOne:\n# module: homeArrivalNotifier\n# class: HomeA"
},
{
"path": "isHomeDeterminer/isHomeDeterminer.py",
"chars": 3718,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport globals\n\n#\n# App to\n#\n# Args:\n# app_switch: on/off switch for thi"
},
{
"path": "isHomeDeterminer/isHomeDeterminer.yaml",
"chars": 614,
"preview": "# #Control the isHome state. Determines if someone is home or all persons are away\n# isHomeDeterminer:\n# module: isHom"
},
{
"path": "isUserHomeDeterminer/isUserHomeDeterminer.py",
"chars": 8708,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\nfrom requests.exceptions import HTTPError\n\n#\n# App to togg"
},
{
"path": "isUserHomeDeterminer/isUserHomeDeterminer.yaml",
"chars": 649,
"preview": "# #Determine if user one gets/leaves home\n# isUserHomeDeterminerUserOne:\n# module: isUserHomeDeterminer\n# class: IsU"
},
{
"path": "leavingZoneNotifier/leavingZoneNotifier.py",
"chars": 6734,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App to notify if user_one is leaving a zone.\n# User h"
},
{
"path": "leavingZoneNotifier/leavingZoneNotifier.yaml",
"chars": 1868,
"preview": "# leavingWorkNotifierUserOne:\n# module: leavingZoneNotifier\n# class: LeavingZoneNotifier\n# app_switch: input_boole"
},
{
"path": "motionTrigger/motionTrigger.py",
"chars": 9656,
"preview": "import appdaemon.plugins.hass.hassapi as hass # pylint: disable=import-error\nimport datetime\n\n#\n# Special version of Mo"
},
{
"path": "motionTrigger/motionTrigger.yaml",
"chars": 594,
"preview": "# bedroomMotionTrigger:\n# module: motionTrigger\n# class: MotionTrigger\n# app_switch: input_boolean.bedroom_motion_"
},
{
"path": "newWifiDeviceNotify/newWifiDeviceNotify.py",
"chars": 9536,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nfrom fritz_switch_profiles import FritzProfileSwitch\n\n#\n# App which sends "
},
{
"path": "newWifiDeviceNotify/newWifiDeviceNotify.yaml",
"chars": 981,
"preview": "newWifiDeviceNotify:\n module: newWifiDeviceNotify\n class: DeviceNotify\n notify_name: group_notifications\n user_id: !"
},
{
"path": "newWifiDeviceNotify/requirements.txt",
"chars": 30,
"preview": "fritz_switch_profiles >= 1.0.0"
},
{
"path": "nextAppointmentLeaveNotifier/nextAppointmentLeaveNotifier.py",
"chars": 4953,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which notifies the user to start to the next appo"
},
{
"path": "nextAppointmentLeaveNotifier/nextAppointmentLeaveNotifier.yaml",
"chars": 1479,
"preview": "# nextAppointmentLeaveNotifier:\n# module: nextAppointmentLeaveNotifier\n# class: NextAppointmentLeaveNotifier\n# sen"
},
{
"path": "notifier/notifier.py",
"chars": 3408,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# Centralizes messaging. Among other things, it will de"
},
{
"path": "notifier/notifier.yaml",
"chars": 646,
"preview": "Notifier:\n module: notifier\n class: Notifier\n app_switch_alexa: input_boolean.notifier_alexa\n alexa_tts: alexa_media"
},
{
"path": "notifyOfActionWhenAway/notifyOfActionWhenAway.py",
"chars": 3081,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App to send notification when a sensor changes state\n#\n# Args:\n#\n# a"
},
{
"path": "notifyOfActionWhenAway/notifyOfActionWhenAway.yaml",
"chars": 1080,
"preview": "notifyOfActionWhenAway:\n module: notifyOfActionWhenAway\n class: NotifyOfActionWhenAway\n app_switch: input_boolean.not"
},
{
"path": "plantWateringNotifier/plantWateringNotifier.py",
"chars": 7041,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which reminds you daily to water your plants if i"
},
{
"path": "plantWateringNotifier/plantWateringNotifier.yaml",
"chars": 1262,
"preview": "# plantWateringNotifier:\n# module: plantWateringNotifier\n# class: PlantWateringNotifier\n# app_switch: input_boolea"
},
{
"path": "pollenNotifier/pollenNotifier.py",
"chars": 4415,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which notifies you when there is a pollen forecas"
},
{
"path": "pollenNotifier/pollenNotifier.yaml",
"chars": 1452,
"preview": "roggenNotifier:\n module: pollenNotifier\n class: PollenNotifier\n app_switch: input_boolean.roggen_notifier\n pollen_se"
},
{
"path": "powerUsageNotification/powerUsageNotification.py",
"chars": 6189,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App which notifies you when a power usage sensor indicated a device i"
},
{
"path": "powerUsageNotification/powerUsageNotification.yaml",
"chars": 1089,
"preview": "powerUsageNotification_Dishwasher:\n module: powerUsageNotification\n class: PowerUsageNotification\n app_switch: input_"
},
{
"path": "reminder/reminder.py",
"chars": 4671,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\nimport uuid\n\n#\n# App which reminds you daily and again in "
},
{
"path": "reminder/reminder.yaml",
"chars": 715,
"preview": "# reminderPlantwateringNew:\n# module: reminder\n# class: Reminder\n# app_switch: input_boolean.plant_watering_remind"
},
{
"path": "requirements.txt",
"chars": 5,
"preview": "wheel"
},
{
"path": "seqSink/requirements.txt",
"chars": 16,
"preview": "requests==2.24.0"
},
{
"path": "seqSink/seqSink.py",
"chars": 2316,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport socket\nimport os\nimport json\nimport requests\n\n#\n# App which forward"
},
{
"path": "seqSink/seqSink.yaml",
"chars": 117,
"preview": "# seqSink:\n# module: seqSink\n# class: SeqSink\n# server_url: \"http://seq:5341/\"\n# api_key: !secret seq_api_key"
},
{
"path": "setThermostat/setThermostat.py",
"chars": 6027,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App which sets a thermostat to a target temperature b"
},
{
"path": "setThermostat/setThermostat.yaml",
"chars": 1130,
"preview": "# warm_bath_before_wakeup:\n# module: setThermostat\n# class: SetThermostat\n# app_switch: input_boolean.warm_bath_be"
},
{
"path": "setThermostatOnStateChange/setThermostatOnStateChange.py",
"chars": 3177,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n#\n# App which sets a thermostat to a target temperature on state change\n#"
},
{
"path": "setThermostatOnStateChange/setThermostatOnStateChange.yaml",
"chars": 8207,
"preview": "# setWohnzimmerThermostatWhenLeaving:\n# module: setThermostatOnStateChange\n# class: SetThermostatOnStateChange\n# a"
},
{
"path": "sleepModeHandler/sleepModeHandler.py",
"chars": 3087,
"preview": "import appdaemon.plugins.hass.hassapi as hass\n\n\n#\n# App which sets the sleep mode on/off\n#\n# Args:\n# app_switch: on/of"
},
{
"path": "sleepModeHandler/sleepModeHandler.yaml",
"chars": 636,
"preview": "# sleepModeHandler:\n# module: sleepModeHandler\n# class: SleepModeHandler\n# app_switch: input_boolean.sleep_mode_ha"
},
{
"path": "sleepModeHandler/userSleepModeHandler.py",
"chars": 5658,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nfrom queue import Queue\n\n#\n# App which sets the sleep mode on/off\n#\n# Args"
},
{
"path": "sleepModeHandler/userSleepModeHandler.yaml",
"chars": 800,
"preview": "# userSleepModeHandlerUserOne:\n# module: userSleepModeHandler\n# class: UserSleepModeHandler\n# app_switch: input_bo"
},
{
"path": "travelTimeNotifier/travelTimeNotifier.py",
"chars": 5567,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\nfrom typing import Optional\n\n#\n# App which notifies the us"
},
{
"path": "travelTimeNotifier/travelTimeNotifier.yaml",
"chars": 2275,
"preview": "# travelTime_home_from_work:\n# module: travelTimeNotifier\n# class: TravelTimeNotifier\n# sensor: sensor.travel_time_ho"
},
{
"path": "turnFanOnWhenHot/turnFanOnWhenHot.py",
"chars": 4465,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nimport datetime\n\n#\n# App to Turn on fan when temp is above a threshold and"
},
{
"path": "turnFanOnWhenHot/turnFanOnWhenHot.yaml",
"chars": 814,
"preview": "# turnLargeFanOnWhenHot:\n# module: turnFanOnWhenHot\n# class: TurnFanOnWhenHot\n# app_switch: input_boolean.turn_lar"
},
{
"path": "turnOffBarAfterRestart/turnOffBarAfterRestart.py",
"chars": 2375,
"preview": "import appdaemon.plugins.hass.hassapi as hass\nfrom requests.exceptions import HTTPError\n\n#\n# Will turn the bar table gre"
},
{
"path": "turnOffBarAfterRestart/turnOffBarAfterRestart.yaml",
"chars": 121,
"preview": "# turnOffBarAfterRestart:\n# module: turnOffBarAfterRestart\n# class: TurnOffBarAfterRestart\n# light: light.bar_tabl"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the eifinger/appdaemon-scripts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 100 files (298.5 KB), approximately 69.5k tokens, and a symbol index with 276 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.