From ee606cac14a500a5b1aa62e9a80d69f108b37ff6 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 00:04:10 +0100
Subject: [PATCH 001/123] Fix yuotube link
---
.../2016-12-17-text-to-speech-aquostv-flic-zamg.markdown | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index 13e90e4527a..9790da7ddc8 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -11,14 +11,14 @@ categories: Release-Notes
og_image: /images/blog/2016-12-0.35/social.png
---
-5000 stars on GitHub, 2000 people in out Gitter chatroom and over a million monthly pageviews. I don't think we could wish for a better place to be at the end of 2016. Feels like an early Christmas present! Our early one for you is 0.35. It's not a single thing inside a nice wrapping, more like several little gifts inside the 0.35 box.
+5000 stars on GitHub, 2000 people in out Gitter chatroom and over a million monthly page views. I don't think we could wish for a better place to be at the end of 2016. Feels like an early Christmas present! Our early one for you is 0.35. It's not a single thing inside a nice wrapping, more like several little gifts inside the 0.35 box.
This will be the last release of 2016 as our developers are taking a well deserved break. We will be back in 2017!
## {% linkable_title Text to Speech %}
With the addition of a [text-to-speech][tts] component by [@pvizeli] we have been able to bring Home Assistant to a whole new level. The text-to-speech component will take in any text and will play it on a media player that supports to play media. We have tested this on Sonos, Chromecast, and Google Home.
-https://www.youtube.com/watch?v=Ke0QuoJ4tRM
+[https://www.youtube.com/watch?v=Ke0QuoJ4tRM](https://www.youtube.com/watch?v=Ke0QuoJ4tRM)
## {% linkable_title Call for help with HASSbian (our Raspberry Pi image) %}
In an effort to make Home Assistant, we're planning to extend the things that people can do out of the box with HASSbian, our Raspberry Pi image. As you might know, the image is currently maintained by [@Landrash]. However he also spends a lot of time on improving the docs and helping out with a ton of other things.
@@ -40,7 +40,7 @@ The [GPSLogger](https://home-assistant.io/components/device_tracker.gpslogger/)
- Sensor: Support weather conditions from Austrian [ZAMG][zamg] ([@mjl])
- Verisure: Add Verisure smartcam capture service ([@turbokongen])
- Binary sensor: [Flic][flic] button support added ([@soldag])
-- Sensor: Support for [SenseHat][sensehat] ([@farminf])
+- Sensor: Support for [Sense HAT][sensehat] ([@farminf])
- Binary sensor: [Hikvision][hikvision] binary sensor support ([@mezz64])
- [Text-to-speech][tts] support ([@pvizeli])
- Sensor: Support for Broadlink [sensors][bl-sensor] ([@Danielhiversen])
From 03f680c07835db6d5a7150f4f2b1e434e6a284a1 Mon Sep 17 00:00:00 2001
From: Keaton Taylor
Date: Sun, 18 Dec 2016 02:44:34 -0600
Subject: [PATCH 002/123] example for optional values (#1562)
Added where to place optional values relay_time and state_pull_mode.
---
source/_components/cover.rpi_gpio.markdown | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source/_components/cover.rpi_gpio.markdown b/source/_components/cover.rpi_gpio.markdown
index f152aad8b79..3b2205d724b 100644
--- a/source/_components/cover.rpi_gpio.markdown
+++ b/source/_components/cover.rpi_gpio.markdown
@@ -27,6 +27,8 @@ To enable Raspberry Pi Covers in your installation, add the following to your `c
# Example configuration.yaml entry
cover:
platform: rpi_gpio
+ relay_time: 0.2
+ state_pull_mode: 'UP'
covers:
- relay_pin: 10
state_pin: 11
From 459001591138f67166131e4dec4cca8db0b0f775 Mon Sep 17 00:00:00 2001
From: Magnus Ihse Bursie
Date: Sun, 18 Dec 2016 09:54:45 +0100
Subject: [PATCH 003/123] Add real-life example for turn_off script on
wake-on-lan (#1535)
* Update switch.wake_on_lan.markdown
* Update switch.wake_on_lan.markdown
Suggested recipe for using turn_off to suspend a linux computer.
---
.../_components/switch.wake_on_lan.markdown | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/source/_components/switch.wake_on_lan.markdown b/source/_components/switch.wake_on_lan.markdown
index 833102d5466..bc7e314ccb1 100644
--- a/source/_components/switch.wake_on_lan.markdown
+++ b/source/_components/switch.wake_on_lan.markdown
@@ -16,6 +16,7 @@ The `wake_on_lan` (WOL) switch platform allows you to turn on a [WOL](https://en
The WOL switch can only turn on your computer and monitor the state. There is no universal way to turn off a computer remotely. The `turn_off` variable is there to help you call a script when you have figured out how to remotely turn off your computer.
+See below for suggestions on how to do this.
To enable this switch in your installation, add the following to your `configuration.yaml` file:
@@ -33,3 +34,31 @@ Configuration variables:
- **name** (*Optional*): The name of the switch. Default is 'Wake on LAN'.
- **host** (*Optional*): The IP address or hostname to check the state of the device (on/off).
- **turn_off** (*Optional*): Defines an [action](/getting-started/automation/) to run when the switch is turned off.
+
+## {% linkable_title Examples %}
+
+Here are some real life examples of how to use the **turn_off** variable.
+
+### {% linkable_title Suspending linux %}
+Suggested recipe for letting the turn_off script suspend a Linux computer (the **target**)
+from Home Assistant running on another Linux computer (the **server**).
+
+1. On the **server**, log in as the user account Home Assistant is running under. (I'm using `hass` in this example)
+2. On the **server**, create ssh keys by running `ssh-keygen`. Just press enter on all questions.
+3. On the **target**, create a new account that Home Assistant can ssh into: `sudo adduser hass`. Just press enter on all questions except password. I recommend using the same user name as on the server. If you do, you can leave out `hass@` in the ssh commands below.
+4. On the **server**, transfer your public ssh key by `ssh-copy-id hass@TARGET` where TARGET is your target machine's name or IP address. Enter the password you created in step 3.
+5. On the **server**, verify that you can reach your target machine without password by `ssh TARGET`.
+6. On the **target**, we need to let the hass user execute the program needed to suspend/shut down the target computer. I'm using `pm-suspend`, use `poweroff` to turn off the computer. First, get the full path: `which pm-suspend`. On my system, this is `/usr/sbin/pm-suspend`.
+7. On the **target**, using an account with sudo access (typically your main account), `sudo visudo`. Add this line last in the file: `hass ALL=NOPASSWD:/usr/sbin/pm-suspend`, where you replace `hass` with the name of your user on the target, if different, and `/usr/sbin/pm-suspend` with the command of your choice, if different.
+8. On the **server**, add the following to your configuration, replacing TARGET with the target's name:
+``` yaml
+switch:
+ - platform: wake_on_lan
+ name: "TARGET"
+ ...
+ turn_off:
+ service: shell_command.turn_off_TARGET
+
+shell_command:
+ turn_off_TARGET: 'ssh hass@TARGET sudo pm-suspend'
+```
From 5d0390f97ec5d028b847c1b8b879224816d0a907 Mon Sep 17 00:00:00 2001
From: Nolan Gilley
Date: Sun, 18 Dec 2016 03:55:43 -0500
Subject: [PATCH 004/123] show a template switch example for harmony (#1548)
* show a template switch example
* Update remote.harmony.markdown
---
source/_components/remote.harmony.markdown | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/source/_components/remote.harmony.markdown b/source/_components/remote.harmony.markdown
index 9f07c44f070..e392dab25fc 100755
--- a/source/_components/remote.harmony.markdown
+++ b/source/_components/remote.harmony.markdown
@@ -60,6 +60,22 @@ Supported services:
### {% linkable_title Examples %}
+A template switch can be used to display and control the state of an activity in the frontend.
+
+```yaml
+switch:
+ - platform: template
+ switches:
+ tv:
+ value_template: "{% raw %}{% if is_state('remote.family_room', 'on') %}on{% else %}off{% endif %}{% endraw %}"
+ turn_on:
+ service: remote.turn_on
+ entity_id: remote.family_room
+ turn_off:
+ service: remote.turn_off
+ entity_id: remote.family_room
+```
+
Template sensors can be utilized to display current activity in the frontend.
```yaml
From 6f25bc30f7572f15140700de92b43fd2f62b9072 Mon Sep 17 00:00:00 2001
From: Oliver
Date: Sun, 18 Dec 2016 09:58:49 +0100
Subject: [PATCH 005/123] Split description of both platforms / added auto
discovery feature for denonavr (#1582)
---
.../_components/media_player.denon.markdown | 32 ++++++++++++-------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/source/_components/media_player.denon.markdown b/source/_components/media_player.denon.markdown
index a083e47ac5e..d579463a5ce 100644
--- a/source/_components/media_player.denon.markdown
+++ b/source/_components/media_player.denon.markdown
@@ -28,7 +28,7 @@ Supported devices:
To add a Denon Network Receiver to your installation, add the following to your `configuration.yaml` file:
-Telnet interface
+**Telnet interface**
```yaml
# Example configuration.yaml entry
media_player:
@@ -36,7 +36,20 @@ media_player:
host: IP_ADDRESS
```
-denonavr interface
+Configuration variables:
+
+- **host** (*Required*): IP address of the device. Example: 192.168.1.32
+- **name** (*Optional*): Name of the device.
+
+A few notes for platform: denon
+- The receiver handles only one telnet connection and refuses others.
+- Be careful with the volume. 100% or even 50% is very loud.
+- To be able to wake up the receiver, activate the "remote" setting in the receiver's settings.
+- Play and pause are supported, toggling is not possible.
+- Seeking cannot be implemented as the UI sends absolute positions. Only seeking via simulated button presses is possible.
+
+
+**denonavr interface**
```yaml
# Example configuration.yaml entry
media_player:
@@ -46,15 +59,10 @@ media_player:
Configuration variables:
-- **host** (*Required*): IP address of the device. Example: 192.168.1.32
-- **name** (*Optional*): Name of the device
+- **host** (*Optional*): IP address of the device. Example: 192.168.1.32. If not set, auto discovery is used.
+- **name** (*Optional*): Name of the device. If not set, friendlyName of receiver is used.
-A few notes:
-
-- The receiver handles only one telnet connection and refuses others.
-- Be careful with the volume. 100% or even 50% is very loud.
-- To be able to wake up the receiver, activate the "remote" setting in the receiver's settings.
-- Play and pause are supported, toggling is not possible.
-- Seeking cannot be implemented as the UI sends absolute positions. Only seeking via simulated button presses is possible.
+A few notes for platform: denonavr
- Additional option the control Denon AVR receivers with a builtin web server is using the HTTP interface with denonavr platform
-- denonavr platform supports some additional functionalities like album covers and is supporting more than simultaneous one connection
+- denonavr platform supports some additional functionalities like album covers, custom input source names and auto discovery
+- Still be careful with the volume. 100% in an action movie will tear down your walls.
From 733559db1797c7e71ac1a7c34e15ed1b7485f7b9 Mon Sep 17 00:00:00 2001
From: Andrew Cockburn
Date: Sun, 18 Dec 2016 04:01:03 -0500
Subject: [PATCH 006/123] Add AppDaemon docs to ecosystem (#1589)
---
source/_ecosystem/appdaemon.markdown | 14 +
source/_ecosystem/appdaemon/api.markdown | 2110 +++++++++++++++++
.../appdaemon/configuration.markdown | 80 +
.../appdaemon/example_apps.markdown | 14 +
.../appdaemon/installation.markdown | 46 +
.../_ecosystem/appdaemon/operation.markdown | 14 +
source/_ecosystem/appdaemon/reboot.markdown | 14 +
source/_ecosystem/appdaemon/running.markdown | 96 +
source/_ecosystem/appdaemon/tutorial.markdown | 130 +
source/_ecosystem/appdaemon/updating.markdown | 27 +
source/_ecosystem/appdaemon/windows.markdown | 23 +
.../ecosystem_appdaemon_navigation.html | 19 +
.../asides/ecosystem_navigation.html | 2 +
13 files changed, 2589 insertions(+)
create mode 100755 source/_ecosystem/appdaemon.markdown
create mode 100755 source/_ecosystem/appdaemon/api.markdown
create mode 100644 source/_ecosystem/appdaemon/configuration.markdown
create mode 100644 source/_ecosystem/appdaemon/example_apps.markdown
create mode 100644 source/_ecosystem/appdaemon/installation.markdown
create mode 100644 source/_ecosystem/appdaemon/operation.markdown
create mode 100644 source/_ecosystem/appdaemon/reboot.markdown
create mode 100755 source/_ecosystem/appdaemon/running.markdown
create mode 100755 source/_ecosystem/appdaemon/tutorial.markdown
create mode 100644 source/_ecosystem/appdaemon/updating.markdown
create mode 100755 source/_ecosystem/appdaemon/windows.markdown
create mode 100755 source/_includes/asides/ecosystem_appdaemon_navigation.html
diff --git a/source/_ecosystem/appdaemon.markdown b/source/_ecosystem/appdaemon.markdown
new file mode 100755
index 00000000000..9ec0e87f8e3
--- /dev/null
+++ b/source/_ecosystem/appdaemon.markdown
@@ -0,0 +1,14 @@
+---
+layout: page
+title: "AppDaemon"
+description: "AppDaemon is a loosely coupled, multithreaded, sandboxed python execution environment for writing automation apps for Home Assistant"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+AppDaemon is a loosely coupled, multithreaded, sandboxed python execution environment for writing automation apps for Home Assistant.
\ No newline at end of file
diff --git a/source/_ecosystem/appdaemon/api.markdown b/source/_ecosystem/appdaemon/api.markdown
new file mode 100755
index 00000000000..63299c41948
--- /dev/null
+++ b/source/_ecosystem/appdaemon/api.markdown
@@ -0,0 +1,2110 @@
+---
+layout: page
+title: "AppDaemon API Reference"
+description: "AppDaemon API Reference"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+## Anatomy of an App
+
+Automations in AppDaemon are performed by creating a piece of code (essentially a Python Class) and then instantiating it as an Object one or more times by configuring it as an App in the configuration file. The App is given a chance to register itself for whatever events it wants to subscribe to, and AppDaemon will then make calls back into the Object's code when those events occur, allowing the App to respond to the event with some kind of action.
+
+The first step is to create a unique file within the apps directory (as defined in the `[AppDaemon]` section of configuration file). This file is in fact a Python module, and is expected to contain one or more classes derived from the supplied `AppDaemon` class, imported from the supplied `homeassistant.appapi` module. The start of an app might look like this:
+
+```python
+import homeassistant.appapi as appapi
+
+class MotionLights(appapi.AppDaemon):
+```
+
+When configured as an app in the config file (more on that later) the lifecycle of the App begins. It will be instantiated as an object by AppDaemon, and immediately, it will have a call made to it's `initialize()` function - this function must appear as part of every app:
+
+```python
+ def initialize(self):
+```
+
+The initialize function allows the app to register any callbacks it might need for responding to state changes, and also any setup activities. When the `initialize()` function returns, the App will be dormant until any of it's callbacks are activated.
+
+There are several circumstances under which `initialize()` might be called:
+
+- Initial start of AppDaemon
+- Following a change to the Class code
+- Following a change to the module parameters
+- Following initial configuration of an app
+- Following a change in the status of Daylight Savings Time
+- Following a restart of Home Assistant
+
+In every case, the App is responsible for recreating any state it might need as if it were the first time it was ever started. If `initialize()` is called, the app can safely assume that it is either being loaded for the first time, or that all callbacks and timers have been cancelled. In either case, the APP will need to recreate them. Depending upon the application it may be desirable for the App to establish state such as whether or not a particular light is on, within the `initialize()` function to ensure that everything is as expected or to make immediate remedial action (e.g. turn off a light that might have been left on by mistake when the app was restarted).
+
+After the `initialize()` function is in place, the rest of the app consists of functions that are called by the various callback mechanisms, and any additional functions the user wants to add as part of the program logic. Apps are able to subscribe to 2 main classes of events:
+
+- Scheduled Events
+- State Change Events
+
+These, along with their various subscription calls and helper functions, will be described in detail in later sections.
+
+To wrap up this section, here is a complete functioning App (with comments):
+
+```python
+import homeassistant.appapi as appapi
+import datetime
+
+# Declare Class
+class NightLight(appapi.AppDaemon):
+ #initialize() function which will be called at startup and reload
+ def initialize(self):
+ # Create a time object for 7pm
+ time = datetime.time(19, 00, 0)
+ # Schedule a daily callback that will call run_daily() at 7pm every night
+ self.run_daily(self.run_daily_callback, time)
+
+ # Our callback function will be called by the scheduler every day at 7pm
+ def run_daily_callback(self, kwargs):
+ # Call to Home Assistant to turn the porch light on
+ self.turn_on("light.porch")
+```
+
+To summarize - an App's lifecycle consists of being initialized, which allows it to set one or more state and/or schedule callbacks. When those callbacks are activated, the App will typically use one of the Service Calling calls to effect some change to the devices of the system and then wait for the next relevant state change. That's all there is to it!
+
+## About the API
+
+The implementation of the API is located in the AppDaemon class that Apps are derived from. The code for the functions is therefore available to the App simply by invoking the name of the function from the object namespace using the `self` keyword, as in the above examples. `self.turn_on()` for example is just a method defined in the parent class and made available to the child. This design decision was made to simplify some of the implementation and hide passing of unnecessary variables during the API invocation.
+
+## Configuration of Apps
+Apps are configured by specifying new sections in the configuration file. `[AppDaemon]` is a reserved section, for configuration of AppDaemon itself. The name of the section is the name the App is referred to within the system in log files etc. and must be unique.
+
+To configure a new App you need a minimum of two directives:
+
+- `module` - the name of the module (without the `.py`) that contains the class to be used for this App
+- `class` - the name of the class as defined within the module for the APPs code
+
+Although the section/App name must be unique, it is possible to re-use a class as many times as you want, and conversely to put as many classes in a module as you want. A sample definition for a new App might look as follows:
+
+```ini
+[newapp]
+module = new
+class = NewApp
+```
+
+When AppDaemon sees the following configuration it will expect to find a class called `NewApp` defined in a module called `new.py` in the apps subdirectory. Apps can be placed at the root of the Apps directory or within a subdirectory, an arbitrary depth down - wherever the App is, as long as it is in some subdirectory of the Apps dir, or in the Apps dir itself, AppDaemon will find it. There is no need to include information about the path, just the name of the file itself (without the `.py`) is sufficient. If names in the subdirectories overlap, AppDir will pick one of them but the exact choice it will make is undefined.
+
+When starting the system for the first time or when reloading an App or Module, the system will log the fact in it's main log. It is often the case that there is a problem with the class, maybe a syntax error or some other problem. If that is the case, details will be output to the error log allowing the user to remedy the problem and reload.
+
+## Steps to writing an App
+
+1. Create the code in a new or shared module by deriving a class from AppDaemon, add required callbacks and code
+2. Add the App to the configuration file
+3. There is no number 3
+
+## Reloading Modules and Classes
+
+Reloading of modules is automatic. When the system spots a change in a module, it will automatically reload and recompile the module. It will also figure out which Apps were using that Module and restart them, causing all of their existing callbacks to be cleared, and their `initialize()` function to be called.
+
+The same is true if changes are made to an App's configuration - changing the class, or arguments (see later) will cause that app to be reloaded in the same way. The system is also capable of detecting if a new app has been added, or if one has been removed, and it will act appropriately, starting the new app immediately and removing all callbacks for the removed app.
+
+The suggested order for creating a new App is to add the module code first and work until it compiles cleanly, and only then add an entry in the configuration file to actually run it. A good workflow is to continuously monitor the error file (using `tail -f` on Linux for instance) to ensure that errors are seen and can be remedied.
+
+## Passing Arguments to Apps
+
+There wouldn't be much point in being able to run multiple versions of an App if there wasn't some way to instruct them to do something different. For this reason it is possible to pass any required arguments to an App, which are then made available to the object at runtime. The arguments themselves can be called anything (apart from `module` or `class`) and are simply added into the section after the 2 mandatory directives like so:
+
+```ini
+[MyApp]
+module = myapp
+class = MyApp
+param1 = spam
+param2 = eggs
+```
+
+Within the Apps code, the 2 parameters (as well as the module and class) are available as a dictionary called `args`, and accessed as follows:
+
+```python
+param1 = self.args["param1"]
+param2 = self.args["param2"]
+```
+
+A use case for this might be an App that detects motion and turns on a light. If you have 3 places you want to run this, rather than hardcoding this into 3 separate Apps, you need only code a single app and instantiate it 3 times with different arguments. It might look something like this:
+
+```ini
+[downstairs_motion_light]
+module = motion_light
+class = MotionLight
+sensor = binary_sensor.downstairs_hall
+light = light.downstairs_hall
+[upstairs_motion_light]
+module = motion_light
+class = MotionLight
+sensor = binary_sensor.upstairs_hall
+light = light.upstairs_hall
+[garage_motion_light]
+module = motion_light
+class = MotionLight
+sensor = binary_sensor.garage
+light = light.garage
+```
+
+## Callback Constraints
+
+Callback constraints are a feature of AppDaemon that removes the need for repetition of some common coding checks. Many Apps will wish to process their callbacks only when certain conditions are met, e.g. someone is home, and it's after sunset. These kinds of conditions crop up a lot, and use of callback constraints can significantly simplify the logic required within callbacks.
+
+Put simply, callback constraints are one or more conditions on callback execution that can be applied to an individual App. An App's callbacks will only be executed if all of the constraints are met. If a constraint is absent it will not be checked for.
+
+For example, the presence callback constraint can be added to an App by adding a parameter to it's configuration like this:
+
+```ini
+[some_app]
+module = some_module
+class = SomeClass
+constrain_presence = noone
+```
+
+Now, although the `initialize()` function will be called for MyClass, and it will have a chance to register as many callbacks as it desires, none of the callbacks will execute, in this case, until everyone has left. This could be useful for an interior motion detector App for instance. There are several different types of constraints:
+
+- input_boolean
+- input_select
+- presence
+- time
+
+An App can have as many or as few as are required. When more than one constraint is present, they must all evaluate to true to allow the callbacks to be called. Constraints becoming true are not an event in their own right, but if they are all true at a point in time, the next callback that would otherwise been blocked due to constraint failure will now be called. Similarly, if one of the constraints becomes false, the next callback that would otherwise have been called will be blocked.
+
+They are described individually below.
+
+### input_boolean
+By default, the input_boolean constraint prevents callbacks unless the specified input_boolean is set to "on". This is useful to allow certain Apps to be turned on and off from the user interface. For example:
+
+```ini
+[some_app]
+module = some_module
+class = SomeClass
+constrain_input_boolean = input_boolean.enable_motion_detection
+```
+
+If you want to reverse the logic so the constraint is only called when the input_boolean is off, use the optional state parameter by appending ",off" to the argument, e.g.:
+
+```ini
+[some_app]
+module = some_module
+class = SomeClass
+constrain_input_boolean = input_boolean.enable_motion_detection,off
+```
+
+### input_select
+The input_select constraint prevents callbacks unless the specified input_select is set to one or more of the nominated (comma separated) values. This is useful to allow certain Apps to be turned on and off according to some flag, e.g. a house mode flag.
+
+```ini
+# Single value
+constrain_input_select = input_select.house_mode,Day
+# or multiple values
+constrain_input_select = input_select.house_mode,Day,Evening,Night
+```
+
+### presence
+The presence constraint will constrain based on presence of device trackers. It takes 3 possible values:
+- `noone` - only allow callback execution when no one is home
+- `anyone` - only allow callback execution when one or more person is home
+- `everyone` - only allow callback execution when everyone is home
+
+```ini
+constrain_presence = anyone
+# or
+constrain_presence = someone
+# or
+constrain_presence = noone
+```
+
+### time
+The time constraint consists of 2 variables, `constrain_start_time` and `constrain_end_time`. Callbacks will only be executed if the current time is between the start and end times.
+- If both are absent no time constraint will exist
+- If only start is present, end will default to 1 second before midnight
+- If only end is present, start will default to midnight
+
+The times are specified in a string format with one of the following formats:
+- HH:MM:SS - the time in Hours Minutes and Seconds, 24 hour format.
+- `sunrise`|`sunset` [+|- HH:MM:SS]- time of the next sunrise or sunset with an optional positive or negative offset in Hours Minutes and seconds
+
+The time based constraint system correctly interprets start and end times that span midnight.
+
+```ini
+# Run between 8am and 10pm
+constrain_start_time = 08:00:00
+constrain_end_time = 22:00:00
+# Run between sunrise and sunset
+constrain_start_time = sunrise
+constrain_end_time = sunset
+# Run between 45 minutes before sunset and 45 minutes after sunrise the next day
+constrain_start_time = sunset - 00:45:00
+constrain_end_time = sunrise + 00:45:00
+```
+
+### days
+The day constraint consists of as list of days for which the callbacks will fire, e.g.
+
+```ini
+constrain_days = mon,tue,wed
+```
+
+Callback constraints can also be applied to individual callbacks within Apps, see later for more details.
+
+## A Note on Threading
+
+AppDaemon is multithreaded. This means that any time code within an App is executed, it is executed by one of many threads. This is generally not a particularly important consideration for this application; in general, the execution time of callbacks is expected to be far quicker than the frequency of events causing them. However, it should be noted for completeness, that it is certainly possible for different pieces of code within the App to be executed concurrently, so some care may be necessary if different callback for instance inspect and change shared variables. This is a fairly standard caveat with concurrent programming, and if you know enough to want to do this, then you should know enough to put appropriate safeguards in place. For the average user however this shouldn't be an issue. If there are sufficient use cases to warrant it, I will consider adding locking to the function invocations to make the entire infrastructure threadsafe, but I am not convinced that it is necessary.
+
+An additional caveat of a threaded worker pool environment is that it is the expectation that none of the callbacks tie threads up for a significant amount of time. To do so would eventually lead to thread exhaustion, which would make the system run behind events. No events would be lost as they would be queued, but callbacks would be delayed which is a bad thing.
+
+Given the above, NEVER use Python's `time.sleep()` if you want to perform an operation some time in the future, as this will tie up a thread for the period of the sleep. Instead use the scheduler's `run_in()` function which will allow you to delay without blocking any threads.
+
+## State Operations
+
+### A note on Home Assistant State
+
+State within Home Assistant is stored as a collection of dictionaries, one for each entity. Each entity's dictionary will have some common fields and a number of entity type specific fields The state for an entity will always have the attributes:
+
+- `last_updated`
+- `last_changed`
+- `state`
+
+Any other attributes such as brightness for a lamp will only be present if the entity supports them, and will be stored in a sub-dictionary called `attributes`. When specifying these optional attributes in the `get_state()` call, no special distinction is required between the main attributes and the optional ones - `get_state()` will figure it out for you.
+
+Also bear in mind that some attributes such as brightness for a light, will not be present when the light is off.
+
+In most cases, the attribute `state` has the most important value in it, e.g. for a light or switch this will be `on` or `off`, for a sensor it will be the value of that sensor. Many of the AppDaemon API calls and callbacks will implicitly return the value of state unless told to do otherwise.
+
+### get_state()
+
+#### Synopsis
+
+```python
+get_state(entity = None, attribute = None)
+```
+
+`get_state()` is used to query the state of any component within Home Assistant. State updates are continuously tracked so this call runs locally and does not require AppDaemon to call back to Home Assistant and as such is very efficient.
+
+#### Returns
+
+`get_state()` returns a `dictionary` or single value, the structure of which varies according to the parameters used.
+
+#### Parameters
+
+All parameters are optional, and if `get_state()` is called with no parameters it will return the entire state of Home Assistant at that given time. This will consist of a dictionary with a key for each entity. Under that key will be the standard entity state information.
+
+##### entity
+
+This is the name of an entity or device type. If just a device type is provided, e.g. `light` or `binary_sensor`, `get_state()` will return a dictionary of all devices of that type, indexed by the entity_id, containing all the state for each entity.
+
+If a fully qualified `entity_id` is provided, `get_state()` will return the state attribute for that entity, e.g. `on` or `off` for a light.
+
+##### attribute
+
+Name of an attribute within the entity state object. If this parameter is specified in addition to a fully qualified `entity_id`, a single value representing the attribute will be returned, or `None` if it is not present.
+
+The value `all` for attribute has special significance and will return the entire state dictionary for the specified entity rather than an individual attribute value.
+
+#### Examples
+
+```python
+# Return state for the entire system
+state = self.get_state()
+
+# Return state for all switches in the system
+state = self.get_state("switch")
+
+# Return the state attribute for light.office_1
+state = self.get_state("light.office_1")
+
+# Return the brightness attribute for light.office_1
+state = self.get_state("light.office_1", "brightness")
+
+# Return the entire state for light.office_1
+state = self.get_state("light.office_1", "all")
+```
+
+### set_state()
+
+`set_state()` will make a call back to Home Assistant and make changes to the internal state of Home Assistant. This is not something that you would usually want to do and the applications are limited however the call is included for completeness. Note that for instance, setting the state of a light to `on` won't actually switch the device on, it will merely change the state of the device in Home Assistant so that it no longer reflects reality. In most cases, the state will be corrected the next time Home Assistant polls the device or someone causes a state change manually. To effect actual changes of devices use one of the service call functions.
+
+One possible use case for `set_state()` is for testing. If for instance you are writing an App to turn on a light when it gets dark according to a luminance sensor, you can use `set_state()` to temporarily change the light level reported by the sensor to test your program. However this is also possible using the developer tools.
+
+At the time of writing, it appears that no checking is done as to whether or not the entity exists, so it is possible to add entirely new entries to Home Assistant's state with this call.
+
+#### Synopsis
+
+```python
+set_state(entity_id, **kwargs)
+```
+
+#### Returns
+
+`set_state()` returns a dictionary representing the state of the device after the call has completed.
+
+#### Parameters
+
+##### entity_id
+
+Entity id for which the state is to be set, e.g. `light.office_1`.
+
+##### values
+
+A list of keyword values to be changed or added to the entities state. e.g. `state = "off"`. Note that any optional attributes such as colors for bulbs etc, need to reside in a dictionary called `attributes`; see the example.
+
+#### Examples
+
+```python
+status = self.set_state("light.office_1", state = "on", attributes = {"color_name": "red"})
+```
+
+### About Callbacks
+
+A large proportion of home automation revolves around waiting for something to happen and then reacting to it; a light level drops, the sun rises, a door opens etc. Home Assistant keeps track of every state change that occurs within the system and streams that information to AppDaemon almost immediately.
+
+An individual App however usually doesn't care about the majority of state changes going on in the system; Apps usually care about something very specific, like a specific sensor or light. Apps need a way to be notified when a state change happens that they care about, and be able to ignore the rest. They do this through registering callbacks. A callback allows the App to describe exactly what it is interested in, and tells AppDaemon to make a call into its code in a specific place to be able to react to it - this is a very familiar concept to anyone familiar with event-based programming.
+
+There are 3 types of callbacks within AppDaemon:
+
+- State Callbacks - react to a change in state
+- Scheduler Callbacks - react to a specific time or interval
+- Event Callbacks - react to specific Home Assistant and Appdaemon events.
+
+All callbacks allow the user to specify additional parameters to be handed to the callback via the standard Python `**kwargs` mechanism for greater flexibility.
+
+### About Registering Callbacks
+
+Each of the various types of callback have their own function or functions for registering the callback:
+
+- `listen_state()` for state callbacks
+- Various scheduler calls such as `run_once()` for scheduler callbacks
+- `listen_event()` for event callbacks.
+
+Each type of callback shares a number of common mechanisms that increase flexibility.
+
+#### Callback Level Constraints
+
+When registering a callback, you can add constraints identical to the Application level constraints described earlier. The difference is that a constraint applied to an individual callback only affects that callback and no other. The constraints are applied by adding Python keyword-value style arguments after the positional arguments. The parameters themselves are named identically to the previously described constraints and have identical functionality. For instance, adding:
+
+`constrain_presence="everyone"`
+
+to a callback registration will ensure that the callback is only run if the callback conditions are met and in addition everyone is present although any other callbacks might run whenever their event fires if they have no constraints.
+
+For example:
+
+`self.listen_state(self.motion, "binary_sensor.drive", constrain_presence="everyone")`
+
+#### User Arguments
+
+Any callback has the ability to allow the App creator to pass through arbitrary keyword arguments that will be presented to the callback when it is run. The arguments are added after the positional parameters just like the constraints. The only restriction is that they cannot be the same as any constraint name for obvious reasons. For example, to pass the parameter `arg1 = "home assistant"` through to a callback you would register a callback as follows:
+
+`self.listen_state(self.motion, "binary_sensor.drive", arg1="home assistant")`
+
+Then in the callback you could use it as follows:
+
+```python
+def motion(self, entity, attribute, old, new, **kwargs):
+ self.log("Arg1 is {}".format(kwargs["arg1"]))
+```
+
+### State Callbacks
+
+AppDaemons's state callbacks allow an App to listen to a wide variety of events, from every state change in the system, right down to a change of a single attribute of a particular entity. Setting up a callback is done using a single API call `listen_state()` which takes various arguments to allow it to do all of the above. Apps can register as many or as few callbacks as they want.
+
+### About State Callback Functions
+
+When calling back into the App, the App must provide a class function with a known signature for AppDaemon to call. The callback will provide various information to the function to enable the function to respond appropriately. For state callbacks, a class defined callback function should look like this:
+
+```python
+ def my_callback(self, entity, attribute, old, new, **kwargs):
+
+```
+
+You can call the function whatever you like - you will reference it in the `listen_state()` call, and you can create as many callback functions as you need.
+
+The parameters have the following meanings:
+
+#### self
+
+A standard Python object reference.
+
+#### entity
+
+Name of the entity the callback was requested for or `None`.
+
+#### attribute
+
+Name of the attribute the callback was requested for or `None`.
+
+#### old
+
+The value of the state before the state change.
+
+#### new
+
+The value of the state after the state change.
+
+`old` and `new` will have varying types depending on the type of callback.
+
+#### \*\*kwargs
+
+A dictionary containing any constraints and/or additional user specific keyword arguments supplied to the `listen_state()` call.
+
+### listen_state()
+
+`listen_state()` allows the user to register a callback for a wide variety of state changes.
+
+#### Synopsis
+
+```python
+handle = listen_state(callback, entity = None, **kwargs)
+```
+
+#### Returns
+
+A unique identifier that can be used to cancel the callback if required. Since variables created within object methods are local to the function they are created in, and in all likelihood the cancellation will be invoked later in a different function, it is recommended that handles are stored in the object namespace, e.g. `self.handle`.
+
+#### Parameters
+
+All parameters except `callback` are optional, and if `listen_state()` is called with no additional parameters it will subscribe to any state change within Home Assistant.
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard State Callback format documented above.
+
+##### entity
+
+This is the name of an entity or device type. If just a device type is provided, e.g. `light` or `binary_sensor`, `listen_state()` will subscribe to state changes of all devices of that type. If a fully qualified `entity_id` is provided, `listen_state()` will listen for state changes for just that entity.
+
+When called, AppDaemon will supply the callback function, in old and new, with the state attribute for that entity, e.g. `on` or `off` for a light.
+
+##### attribute (optional)
+
+Name of an attribute within the entity state object. If this parameter is specified in addition to a fully qualified `entity_id`, `listen_state()` will subscribe to changes for just that attribute within that specific entity. The new and old parameters in the callback function will be provided with a single value representing the attribute.
+
+The value `all` for attribute has special significance and will listen for any state change within the specified entity, and supply the callback functions with the entire state dictionary for the specified entity rather than an individual attribute value.
+
+##### new = (optional)
+
+If `new` is supplied as a parameter, callbacks will only be made if the state of the selected attribute (usually `state`) in the new state match the value of `new`.
+
+##### old = (optional)
+
+If `old` is supplied as a parameter, callbacks will only be made if the state of the selected attribute (usually `state`) in the old state match the value of `old`.
+
+Note: `old` and `new` can be used singly or together.
+
+##### duration = (optional)
+
+If duration is supplied as a parameter, the callback will not fire unless the state listened for is maintained for that number of seconds. This makes the most sense if a specific attribute is specified (or the default os `state` is used), an in conjunction with the `old` or `new` parameters, or both. When the callback is called, it is supplied with the values of `entity`, `attr`, `old` and `new` that were current at the time the actual event occured, since the assumption is that none of them have changed in the intervening period.
+
+```python
+ def my_callback(self, **kwargs):
+
+```
+
+(Scheduler callbacks are documented in detail laer in this document)
+
+##### \*\*kwargs
+
+Zero or more keyword arguments that will be supplied to the callback when it is called.
+
+#### Examples
+
+```python
+# Listen for any state change and return the state attribute
+self.handle = self.listen_state(self.my_callback)
+
+# Listen for any state change involving a light and return the state attribute
+self.handle = self.listen_state(self.my_callback, "light")
+
+# Listen for a state change involving light.office1 and return the state attribute
+self.handle = self.listen_state(self.my_callback, "light.office_1")
+
+# Listen for a state change involving light.office1 and return the entire state as a dict
+self.handle = self.listen_state(self.my_callback, "light.office_1", attribute = "all")
+
+# Listen for a state change involving the brightness attribute of light.office1
+self.handle = self.listen_state(self.my_callback, "light.office_1", attribute = "brightness")
+
+# Listen for a state change involving light.office1 turning on and return the state attribute
+self.handle = self.listen_state(self.my_callback, "light.office_1", new = "on")
+
+# Listen for a state change involving light.office1 changing from brightness 100 to 200 and return the state attribute
+self.handle = self.listen_state(self.my_callback, "light.office_1", old = "100", new = "200")
+
+# Listen for a state change involving light.office1 changing to state on and remaining on for a minute
+self.handle = self.listen_state(self.my_callback, "light.office_1", new = "on", duration = 60)
+
+```
+
+
+### cancel_listen_state()
+
+Cancel a `listen_state()` callback. This will mean that the App will no longer be notified for the specific state change that has been cancelled. Other state changes will continue to be monitored.
+
+#### Synopsis
+
+```python
+cancel_listen_state(handle)
+```
+
+#### Returns
+
+Nothing
+
+#### Parameters
+
+##### handle
+
+The handle returned when the `listen_state()` call was made.
+
+#### Examples
+
+```python
+self.cancel_listen_state(self.office_light_handle)
+```
+
+### info_listen_state()
+
+Get information on state a callback from it's handle.
+
+#### Synopsis
+
+```python
+entity, attribute, kwargs = self.info_listen_state(self.handle)
+```
+
+#### Returns
+
+entity, attribute, kwargs - the values supplied when the callback was initially created.
+
+#### Parameters
+
+##### handle
+
+The handle returned when the `listen_state()` call was made.
+
+#### Examples
+
+```python
+entity, attribute, kwargs = self.info_listen_state(self.handle)
+```
+
+## Scheduler
+
+AppDaemon contains a powerful scheduler that is able to run with 1 second resolution to fire off specific events at set times, or after set delays, or even relative to sunrise and sunset. In general, events should be fired less than a second after specified but under certain circumstances there may be short additional delays.
+
+### About Schedule Callbacks
+
+As with State Change callbacks, Scheduler Callbacks expect to call into functions with a known and specific signature and a class defined Scheduler callback function should look like this:
+
+```python
+ def my_callback(self, **kwargs):
+
+```
+
+You can call the function whatever you like; you will reference it in the Scheduler call, and you can create as many callback functions as you need.
+
+The parameters have the following meanings:
+
+#### self
+A standard Python object reference
+
+#### \*\*kwargs
+
+A dictionary containing Zero or more keyword arguments to be supplied to the callback.
+
+### Creation of Scheduler Callbacks
+
+Scheduler callbacks are created through use of a number of convenience functions which can be used to suit the situation.
+
+#### run_in()
+
+Run the callback in a defined number of seconds. This is used to add a delay, for instance a 60 second delay before a light is turned off after it has been triggered by a motion detector. This callback should always be used instead of `time.sleep()` as discussed previously.
+
+#### Synopsis
+
+```python
+self.handle = self.run_in(callback, delay, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### delay
+
+Delay, in seconds before the callback is invoked.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+self.handle = self.run_in(self.run_in_c)
+self.handle = self.run_in(self.run_in_c, title = "run_in5")
+```
+#### run_once()
+
+Run the callback once, at the specified time of day. If the time of day is in the past, the callback will occur on the next day.
+
+#### Synopsis
+
+```python
+self.handle = self.run_once(callback, time, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### time
+
+A Python `time` object that specifies when the callback will occur. If the time specified is in the past, the callback will occur the next day at the specified time.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run at 4pm today, or 4pm tomorrow if it is already after 4pm
+import datetime
+...
+runtime = datetime.time(16, 0, 0)
+handle = self.run_once(self.run_once_c, runtime)
+```
+
+#### run_at()
+
+Run the callback once, at the specified date and time.
+
+#### Synopsis
+
+```python
+self.handle = self.run_at(callback, datetime, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer. `run_at()` will raise an exception if the specified time is in the past.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### datetime
+
+A Python `datetime` object that specifies when the callback will occur.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run at 4pm today
+import datetime
+...
+runtime = datetime.time(16, 0, 0)
+today = datetime.date.today()
+event = datetime.datetime.combine(today, runtime)
+handle = self.run_once(self.run_once_c, event)
+```
+#### run_daily()
+
+Execute a callback at the same time every day. If the time has already passed, the function will not be invoked until the following day at the specified time.
+
+#### Synopsis
+
+```python
+self.handle = self.run_daily(callback, time, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### time
+
+A Python `time` object that specifies when the callback will occur. If the time specified is in the past, the callback will occur the next day at the specified time.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run daily at 7pm
+import datetime
+...
+time = datetime.time(19, 0, 0)
+self.run_daily(self.run_daily_c, runtime)
+```
+
+#### run_hourly()
+
+Execute a callback at the same time every hour. If the time has already passed, the function will not be invoked until the following hour at the specified time.
+
+#### Synopsis
+
+```python
+self.handle = self.run_hourly(callback, time = None, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### time
+
+A Python `time` object that specifies when the callback will occur, the hour component of the time object is ignored. If the time specified is in the past, the callback will occur the next hour at the specified time. If time is not supplied, the callback will start an hour from the time that `run_hourly()` was executed.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run every hour, on the hour
+import datetime
+...
+time = datetime.time(0, 0, 0)
+self.run_daily(self.run_daily_c, runtime)
+```
+#### run_minutely()
+
+Execute a callback at the same time every minute. If the time has already passed, the function will not be invoked until the following minute at the specified time.
+
+#### Synopsis
+
+```python
+self.handle = self.run_minutely(callback, time = None, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### time
+
+A Python `time` object that specifies when the callback will occur, the hour and minute components of the time object are ignored. If the time specified is in the past, the callback will occur the next hour at the specified time. If time is not supplied, the callback will start a minute from the time that `run_minutely()` was executed.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run Every Minute on the minute
+import datetime
+...
+time = datetime.time(0, 0, 0)
+self.run_minutely(self.run_minutely_c, time)
+```
+
+#### run_every()
+
+Execute a repeating callback with a configurable delay starting at a specific time.
+
+#### Synopsis
+
+```python
+self.handle = self.run_every(callback, time, repeat, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### time
+
+A Python `time` object that specifies when the initial callback will occur.
+
+##### repeat
+
+After the initial callback has occurred, another will occur every `repeat` seconds.
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Run every 17 minutes starting in 2 hours time
+import datetime
+...
+self.run_every(self.run_every_c, time, 17 * 60)
+```
+
+#### cancel_timer()
+Cancel a previously created timer
+
+#### Synopsis
+
+```python
+self.cancel_timer(handle)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### handle
+
+A handle value returned from the original call to create the timer.
+
+#### Examples
+
+```python
+self.cancel_timer(handle)
+```
+
+### info_timer()
+
+Get information on a scheduler event from it's handle.
+
+#### Synopsis
+
+```python
+time, interval, kwargs = self.info_timer(handle)
+```
+
+#### Returns
+
+time - datetime object representing the next time the callback will be fired
+
+interval - repeat interval if applicable, `0` otherwise.
+
+kwargs - the values supplied when the callback was initially created.
+
+#### Parameters
+
+##### handle
+
+The handle returned when the scheduler call was made.
+
+#### Examples
+
+```python
+time, interval, kwargs = self.info_timer(handle)
+```
+
+
+
+### Scheduler Ransomization
+
+All of the scheduler calls above support 2 additional optional arguments, `random_start` and `random_end`. Using these arguments it is possible to randomize the firing of callbacks to the degree desired by setting the appropriate number of seconds with the parameters.
+
+- `random_start` - start of range of the random time
+- `random_end` - end of range of the random time
+
+`random_start` must always be numerically lower than `random_end`, they can be negative to denote a random offset before and event, or positive to denote a random offset after an event. The event would be a an absolute or relative time or sunrise/sunset depending on whcih scheduler call you use and these values affect the base time by the spcified amount. If not specified, they will default to `0`.
+
+For example:
+
+```python
+# Run a callback in 2 minutes minus a random number of seconds between 0 and 60, e.g. run between 60 and 120 seconds from now
+self.handle = self.run_in(callback, 120, random_start = -60, **kwargs)
+# Run a callback in 2 minutes plus a random number of seconds between 0 and 60, e.g. run between 120 and 180 seconds from now
+self.handle = self.run_in(callback, 120, random_end = 60, **kwargs)
+# Run a callback in 2 minutes plus or minus a random number of seconds between 0 and 60, e.g. run between 60 and 180 seconds from now
+self.handle = self.run_in(callback, 120, random_start = -60, random_end = 60, **kwargs)
+```
+
+## Sunrise and Sunset
+
+AppDaemon has a number of features to allow easy tracking of sunrise and sunset as well as a couple of scheduler functions. Note that the scheduler functions also support the randomization parameters described above, but they cannot be used in conjunction with the `offset` parameter`.
+
+### run_at_sunrise()
+
+Run a callback at or around sunrise.
+
+#### Synopsis
+
+```python
+self.handle = self.run_at_sunrise(callback, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### offset =
+
+The time in seconds that the callback should be delayed after sunrise. A negative value will result in the callback occurring before sunrise. This parameter cannot be combined with `random_start` or `random_end`
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+import datetime
+...
+# Run 45 minutes before sunset
+self.run_at_sunrise(self.sun, offset = datetime.timedelta(minutes = -45).total_seconds(), "Sunrise -45 mins")
+# or you can just do the math yourself
+self.run_at_sunrise(self.sun, offset = 30 * 60, "Sunrise +30 mins")
+# Run at a random time +/- 60 minutes from sunrise
+self.run_at_sunrise(self.sun, random_start = -60*60, random_end = 60*60, "Sunrise, random +/- 60 mins")
+# Run at a random time between 30 and 60 minutes before sunrise
+self.run_at_sunrise(self.sun, random_start = -60*60, random_end = 30*60, "Sunrise, random - 30 - 60 mins")
+```
+
+### run_at_sunset()
+
+Run a callback at or around sunset.
+
+#### Synopsis
+
+```python
+self.handle = self.run_at_sunset(callback, offset, **kwargs)
+```
+
+#### Returns
+
+A handle that can be used to cancel the timer.
+
+#### Parameters
+
+##### callback
+
+Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
+
+##### offset =
+
+The time in seconds that the callback should be delayed after sunrise. A negative value will result in the callback occurring before sunrise. This parameter cannot be combined with `random_start` or `random_end`
+
+##### \*\*kwargs
+
+Arbitary keyword parameters to be provided to the callback function when it is invoked.
+
+#### Examples
+
+```python
+# Example using timedelta
+import datetime
+...
+self.run_at_sunset(self.sun, datetime.timedelta(minutes = -45).total_seconds(), "Sunset -45 mins")
+# or you can just do the math yourself
+self.run_at_sunset(self.sun, 30 * 60, "Sunset +30 mins")
+# Run at a random time +/- 60 minutes from sunset
+self.run_at_sunset(self.sun, random_start = -60*60, random_end = 60*60, "Sunset, random +/- 60 mins")
+# Run at a random time between 30 and 60 minutes before sunset
+self.run_at_sunset(self.sun, random_start = -60*60, random_end = 30*60, "Sunset, random - 30 - 60 mins")
+```
+### sunrise()
+
+Return the time that the next Sunrise will occur.
+
+#### Synopsis
+
+```python
+self.sunrise()
+```
+
+#### Returns
+
+A Python datetime that represents the next time Sunrise will occur.
+
+#### Examples
+
+```python
+rise_time = self.sunrise()
+```
+### sunset()
+
+Return the time that the next Sunset will occur.
+
+#### Synopsis
+
+```python
+self.sunset()
+```
+
+#### Returns
+
+A Python datetime that represents the next time Sunset will occur.
+
+#### Examples
+
+```python
+set_time = self.sunset()
+```
+### sun_up()
+
+A function that allows you to determine if the sun is currently up.
+
+#### Synopsis
+
+```python
+result = self.sun_up()
+```
+
+#### Returns
+
+`True` if the sun is up, False otherwise.
+
+#### Examples
+
+```python
+if self.sun_up():
+ do something
+```
+
+### sun_down()
+
+A function that allows you to determine if the sun is currently down.
+
+#### Synopsis
+
+```python
+result = self.sun_down()
+```
+
+#### Returns
+
+`True` if the sun is down, False otherwise.
+
+#### Examples
+
+```python
+if self.sun_down():
+ do something
+```
+## Calling Services
+
+### About Services
+
+Services within Home Assistant are how changes are made to the system and its devices. Services can be used to turn lights on and off, set thermostats and a whole number of other things. Home Assistant supplies a single interface to all these disparate services that take arbitrary parameters. AppDaemon provides the `call_service()` function to call into Home Assistant and run a service. In addition, it also provides convenience functions for some of the more common services making calling them a little easier.
+
+### call_service()
+
+Call service is the basic way of calling a service within AppDaemon. It can call any service and provide any required parameters. Available services can be found using the developer tools in the UI. For listed services, the part before the first period is the domain, and the part after is the service name. For instance, `light.turn_on` has a domain of `light` and a service name of `turn_on`.
+
+#### Synopsis
+
+```python
+self.call_service(self, service, **kwargs)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### service
+
+The service name, e.g. `light.turn_on`.
+
+##### \*\*kwargs
+
+Each service has different parameter requirements. This argument allows you to specify a comma separated list of keyword value pairs, e.g. `entity_id = light.office_1`. These parameters will be different for every service and can be discovered using the developer tools. Most if not all service calls require an `entity_id` however, so use of the above example is very common with this call.
+
+#### Examples
+
+```python
+self.call_service("light.turn_on", entity_id = "light/office_lamp", color_name = "red")
+self.call_service("notify/notify", title = "Hello", message = "Hello World")
+```
+### turn_on()
+
+This is a convenience function for the `homassistant.turn_on` function. It is able to turn on pretty much anything in Home Assistant that can be turned on or run:
+
+- Lights
+- Switches
+- Scenes
+- Scripts
+
+And many more.
+
+#### Synopsis
+
+```python
+self.turn_on(entity_id, **kwargs)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity_id of the thing to be turned on, e.g. `light.office_lamp` or ```scene.downstairs_on```
+
+##### \*\*kwargs
+
+A comma separated list of key value pairs to allow specification of parameters over and above `entity_id`.
+
+#### Examples
+
+```python
+self.turn_on("switch.patio_lights")
+self.turn_on("scene.bedrrom_on")
+self.turn_on("light.office_1", color_name = "green")
+```
+
+### turn_off()
+
+This is a convenience function for the `homassistant.turn_off` function. Like `homeassistant.turn_on`, it is able to turn off pretty much anything in Home Assistant that can be turned off.
+
+#### Synopsis
+
+```python
+self.turn_off(entity_id)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity_id of the thing to be turned off, e.g. `light.office_lamp` or `scene.downstairs_on`.
+
+#### Examples
+
+```python
+self.turn_off("switch.patio_lights")
+self.turn_off("light.office_1")
+```
+
+### toggle()
+
+This is a convenience function for the `homassistant.toggle` function. It is able to flip the state of pretty much anything in Home Assistant that can be turned on or off.
+
+#### Synopsis
+
+```python
+self.toggle(entity_id)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity_id of the thing to be toggled, e.g. `light.office_lamp` or `scene.downstairs_on`.
+
+#### Examples
+
+```python
+self.toggle("switch.patio_lights")
+self.toggle("light.office_1", color_name = "green")
+```
+
+### select_value()
+
+This is a convenience function for the `input_slider.select_value` function. It is able to set the value of an input_slider in Home Assistant.
+
+#### Synopsis
+
+```python
+self.select_value(entity_id, value)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity_id of the input_slider to be changed, e.g. `input_slider.alarm_hour`.
+
+##### value
+
+The new value to set the input slider to.
+
+#### Examples
+
+```python
+self.select_value("input_slider.alarm_hour", 6)
+```
+
+### select_option()
+
+This is a convenience function for the `input_select.select_option` function. It is able to set the value of an input_select in Home Assistant.
+
+#### Synopsis
+
+```python
+self.select_option(entity_id, option)
+```
+
+#### Returns
+
+None
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity_id of the input_select to be changed, e.g. `input_select.mode`.
+
+##### value
+
+The new value to set the input slider to.
+
+#### Examples
+
+```python
+self.select_option("input_select.mode", "Day")
+```
+
+### notify()
+
+This is a convenience function for the `notify.notify` service. It will send a notification to your defualt notification service. If you have more than one, use `call_service()` to call the specific notification service you require instead.
+
+#### Synopsis
+
+```python
+notify(message, title=None)
+```
+#### Returns
+
+None
+
+#### Parameters
+
+##### message
+
+Message to be sent to the notification service.
+
+##### title
+
+Title of the notification - optional.
+
+#### Examples
+
+```python
+self.notify("", "Switching mode to Evening")
+```
+
+## Events
+
+### About Events
+
+Events are a fundamental part of how Home Assistant works under the covers. HA has an event bus that all components can read and write to, enabling components to inform other components when important events take place. We have already seen how state changes can be propagated to AppDaemon - a state change however is merely an example of an event within Home Assistant. There are several other event types, among them are:
+
+- `homeassistant_start`
+- `homeassistant_stop`
+- `state_changed`
+- `service_registered`
+- `call_service`
+- `service_executed`
+- `platform_discovered`
+- `component_loaded`
+
+Using AppDaemon, it is possible to subscribe to specific events as well as fire off events.
+
+In addition to the Home Assistant supplied events, AppDaemon adds 2 more events. These are internal to AppDaemon and are not visible on the Home Assistant bus:
+
+- `appd_started` - fired once when AppDaemon is first started and after Apps are initialized
+- `ha_started` - fired every time AppDaemon detects a Home Assistant restart
+
+### About Event Callbacks
+
+As with State Change and Scheduler callbacks, Event Callbacks expect to call into functions with a known and specific signature and a class defined Scheduler callback function should look like this:
+
+```python
+ def my_callback(self, event_name, data, kwargs):
+
+```
+
+You can call the function whatever you like - you will reference it in the Scheduler call, and you can create as many callback functions as you need.
+
+The parameters have the following meanings:
+
+#### self
+
+A standard Python object reference.
+
+#### event_name
+
+Name of the event that was called, e.g. `call_service`.
+
+#### data
+
+Any data that the system supplied with the event as a dict.
+
+#### kwargs
+
+A dictionary containing Zero or more user keyword arguments to be supplied to the callback.
+
+### listen_event()
+
+Listen event sets up a callback for a specific event, or any event.
+
+#### Synopsis
+
+```python
+handle = listen_event(function, event = None, **kwargs):
+```
+#### Returns
+
+A handle that can be used to cancel the callback.
+
+#### Parameters
+
+##### function
+
+The function to be called when the event is fired.
+
+##### event
+
+Name of the event to subscribe to. Can be a standard Home Assistant event such as `service_registered` or an arbitrary custom event such as `"MODE_CHANGE"`. If no event is specified, `listen_event()` will subscribe to all events.
+
+##### \*\*kwargs (optional)
+
+One or more keyword value pairs representing App specific parameters to supply to the callback. If the keywords match values within the event data, they will act as filters, meaning that if they don't match the values, the callback will not fire.
+
+As an example of this, a Minimote controller when activated will generate an event called `zwave.scene_activated`, along with 2 pieces of data that are specific to the event - `entity_id` and `scene`. If you include keyword values for either of those, the values supplied to the `listen_event()1 call must match the values in the event or it will not fire. If the keywords do not match any of the data in the event they are simply ignored.
+
+Filtering will work with any event type, but it will be necessary to figure out the data associated with the event to understand what values can be filtered on. This can be achieved by examining Home Assistant's logfiles when the event fires.
+
+#### Examples
+```python
+self.listen_event(self.mode_event, "MODE_CHANGE")
+# Listen for a minimote event activating scene 3:
+self.listen_event(self.generic_event, "zwave.scene_activated", scene_id = 3)
+# Listen for a minimote event activating scene 3 from a specific minimote:
+self.listen_event(self.generic_event, "zwave.scene_activated", entity_id = "minimote_31", scene_id = 3)
+```
+
+### cancel_listen_event()
+
+Cancels callbacks for a specific event.
+
+#### Synopsis
+
+```python
+cancel_listen_event(handle)
+```
+#### Returns
+
+None.
+
+#### Parameters
+
+##### handle
+
+A handle returned from a previous call to `listen_event()`.
+
+#### Examples
+
+```python
+self.cancel_listen_event(handle)
+```
+
+### info_listen_event()
+
+Get information on an event callback from it's handle.
+
+#### Synopsis
+
+```python
+service, kwargs = self.info_listen_event(handle)
+```
+
+#### Returns
+
+service, kwargs - the values supplied when the callback was initially created.
+
+#### Parameters
+
+##### handle
+
+The handle returned when the `listen_event()` call was made.
+
+#### Examples
+
+```python
+service, kwargs = self.info_listen_event(handle)
+```
+
+
+### fire_event()
+
+Fire an event on the HomeAssistant bus, for other components to hear.
+
+#### Synopsis
+
+```python
+fire_event(event, **kwargs)
+```
+
+#### Returns
+
+None.
+
+#### Parameters
+
+##### event
+
+Name of the event. Can be a standard Home Assistant event such as `service_registered` or an arbitrary custom event such as `"MODE_CHANGE"`.
+
+##### \*\*kwargs
+
+Zero or more keyword arguments that will be supplied as part of the event.
+
+#### Examples
+
+```python
+self.fire_event("MY_CUSTOM_EVENT", jam="true")
+```
+
+### Event Callback Function Signature
+
+Functions called as an event callback will be supplied with 2 arguments:
+
+```python
+def service(self, event_name, data):
+```
+
+#### event_name
+
+The name of the event that caused the callback, e.g. `"MODE_CHANGE"` or `call_service`.
+
+#### data
+
+A dictionary containing any additional information associated with the event.
+
+### Use of Events for Signalling between Home Assistant and AppDaemon
+
+Home Assistant allows for the creation of custom events and existing components can send and receive them. This provides a useful mechanism for signaling back and forth between Home Assistant and AppDaemon. For instance, if you would like to create a UI Element to fire off some code in Home Assistant, all that is necessary is to create a script to fire a custom event, then subscribe to that event in AppDaemon. The script would look something like this:
+
+```yaml
+alias: Day
+sequence:
+- event: MODE_CHANGE
+ event_data:
+ mode: Day
+```
+
+The custom event `MODE_CHANGE` would be subscribed to with:
+
+```python
+self.listen_event(self.mode_event, "MODE_CHANGE")
+```
+
+Home Assistant can send these events in a variety of other places - within automations, and also directly from Alexa intents. Home Assistant can also listen for custom events with it's automation component. This can be used to signal from AppDaemon code back to home assistant. Here is a sample automation:
+
+```yaml
+automation:
+ trigger:
+ platform: event
+ event_type: MODE_CHANGE
+ ...
+ ...
+```
+
+This can be triggered with a call to AppDaemon's fire_event() as follows:
+
+```python
+self.fire_event("MODE_CHANGE", mode = "Day")
+```
+
+## Presence
+
+Presence in Home Assistant is tracked using Device Trackers. The state of all device trackers can be found using the `get_state()` call, however AppDaemon provides several convenience functions to make this easier.
+
+### get_trackers()
+
+Return a list of all device trackers. This is designed to be iterated over.
+
+#### Synopsis
+
+```python
+tracker_list = get_trackers()
+```
+#### Returns
+
+An iterable list of all device trackers.
+
+#### Examples
+
+```python
+trackers = self.get_trackers()
+for tracker in trackers:
+ do something
+```
+
+### get_tracker_state()
+
+Get the state of a tracker. The values returned depend in part on the configuration and type of device trackers in the system. Simpler tracker types like `Locative` or `NMAP` will return one of 2 states:
+
+- `home`
+- `not_home`
+
+Some types of device tracker are in addition able to supply locations that have been configured as Geofences, in which case the name of that location can be returned.
+
+#### Synopsis
+
+```python
+location = self.get_tracker_state(tracker_id)
+```
+
+#### Returns
+
+A string representing the location of the tracker.
+
+#### Parameters
+
+##### tracker_id
+
+Fully qualified entity_id of the device tracker to query, e.g. `device_tracker.andrew`.
+
+#### Examples
+
+```python
+trackers = self.get_trackers()
+for tracker in trackers:
+ self.log("{} is {}".format(tracker, self.get_tracker_state(tracker)))
+```
+
+### everyone_home()
+
+A convenience function to determine if everyone is home. Use this in preference to getting the state of `group.all_devices()` as it avoids a race condition when using state change callbacks for device trackers.
+
+#### Synopsis
+
+```python
+result = self.everyone_home()
+```
+#### Returns
+
+Returns `True` if everyone is at home, `False` otherwise.
+
+#### Examples
+
+```python
+if self.everyone_home():
+ do something
+```
+### anyone_home()
+
+A convenience function to determine if one or more person is home. Use this in preference to getting the state of `group.all_devices()` as it avoids a race condition when using state change callbacks for device trackers.
+
+#### Synopsis
+
+```python
+result = self.anyone_home()
+```
+
+#### Returns
+
+Returns `True` if anyone is at home, `False` otherwise.
+
+#### Examples
+
+```python
+if self.anyone_home():
+ do something
+```
+### noone_home()
+
+A convenience function to determine if no people are at home. Use this in preference to getting the state of group.all_devices() as it avoids a race condition when using state change callbacks for device trackers.
+
+#### Synopsis
+
+```python
+result = self.noone_home()
+```
+
+#### Returns
+
+Returns `True` if no one is home, `False` otherwise.
+
+#### Examples
+
+```python
+if self.noone_home():
+ do something
+```
+
+## Miscellaneous Helper Functions
+
+### time()
+
+Returns a python `time` object representing the current time. Use this in preference to the standard Python ways to discover the current time, especially when using the "Time Travel" feature for testing.
+
+#### Synopsis
+
+```python
+time()
+```
+
+#### Returns
+
+A localised Python time object representing the current AppDaemon time.
+
+#### Parameters
+
+None
+
+#### Example
+
+```python
+now = self.time()
+```
+
+### date()
+
+Returns a python `date` object representing the current date. Use this in preference to the standard Python ways to discover the current date, especially when using the "Time Travel" feature for testing.
+
+#### Synopsis
+
+```python
+date()
+```
+
+#### Returns
+
+A localised Python time object representing the current AppDaemon date.
+
+#### Parameters
+
+None
+
+#### Example
+
+```python
+today = self.date()
+```
+
+### datetime()
+
+Returns a python `datetime` object representing the current date and time. Use this in preference to the standard Python ways to discover the current time, especially when using the "Time Travel" feature for testing.
+
+#### Synopsis
+
+```python
+datetime()
+```
+
+#### Returns
+
+A localised Python datetime object representing the current AppDaemon date and time.
+
+#### Parameters
+
+None
+
+#### Example
+
+```python
+now = self.datetime()
+```
+
+
+### convert_utc()
+
+Home Assistant provides timestamps of several different sorts that may be used to gain additional insight into state changes. These timestamps are in UTC and are coded as ISO 8601 Combined date and time strings. `convert_utc()` will accept one of these strings and convert it to a localised Python datetime object representing the timestamp
+
+#### Synopsis
+
+```python
+convert_utc(utc_string)
+```
+
+#### Returns
+
+`convert_utc(utc_string)` returns a localised Python datetime object representing the timestamp.
+
+#### Parameters
+
+##### utc_string
+
+An ISO 8601 encoded date and time string in the following format: `2016-07-13T14:24:02.040658-04:00`
+
+#### Example
+
+### parse_time()
+
+Takes a string representation of a time, or sunrise or sunset offset and converts it to a `datetime.time` object.
+
+#### Synopsis
+
+```python
+parse_time(time_string)
+```
+
+#### Returns
+
+A `datetime.time` object, representing the time given in the `time_string` argument.
+
+#### Parameters
+
+##### time_string
+
+A representation of the time in a string format with one of the following formats:
+
+- HH:MM:SS - the time in Hours Minutes and Seconds, 24 hour format.
+- sunrise | sunset [+ | - HH:MM:SS]- time of the next sunrise or sunset with an optional positive or negative offset in Hours Minutes and seconds
+
+#### Example
+
+```python
+time = self.parse_time("17:30:00")
+time = self.parse_time("sunrise")
+time = self.parse_time("sunset + 00:30:00")
+time = self.parse_time("sunrise + 01:00:00")
+```
+
+### now_is_between()
+
+Takes two string representations of a time, or sunrise or sunset offset and returns true if the current time is between those 2 times. `now_is_between()` can correctly handle transitions across midnight.
+
+#### Synopsis
+
+```python
+now_is_between(start_time_string, end_time_string)
+```
+
+#### Returns
+
+`True` if the current time is within the specified start and end times, `False` otherwise.
+
+#### Parameters
+
+##### start_time_string, end_time_string
+
+A representation of the start and end time respectively in a string format with one of the following formats:
+
+- HH:MM:SS - the time in Hours Minutes and Seconds, 24 hour format.
+- sunrise | sunset [+ | - HH:MM:SS]- time of the next sunrise or sunset with an optional positive or negative offset in Hours Minutes and seconds
+
+#### Example
+
+```python
+if self.now_is_between("17:30:00", "08:00:00"):
+ do something
+if self.now_is_between("sunset - 00:45:00", "sunrise + 00:45:00"):
+ do something
+```
+
+### friendly_name()
+
+`frindly_name()` will return the Friendly Name of an entity if it has one.
+
+#### Synopsis
+
+```python
+Name = self.friendly_name(entity_id)
+```
+
+#### Returns
+
+The friendly name of the entity if it exists or the entity id if not.
+
+#### Example
+
+```python
+tracker = "device_tracker.andrew"
+self.log("{} ({}) is {}".format(tracker, self.friendly_name(tracker), self.get_tracker_state(tracker)))
+```
+
+### split_entity()
+
+`split_entity()` will take a fully qualified entity id of the form `light.hall_light` and split it into 2 values, the device and the entity, e.g. `light` and `hall_light`.
+
+#### Synopsis
+
+```python
+device, entity = self.split_entity(entity_id)
+```
+
+#### Parameters
+
+##### entity_id
+
+Fully qualified entity id to be split.
+
+#### Returns
+
+A list with 2 entries, the device and entity respectively.
+
+#### Example
+
+```python
+device, entity = self.split_entity(entity_id)
+if device == "scene":
+ do something specific to scenes
+```
+
+
+### get_app()
+
+`get_app()` will return the instantiated object of another app running within the system. This is useful for calling functions or accessing variables that reside in different apps without requiring duplication of code.
+
+#### Synopsis
+
+```python
+get_app(self, name)
+```
+#### Parameters
+
+##### name
+
+Name of the app required. This is the name specified in header section of the config file, not the module or class.
+
+#### Returns
+
+An object reference to the class.
+
+#### Example
+```python
+MyApp = self.get_app("MotionLights")
+MyApp.turn_light_on()
+```
+
+### split_device_list()
+
+`split_device_list()` will take a comma separated list of device types (or anything else for that matter) and return them as an iterable list. This is intended to assist in use cases where the App takes a list of entities from an argument, e.g. a list of sensors to monitor. If only one entry is provided, an iterable list will still be returned to avoid the need for special processing.
+
+#### Synopsis
+
+```python
+devices = split_device_list(list)
+```
+
+#### Returns
+
+A list of split devices with 1 or more entries.
+
+#### Example
+
+```python
+for sensor in self.split_device_list(self.args["sensors"]):
+ do something for each sensor, e.g. make a state subscription
+```
+
+
+### Writing to Logfiles
+
+AppDaemon uses 2 separate logs - the general log and the error log. An AppDaemon App can write to either of these using the supplied convenience methods `log()` and `error()`, which are provided as part of parent `AppDaemon` class, and the call will automatically pre-pend the name of the App making the call. The `-D` option of AppDaemon can be used to specify what level of logging is required and the logger objects will work as expected.
+
+### log()
+
+#### Synopsis
+
+```python
+log(message, level = "INFO")
+```
+
+#### Returns
+
+Nothing
+
+#### Parameters
+
+##### Message
+
+The message to log.
+
+##### level
+
+The log level of the message - takes a string representing the standard logger levels.
+
+#### Examples
+
+```python
+self.log("Log Test: Parameter is {}".format(some_variable))
+self.log("Log Test: Parameter is {}".format(some_variable), level = "ERROR")
+```
+
+### error()
+
+#### Synopsis
+
+```python
+error(message, level = "WARNING")
+```
+#### Returns
+
+Nothing
+
+#### Parameters
+
+##### Message
+
+The message to log.
+
+##### level
+
+The log level of the message - takes a string representing the standard logger levels.
+
+#### Examples
+
+```python
+self.error("Some Warning string")
+self.error("Some Critical string", level = "CRITICAL")
+```
+
+## Sharing information between Apps
+
+Sharing information between different Apps is very simple if required. Each app gets access to a global dictionary stored in a class attribute called `self.global_vars`. Any App can add or read any key as required. This operation is not however threadsafe so some car is needed.
+
+In addition, Apps have access to the entire configuration if required, meaning they can access AppDaemon configuration items as well as parameters from other Apps. To use this, there is a class attribute called `self.config`. It contains a `ConfigParser` object, which is similar in operation to a `Dictionary`. To access any apps parameters, simply reference the ConfigParser object using the Apps name (form the config file) as the first key, and the parameter required as the second, for instance:
+
+```python
+other_apps_arg = self.config["some_app"]["some_parameter"].
+```
+
+To get AppDaemon's config parameters, use the key "AppDaemon", e.g.:
+
+```python
+app_timezone = self.config["AppDaemon"]["time_zone"]
+```
+
+And finally, it is also possible to use the AppDaemon as a global area for sharing parameters across Apps. Simply add the required parameters to the AppDaemon section of your config:
+
+```ini
+[AppDaemon]
+ha_url =
+ha_key =
+...
+global_var = hello world
+```
+
+Then access it as follows:
+
+```python
+my_global_var = conf.config["AppDaemon"]["global_var"]
+```
+
+## Development Workflow
+
+Developing Apps is intended to be fairly simple but is an exercise in programming like any other kind of Python programming. As such, it is expected that apps will contain syntax errors and will generate exceptions during the development process. AppDaemon makes it very easy to iterate through the development process as it will automatically reload code that has changed and also will reload code if any of the parameters in the configuration file change as well.
+
+The recommended workflow for development is as follows:
+
+- Open a window and tail the `appdaemon.log` file
+- Open a second window and tail the `error.log` file
+- Open a third window or the editor of your choice for editing the App
+
+With this setup, you will see that every time you write the file, AppDaemon will log the fact and let you know it has reloaded the App in the `appdaemon.log` file.
+
+If there is an error in the compilation or a runtime error, this will be directed to the `error.log` file to enable you to see the error and correct it. When an error occurs, there will also be a warning message in `appdaemon.log` to tell you to check the error log.
+
+## Time Travel
+
+OK, time travel sadly isn't really possible but it can be very useful when testing Apps. For instance, imagine you have an App that turns a light on every day at sunset. It might be nice to test it without waiting for Sunset - and with AppDaemon's "Time Travel" features you can.
+
+### Choosing a Start Time
+
+Internally, AppDaemon keeps track of it's own time relative to when it was started. This make is possible to start AppDaemon with a different start time and date to the current time. For instance to test that sunset App, start AppDaemon at a time just before sunset and see if it works as expected. To do this, simply use the "-s" argument on AppDaemon's command line. e,g,:
+
+```bash
+$ appdaemon -s "2016-06-06 19:16:00"
+2016-09-06 17:16:00 INFO AppDaemon Version 1.3.2 starting
+2016-09-06 17:16:00 INFO Got initial state
+2016-09-06 17:16:00 INFO Loading Module: /export/hass/appdaemon_test/conf/test_apps/sunset.py
+...
+```
+
+Note the timestamps in the log - AppDaemon believes it is now just before sunset and will process any callbacks appropriately.
+
+### Speeding things up
+
+Some Apps need to run for periods of a day or two for you to test all aspects. This can be time consuming, but Time Travel can also help here in two ways. The first is by speeding up time. To do this, simply use the `-t` option on the command line. This specifies the amount of time a second lasts while time travelling. The default of course is 1 second, but if you change it to `0.1` for instance, AppDaemon will work 10x faster. If you set it to `0`, AppDaemon will work as fast as possible and, depending in your hardware, may be able to get through an entire day in a matter of minutes. Bear in mind however, due to the threaded nature of AppDaemon, when you are running with `-t 0` you may see actual events firing a little later than expected as the rest of the system tries to keep up with the timer. To set the tick time, start AppDaemon as follows:
+
+```bash
+$ appdaemon -t 0.1
+```
+
+AppDaemon also has an interval flag - think of this as a second multiplier. If the flag is set to 3600 for instance, each tick of the scheduler will jump the time forward by an hour. This is good for covering vast amounts of time quickly but event firing accuracy will suffer as a result. For example:
+
+```bash
+$ appdaemon -e 3600
+```
+
+### Automatically stopping
+
+AppDaemon can be set to terminate automatically at a specific time. This can be useful if you want to repeatedly rerun a test, for example to test that random values are behaving as expected. Simply specify the end time with the `-e` flag as follows:
+
+```bash
+$ appdaemon -e "2016-06-06 10:10:00"
+2016-09-06 17:16:00 INFO AppDaemon Version 1.3.2 starting
+2016-09-06 17:16:00 INFO Got initial state
+2016-09-06 17:16:00 INFO Loading Module: /export/hass/appdaemon_test/conf/test_apps/sunset.py
+...
+```
+
+The `-e` flag is most useful when used in conjuntion with the -s flag and optionally the `-t` flag. For example, to run from just before sunset, for an hour, as fast as possible:
+
+```bash
+$ appdaemon -s "2016-06-06 19:16:00" -s "2016-06-06 20:16:00" -t 0
+```
+
+
+### A Note on Times
+
+Some Apps you write may depend on checking times of events relative to the current time. If you are time travelling this will not work if you use standard python library calls to get the current time and date etc. For this reason, always use the AppDamon supplied `time()`, `date()` and `datetime()` calls, documented earlier. These calls will consult with AppDaemon's internal time rather than the actual time and give you the correct values.
\ No newline at end of file
diff --git a/source/_ecosystem/appdaemon/configuration.markdown b/source/_ecosystem/appdaemon/configuration.markdown
new file mode 100644
index 00000000000..d17504ea4ed
--- /dev/null
+++ b/source/_ecosystem/appdaemon/configuration.markdown
@@ -0,0 +1,80 @@
+---
+layout: page
+title: "Configuration"
+description: "AppDaemon Configuration"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+When you have appdaemon installed by either method, copy the `conf/appdaemon.cfg.example` file to `conf/appdaemon.cfg`, then edit the `[AppDaemon]` section to reflect your environment:
+
+```
+[AppDaemon]
+ha_url =
+ha_key =
+logfile = STDOUT
+errorfile = STDERR
+app_dir = /conf/apps
+threads = 10
+latitude =
+longitude =
+elevation =
+cert_path =
+# Apps
+[hello_world]
+module = hello
+class = HelloWorld
+```
+
+- `ha_url` is a reference to your home assistant installation and must include the correct port number and scheme (`http://` or `https://` as appropriate)
+- `ha_key` should be set to your key if you have one, otherwise it can be removed.
+- `logfile` (optional) is the path to where you want `AppDaemon` to keep its main log. When run from the command line this is not used - log messages come out on the terminal. When running as a daemon this is where the log information will go. In the example above I created a directory specifically for AppDaemon to run from, although there is no reason you can't keep it in the `appdaemon` directory of the cloned repository. If `logfile = STDOUT`, output will be sent to stdout instead of stderr when running in the foreground, if not specified, output will be sent to STDOUT.
+- `errorfile` (optional) is the name of the logfile for errors - this will usually be errors during compilation and execution of the apps. If `errorfile = STDERR` errors will be sent to stderr instead of a file, if not specified, output will be sent to STDERR.
+- `app_dir` (optional) is the directory the apps are placed in. If not specified, AppDaemon will look first in `~/.homeassistant` then `/etc/appdaemon` for a subdirectory named `apps`
+- `threads` - the number of dedicated worker threads to create for running the apps. Note, this will bear no resembelance to the number of apps you have, the threads are re-used and only active for as long as required to tun a particular callback or initialization, leave this set to 10 unless you experience thread starvation
+- `latitude`, `longitude`, `elevation`, `timezone` - should all be copied from your home assistant configuration file
+- `cert_path` (optional) - path to root CA cert directory - use only if you are using self signed certs.
+
+The `#Apps` section is the configuration for the Hello World program and should be left in place for initial testing but can be removed later if desired, as other Apps are added, App configuration is described in the [API doc](API.md).
+
+## Docker
+
+For Docker Configuration you need to take a couple of extra things into consideration.
+
+Our Docker image is designed to load your configuration and apps from a volume at `/conf` so that you can manage them in your own git repository, or place them anywhere else on the system and map them using the Docker command line.
+
+For example, if you have a local repository in `/Users/foo/ha-config` containing the following files:
+
+```bash
+$ git ls-files
+configuration.yaml
+customize.yaml
+known_devices.yaml
+appdaemon.cfg
+apps
+apps/magic.py
+```
+
+You will need to modify the `appdaemon.cfg` file to point to these apps in `/conf/apps`:
+
+```
+[AppDaemon]
+ha_url =
+ha_key =
+logfile = STDOUT
+errorfile = STDERR
+app_dir = /conf/apps
+threads = 10
+latitude =
+longitude =
+elevation =
+```
+
+You can run Docker and point the conf volume to that directory.
\ No newline at end of file
diff --git a/source/_ecosystem/appdaemon/example_apps.markdown b/source/_ecosystem/appdaemon/example_apps.markdown
new file mode 100644
index 00000000000..6f42885fec9
--- /dev/null
+++ b/source/_ecosystem/appdaemon/example_apps.markdown
@@ -0,0 +1,14 @@
+---
+layout: page
+title: "Example Apps"
+description: "AppDaemon Example Apps"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+There are a number of example apps under conf/examples, and the `conf/examples.cfg` file gives sample parameters for them.
\ No newline at end of file
diff --git a/source/_ecosystem/appdaemon/installation.markdown b/source/_ecosystem/appdaemon/installation.markdown
new file mode 100644
index 00000000000..f313035ae49
--- /dev/null
+++ b/source/_ecosystem/appdaemon/installation.markdown
@@ -0,0 +1,46 @@
+---
+layout: page
+title: "Installation"
+description: "AppDaemon Installation"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+Installation is either by pip3 or Docker.
+
+## Clone the Repository
+
+For either method you will need to clone the **AppDaemon** repository to the current local directory on your machine.
+
+``` bash
+$ git clone https://github.com/acockburn/appdaemon.git
+```
+
+Change your working directory to the repository root. Moving forward, we will be working from this directory.
+
+``` bash
+$ cd appdaemon
+```
+
+## Install using Docker
+
+To build the Docker image run the following:
+
+``` bash
+$ docker build -t appdaemon .
+```
+
+(Note the period at the end of the above command)
+
+## Install Using PIP3
+
+Before running `AppDaemon` you will need to install the package:
+
+```bash
+$ sudo pip3 install .
+```
diff --git a/source/_ecosystem/appdaemon/operation.markdown b/source/_ecosystem/appdaemon/operation.markdown
new file mode 100644
index 00000000000..e928ec4babd
--- /dev/null
+++ b/source/_ecosystem/appdaemon/operation.markdown
@@ -0,0 +1,14 @@
+---
+layout: page
+title: "Operation"
+description: "Operation"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+Since AppDaemon under the covers uses the exact same APIs as the frontend UI, you typically see it react at about the same time to a given event. Calling back to Home Assistant is also pretty fast especially if they are running on the same machine. In action, observed latency above the built in automation component is usually sub-second.
diff --git a/source/_ecosystem/appdaemon/reboot.markdown b/source/_ecosystem/appdaemon/reboot.markdown
new file mode 100644
index 00000000000..4cac399650b
--- /dev/null
+++ b/source/_ecosystem/appdaemon/reboot.markdown
@@ -0,0 +1,14 @@
+---
+layout: page
+title: "Starting at Reboot"
+description: "Starting at Reboot"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+To run `AppDaemon` at reboot, I have provided a sample init script in the `./scripts` directory. These have been tested on a Raspberry PI - your mileage may vary on other systems. There is also a sample Systemd script.
diff --git a/source/_ecosystem/appdaemon/running.markdown b/source/_ecosystem/appdaemon/running.markdown
new file mode 100755
index 00000000000..62e8718f9b8
--- /dev/null
+++ b/source/_ecosystem/appdaemon/running.markdown
@@ -0,0 +1,96 @@
+---
+layout: page
+title: "Running AppDaemon"
+description: "Running AppDaemon"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+As configured, AppDaemon comes with a single HelloWorld App that will send a greeting to the logfile to show that everything is working correctly.
+
+## Docker
+
+Assuming you have set the config up as described above for Docker, you can run it with the command:
+
+```bash
+$ docker run -d -v /conf:/conf --name appdaemon appdaemon:latest
+```
+
+In the example above you would use:
+
+```bash
+$ docker run -d -v /Users/foo/ha-config:/conf --name appdaemon appdaemon:latest
+```
+
+Where you place the `conf` and `conf/apps` directory is up to you - it can be in downloaded repostory, or anywhere else on the host, as long as you use the correct mapping in the `docker run` command.
+
+You can inspect the logs as follows:
+
+```bash
+$ docker logs appdaemon
+2016-08-22 10:08:16,575 INFO Got initial state
+2016-08-22 10:08:16,576 INFO Loading Module: /export/hass/appdaemon_test/conf/apps/hello.py
+2016-08-22 10:08:16,578 INFO Loading Object hello_world using class HelloWorld from module hello
+2016-08-22 10:08:16,580 INFO Hello from AppDaemon
+2016-08-22 10:08:16,584 INFO You are now ready to run Apps!
+```
+
+Note that for Docker, the error and regular logs are combined.
+
+## PIP3
+
+You can then run AppDaemon from the command line as follows:
+
+```bash
+$ appdaemon -c conf/appdaemon.cfg
+```
+
+If all is well, you should see something like the following:
+
+```
+$ appdaemon -c conf/appdaemon.cfg
+2016-08-22 10:08:16,575 INFO Got initial state
+2016-08-22 10:08:16,576 INFO Loading Module: /export/hass/appdaemon_test/conf/apps/hello.py
+2016-08-22 10:08:16,578 INFO Loading Object hello_world using class HelloWorld from module hello
+2016-08-22 10:08:16,580 INFO Hello from AppDaemon
+2016-08-22 10:08:16,584 INFO You are now ready to run Apps!
+```
+
+## AppDaemon arguments
+
+```
+usage: appdaemon [-h] [-c CONFIG] [-p PIDFILE] [-t TICK] [-s STARTTIME]
+ [-e ENDTIME] [-i INTERVAL]
+ [-D {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [-v] [-d]
+
+optional arguments:
+ -h, --help show this help message and exit
+ -c CONFIG, --config CONFIG
+ full path to config file
+ -p PIDFILE, --pidfile PIDFILE
+ full path to PID File
+ -t TICK, --tick TICK time in seconds that a tick in the schedular lasts
+ -s STARTTIME, --starttime STARTTIME
+ start time for scheduler
+ -e ENDTIME, --endtime ENDTIME
+ end time for scheduler
+ -i INTERVAL, --interval INTERVAL
+ multiplier for scheduler tick
+ -D {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --debug {DEBUG,INFO,WARNING,ERROR,CRITICAL}
+ debug level
+ -v, --version show program's version number and exit
+ -d, --daemon run as a background process
+```
+
+-c is the path to the configuration file. If not specified, AppDaemon will look for a file named `appdaemon.cfg` first in `~/.homeassistant` then in `/etc/appdaemon`. If the file is not specified and it is not found in either location, AppDaemon will raise an exception.
+
+-d and -p are used by the init file to start the process as a daemon and are not required if running from the command line.
+
+-D can be used to increase the debug level for internal AppDaemon operations as well as apps using the logging function.
+
+The -s, -i, -t and -s options are for the Time Travel feature and should only be used for testing. They are described in more detail in the API documentation.
diff --git a/source/_ecosystem/appdaemon/tutorial.markdown b/source/_ecosystem/appdaemon/tutorial.markdown
new file mode 100755
index 00000000000..7c23e38a95d
--- /dev/null
+++ b/source/_ecosystem/appdaemon/tutorial.markdown
@@ -0,0 +1,130 @@
+---
+layout: page
+title: "AppDaemon Tutorial"
+description: "AppDaemon Tutorial"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+## Another Take on Automation
+
+If you haven't yet read Paulus' excellent Blog entry on [Perfect Home Automation](https://home-assistant.io/blog/2016/01/19/perfect-home-automation/) I would encourage you to take a look. As a veteran of several Home Automation systems with varying degrees success, it was this article more than anything else that convinced me that Home Assistant had the right philosophy behind it and was on the right track. One of the most important points made is that being able to control your lights from your phone, 9 times out of 10 is harder than using a lightswitch - where Home Automation really comes into its own is when you start removing the need to use a phone or the switch - the "Automation" in Home Automation. A surprisingly large number of systems out there miss this essential point and have limited abilities to automate anything which is why a robust and open system such as Home Assistant is such an important part of the equation in bring this all together in the vast and chaotic ecosystem that is the "Internet of Things".
+
+So given the importance of Automation, what should Automation allow us to do? I am a pragmatist at heart so I judge individual systems by the ease of accomplishing a few basic but representative tasks:
+
+- Can the system respond to presence or absence of people?
+- Can I turn a light on at Sunset +/- a certain amount of time?
+- Can I arrive home in light or dark and have the lights figure out if they should be on or off?
+- As I build my system out, can I get the individual pieces to co-operate and use and re-use (potentially complex) logic to make sure everything works smoothly?
+- Is it open and expandable?
+- Does it run locally without any reliance on the cloud?
+
+In my opinion, Home Assistant accomplishes the majority of these very well with a combination of Automations, Scripts and Templates, and it's Restful API.
+
+So why `AppDaemon`? AppDaemon is not meant to replace Home Assistant Automations and Scripts, rather complement them. For a lot of things, automations work well and can be very succinct. However, there is a class of more complex automations for which they become harder to use, and appdeamon then comes into its own. It brings quite a few things to the table:
+
+- New paradigm - some problems require a procedural and/or iterative approach, and `AppDaemon` Apps are a much more natural fit for this. Recent enhancements to Home Assistant scripts and templates have made huge strides, but for the most complex scenarios, Apps can do things that Automations can't
+- Ease of use - AppDaemon's API is full of helper functions that make programming as easy and natural as possible. The functions and their operation are as "Pythonic" as possible, experienced Python programmers should feel right at home.
+- Reuse - write a piece of code once and instantiate it as an app as many times as you need with different parameters e.g. a motion light program that you can use in 5 different places around your home. The code stays the same, you just dynamically add new instances of it in the config file
+- Dynamic - AppDaemon has been designed from the start to enable the user to make changes without requiring a restart of Home Assistant, thanks to it's loose coupling. However, it is better than that - the user can make changes to code and AppDaemon will automatically reload the code, figure out which Apps were using it and restart them to use the new code with out the need to restart `AppDaemon` itself. It is also possible to change parameters for an individual or multiple apps and have them picked up dynamically, and for a final trick, removing or adding apps is also picked up dynamically. Testing cycles become a lot more efficient as a result.
+- Complex logic - Python's If/Else constructs are clearer and easier to code for arbitrarily complex nested logic
+- Durable variables and state - variables can be kept between events to keep track of things like the number of times a motion sensor has been activated, or how long it has been since a door opened
+- All the power of Python - use any of Python's libraries, create your own modules, share variables, refactor and re-use code, create a single app to do everything, or multiple apps for individual tasks - nothing is off limits!
+
+It is in fact a testament to Home Assistant's open nature that a component like `AppDaemon` can be integrated so neatly and closely that it acts in all ways like an extension of the system, not a second class citizen. Part of the strength of Home Assistant's underlying design is that it makes no assumptions whatever about what it is controlling or reacting to, or reporting state on. This is made achievable in part by the great flexibility of Python as a programming environment for Home Assistant, and carrying that forward has enabled me to use the same philosophy for `AppDaemon` - it took surprisingly little code to be able to respond to basic events and call services in a completely open ended manner - the bulk of the work after that was adding additonal functions to make things that were already possible easier.
+
+## How it Works
+
+The best way to show what AppDaemon does is through a few simple examples.
+
+### Sunrise/Sunset Lighting
+
+Lets start with a simple App to turn a light on every night at sunset and off every morning at sunrise. Every App when first started will have its `initialize()` function called which gives it a chance to register a callback for AppDaemons's scheduler for a specific time. In this case we are using `run_at_sunrise()` and `run_at_sunset()` to register 2 separate callbacks. The argument `0` is the number of seconds offset from sunrise or sunset and can be negative or positive. For complex intervals it can be convenient to use Python's `datetime.timedelta` class for calculations. When sunrise or sunset occurs, the appropriate callback function, `sunrise_cb()` or `sunset_cb()` is called which then makes a call to Home Assistant to turn the porch light on or off by activating a scene. The variables `args["on_scene"]` and `args["off_scene"]` are passed through from the configuration of this particular App, and the same code could be reused to activate completely different scenes in a different version of the App.
+
+```python
+import homeassistant.appapi as appapi
+
+class OutsideLights(appapi.AppDaemon):
+
+ def initialize(self):
+ self.run_at_sunrise(self.sunrise_cb, 0)
+ self.run_at_sunset(self.sunset_cb, 0)
+
+ def sunrise_cb(self, kwargs):
+ self.turn_on(self.args["off_scene"])
+
+ def sunset_cb(self, kwargs):
+ self.turn_on(self.args["on_scene"])
+
+```
+
+This is also fairly easy to achieve with Home Assistant automations, but we are just getting started.
+
+### Motion Light
+
+Our next example is to turn on a light when motion is detected and it is dark, and turn it off after a period of time. This time, the `initialize()` function registers a callback on a state change (of the motion sensor) rather than a specific time. We tell AppDaemon that we are only interested in state changesd where the motion detector comes on by adding an additional parameter to the callback registration - `new = "on"`. When the motion is detected, the callack function `motion()` is called, and we check whether or not the sun has set using a built-in convenience function: `sun_down()`. Next, we turn the light on with `turn_on()`, then set a timer using `run_in()` to turn the light off after 60 seconds, which is another call to the scheduler to execute in a set time from now, which results in `AppDaemon` calling `light_off()` 60 seconds later using the `turn_off()` call to actually turn the light off. This is still pretty simple in code terms:
+
+```python
+import homeassistant.appapi as appapi
+
+class FlashyMotionLights(appapi.AppDaemon):
+
+ def initialize(self):
+ self.listen_state(self.motion, "binary_sensor.drive", new = "on")
+
+ def motion(self, entity, attribute, old, new, kwargs):
+ if self.sun_down():
+ self.turn_on("light.drive")
+ self.run_in(self.light_off, 60)
+
+ def light_off(self, kwargs):
+ self.turn_off("light.drive")
+```
+
+This is starting to get a little more complex in Home Assistant automations requiring an Automation rule and two separate scripts.
+
+Now lets extend this with a somewhat artificial example to show something that is simple in AppDaemon but very difficult if not impossible using automations. Lets warn someone inside the house that there has been motion outside by flashing a lamp on and off 10 times. We are reacting to the motion as before by turning on the light and setting a timer to turn it off again, but in addition, we set a 1 second timer to run `flash_warning()` which when called, toggles the inside light and sets another timer to call itself a second later. To avoid re-triggering forever, it keeps a count of how many times it has been activated and bales out after 10 iterations.
+
+```python
+import homeassistant.appapi as appapi
+
+class MotionLights(appapi.AppDaemon):
+
+ def initialize(self):
+ self.listen_state(self.motion, "binary_sensor.drive", new = "on")
+
+ def motion(self, entity, attribute, old, new, kwargs):
+ if self.self.sun_down():
+ self.turn_on("light.drive")
+ self.run_in(self.light_off, 60)
+ self.flashcount = 0
+ self.run_in(self.flash_warning, 1)
+
+ def light_off(self, kwargs):
+ self.turn_off("light.drive")
+
+ def flash_warning(self, kwargs):
+ self.toggle("light.living_room")
+ self.flashcount += 1
+ if self.flashcount < 10:
+ self.run_in(self.flash_warning, 1)
+```
+
+Of course if I wanted to make this App or its predecessor reusable I would have provide parameters for the sensor, the light to activate on motion, the warning light and even the number of flashes and delay between flashes.
+
+In addition, Apps can write to `AppDaemon`'s logfiles, and there is a system of constraints that allows yout to control when and under what circumstances Apps and callbacks are active to keep the logic clean and simple.
+
+I have spent the last few weeks moving all of my (fairly complex) automations over to `APPDaemon` and so far it is working very reliably.
+
+Some people will maybe look at all of this and say "what use is this, I can already do all of this", and that is fine, as I said this is an alternative not a replacement, but I am hopeful that for some users this will seem a more natural, powerful and nimble way of building potentially very complex automations.
+
+If this has whet your appetite, feel free to give it a try.
+
+Happy Automating!
+
+
diff --git a/source/_ecosystem/appdaemon/updating.markdown b/source/_ecosystem/appdaemon/updating.markdown
new file mode 100644
index 00000000000..28229fb6ece
--- /dev/null
+++ b/source/_ecosystem/appdaemon/updating.markdown
@@ -0,0 +1,27 @@
+---
+layout: page
+title: "Updating AppDaemon"
+description: "Updating AppDaemon"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+To update AppDaemon after I have released new code, just run the following command to update your copy:
+
+```bash
+$ git pull origin
+```
+
+If you are using pip3 for the install do this:
+
+```bash
+$ sudo pip3 uninstall appdaemon
+$ sudo pip3 install .
+```
+
+If you are using docker, rerun the steps to create a new docker image.
diff --git a/source/_ecosystem/appdaemon/windows.markdown b/source/_ecosystem/appdaemon/windows.markdown
new file mode 100755
index 00000000000..9b443f77534
--- /dev/null
+++ b/source/_ecosystem/appdaemon/windows.markdown
@@ -0,0 +1,23 @@
+---
+layout: page
+title: "Windows Support"
+description: "Windows Support"
+release_date: 2016-11-27 08:00:00 -0500
+sidebar: true
+comments: false
+sharing: true
+footer: true
+regenerate: true
+hide_github_edit: true
+---
+
+AppDaemon runs under windows and has been tested with the official 3.5.2 release of Python. There are a couple of caveats however:
+
+- The `-d` or `--daemonize` option is not supported owing to limitations in the Windows implementation of Python.
+- Some internal diagnostics are disabled. This is not user visible but may hamper troubleshooting of internal issues if any crop up
+
+AppDaemon can be installed exactly as per the instructions for every other version using pip3.
+
+## Windows Under the Linux Subsystem
+
+Windows 10 now supports a full Linux bash environment that is capable of running Python. This is essentially an Ubuntu distribution and works extremely well. It is possible to run AppDaemon in exactly the same way as for Linux distributions, and none of the above Windows Caveats apply to this version. This is the reccomended way to run AppDaemon in a Windows 10 and later environment.
\ No newline at end of file
diff --git a/source/_includes/asides/ecosystem_appdaemon_navigation.html b/source/_includes/asides/ecosystem_appdaemon_navigation.html
new file mode 100755
index 00000000000..8333511804e
--- /dev/null
+++ b/source/_includes/asides/ecosystem_appdaemon_navigation.html
@@ -0,0 +1,19 @@
+
diff --git a/source/_includes/asides/ecosystem_navigation.html b/source/_includes/asides/ecosystem_navigation.html
index a11b2324306..ed30ae7c1cb 100755
--- a/source/_includes/asides/ecosystem_navigation.html
+++ b/source/_includes/asides/ecosystem_navigation.html
@@ -3,6 +3,8 @@
{% include asides/ecosystem_ios_navigation.html | compact_newlines %}
{% elsif url_parts[2] == "hadashboard" %}
{% include asides/ecosystem_hadashboard_navigation.html | compact_newlines %}
+{% elsif url_parts[2] == "appdaemon" %}
+ {% include asides/ecosystem_appdaemon_navigation.html | compact_newlines %}
{% comment %}
{% elsif url_parts[2] == "cookbook" %}
{% include asides/cookbook_navigation.html | compact_newlines %}
From 418fde81004191bb60ad7beb5458ac74493bca00 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 09:50:18 +0100
Subject: [PATCH 007/123] Fix configuration variables and add full
configuration sample
---
source/_components/cover.rpi_gpio.markdown | 28 +++++++++++++++-------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/source/_components/cover.rpi_gpio.markdown b/source/_components/cover.rpi_gpio.markdown
index 3b2205d724b..e51d150d2d5 100644
--- a/source/_components/cover.rpi_gpio.markdown
+++ b/source/_components/cover.rpi_gpio.markdown
@@ -23,6 +23,26 @@ Although you do not need Andrews Hilliday's software controller when you run Hom
To enable Raspberry Pi Covers in your installation, add the following to your `configuration.yaml` file:
+```yaml
+# Example configuration.yaml entry
+cover:
+ platform: rpi_gpio
+ covers:
+ - relay_pin: 10
+ state_pin: 11
+```
+
+Configuration variables:
+
+- **relay_time** (*Optional*): The time that the relay will be on for in seconds. Default is .2 seconds.
+- **state_pull_mode** (*Optional*): The direction the State pin is pulling. It can be UP or DOWN. Default is UP.
+- **covers** array (*Required*): List of your doors.
+ - **relay_pin** (*Required*): The pin of your Raspberry Pi where the relay is connected.
+ - **state_pin** (*Required*): The pin of your Raspberry Pi to retrieve the state.
+ - **name** (*Optional*): Name to use in the frontend.
+
+Full example:
+
```yaml
# Example configuration.yaml entry
cover:
@@ -37,12 +57,4 @@ cover:
name: 'Right door'
```
-Configuration variables:
-
-- **covers** array (*Required*): List of your doors.
- - **name** (*Optional*): Name to use in the Frontend.
- - **relay_pin** (*Required*): The pin of your Raspberry Pi where the relay is connected.
- - **state_pin** (*Required*): The pin of your Raspberry Pi to retrieve the state.
- - **state_pull_mode** (*Optional*): The direction the State pin is pulling. It can be UP or DOWN. Default is UP.
- - **relay_time** (*Optional*): The time that the relay will be on for in seconds. Default is .2 seconds.
From 13ef9d8a1ac0590227d1ca923a49c82dd5706b6d Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 09:51:13 +0100
Subject: [PATCH 008/123] Change indent
---
source/_components/cover.rpi_gpio.markdown | 27 +++++++++++-----------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/source/_components/cover.rpi_gpio.markdown b/source/_components/cover.rpi_gpio.markdown
index e51d150d2d5..35588b23df9 100644
--- a/source/_components/cover.rpi_gpio.markdown
+++ b/source/_components/cover.rpi_gpio.markdown
@@ -26,10 +26,10 @@ To enable Raspberry Pi Covers in your installation, add the following to your `c
```yaml
# Example configuration.yaml entry
cover:
- platform: rpi_gpio
- covers:
- - relay_pin: 10
- state_pin: 11
+ - platform: rpi_gpio
+ covers:
+ - relay_pin: 10
+ state_pin: 11
```
Configuration variables:
@@ -46,15 +46,14 @@ Full example:
```yaml
# Example configuration.yaml entry
cover:
- platform: rpi_gpio
- relay_time: 0.2
- state_pull_mode: 'UP'
- covers:
- - relay_pin: 10
- state_pin: 11
- - relay_pin: 12
- state_pin: 13
- name: 'Right door'
+ - platform: rpi_gpio
+ relay_time: 0.2
+ state_pull_mode: 'UP'
+ covers:
+ - relay_pin: 10
+ state_pin: 11
+ - relay_pin: 12
+ state_pin: 13
+ name: 'Right door'
```
-
From 3be46830ad2981b988edc560b70c0bfde1356657 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 10:40:27 +0100
Subject: [PATCH 009/123] Make titles linkable
---
source/_ecosystem/appdaemon/api.markdown | 721 +++++++++---------
.../appdaemon/configuration.markdown | 4 +-
.../appdaemon/installation.markdown | 8 +-
.../_ecosystem/appdaemon/operation.markdown | 2 +-
source/_ecosystem/appdaemon/running.markdown | 12 +-
source/_ecosystem/appdaemon/tutorial.markdown | 7 +-
source/_ecosystem/appdaemon/windows.markdown | 4 +-
7 files changed, 378 insertions(+), 380 deletions(-)
diff --git a/source/_ecosystem/appdaemon/api.markdown b/source/_ecosystem/appdaemon/api.markdown
index 63299c41948..346c0546881 100755
--- a/source/_ecosystem/appdaemon/api.markdown
+++ b/source/_ecosystem/appdaemon/api.markdown
@@ -11,7 +11,7 @@ regenerate: true
hide_github_edit: true
---
-## Anatomy of an App
+## {% linkable_title Anatomy of an App %}
Automations in AppDaemon are performed by creating a piece of code (essentially a Python Class) and then instantiating it as an Object one or more times by configuring it as an App in the configuration file. The App is given a chance to register itself for whatever events it wants to subscribe to, and AppDaemon will then make calls back into the Object's code when those events occur, allowing the App to respond to the event with some kind of action.
@@ -72,11 +72,11 @@ class NightLight(appapi.AppDaemon):
To summarize - an App's lifecycle consists of being initialized, which allows it to set one or more state and/or schedule callbacks. When those callbacks are activated, the App will typically use one of the Service Calling calls to effect some change to the devices of the system and then wait for the next relevant state change. That's all there is to it!
-## About the API
+## {% linkable_title About the API
The implementation of the API is located in the AppDaemon class that Apps are derived from. The code for the functions is therefore available to the App simply by invoking the name of the function from the object namespace using the `self` keyword, as in the above examples. `self.turn_on()` for example is just a method defined in the parent class and made available to the child. This design decision was made to simplify some of the implementation and hide passing of unnecessary variables during the API invocation.
-## Configuration of Apps
+## {% linkable_title Configuration of Apps %}
Apps are configured by specifying new sections in the configuration file. `[AppDaemon]` is a reserved section, for configuration of AppDaemon itself. The name of the section is the name the App is referred to within the system in log files etc. and must be unique.
To configure a new App you need a minimum of two directives:
@@ -96,13 +96,13 @@ When AppDaemon sees the following configuration it will expect to find a class c
When starting the system for the first time or when reloading an App or Module, the system will log the fact in it's main log. It is often the case that there is a problem with the class, maybe a syntax error or some other problem. If that is the case, details will be output to the error log allowing the user to remedy the problem and reload.
-## Steps to writing an App
+## {% linkable_title Steps to writing an App %}
1. Create the code in a new or shared module by deriving a class from AppDaemon, add required callbacks and code
2. Add the App to the configuration file
3. There is no number 3
-## Reloading Modules and Classes
+## {% linkable_title Reloading Modules and Classes %}
Reloading of modules is automatic. When the system spots a change in a module, it will automatically reload and recompile the module. It will also figure out which Apps were using that Module and restart them, causing all of their existing callbacks to be cleared, and their `initialize()` function to be called.
@@ -110,7 +110,7 @@ The same is true if changes are made to an App's configuration - changing the cl
The suggested order for creating a new App is to add the module code first and work until it compiles cleanly, and only then add an entry in the configuration file to actually run it. A good workflow is to continuously monitor the error file (using `tail -f` on Linux for instance) to ensure that errors are seen and can be remedied.
-## Passing Arguments to Apps
+## {% linkable_title Passing Arguments to Apps %}
There wouldn't be much point in being able to run multiple versions of an App if there wasn't some way to instruct them to do something different. For this reason it is possible to pass any required arguments to an App, which are then made available to the object at runtime. The arguments themselves can be called anything (apart from `module` or `class`) and are simply added into the section after the 2 mandatory directives like so:
@@ -149,7 +149,7 @@ sensor = binary_sensor.garage
light = light.garage
```
-## Callback Constraints
+## {% linkable_title Callback Constraints %}
Callback constraints are a feature of AppDaemon that removes the need for repetition of some common coding checks. Many Apps will wish to process their callbacks only when certain conditions are met, e.g. someone is home, and it's after sunset. These kinds of conditions crop up a lot, and use of callback constraints can significantly simplify the logic required within callbacks.
@@ -175,7 +175,7 @@ An App can have as many or as few as are required. When more than one constraint
They are described individually below.
-### input_boolean
+### {% linkable_title input_boolean %}
By default, the input_boolean constraint prevents callbacks unless the specified input_boolean is set to "on". This is useful to allow certain Apps to be turned on and off from the user interface. For example:
```ini
@@ -194,7 +194,7 @@ class = SomeClass
constrain_input_boolean = input_boolean.enable_motion_detection,off
```
-### input_select
+### {% linkable_title input_select %}
The input_select constraint prevents callbacks unless the specified input_select is set to one or more of the nominated (comma separated) values. This is useful to allow certain Apps to be turned on and off according to some flag, e.g. a house mode flag.
```ini
@@ -204,7 +204,7 @@ constrain_input_select = input_select.house_mode,Day
constrain_input_select = input_select.house_mode,Day,Evening,Night
```
-### presence
+### {% linkable_title presence %}
The presence constraint will constrain based on presence of device trackers. It takes 3 possible values:
- `noone` - only allow callback execution when no one is home
- `anyone` - only allow callback execution when one or more person is home
@@ -218,7 +218,7 @@ constrain_presence = someone
constrain_presence = noone
```
-### time
+### {% linkable_title time %}
The time constraint consists of 2 variables, `constrain_start_time` and `constrain_end_time`. Callbacks will only be executed if the current time is between the start and end times.
- If both are absent no time constraint will exist
- If only start is present, end will default to 1 second before midnight
@@ -242,7 +242,7 @@ constrain_start_time = sunset - 00:45:00
constrain_end_time = sunrise + 00:45:00
```
-### days
+### {% linkable_title days %}
The day constraint consists of as list of days for which the callbacks will fire, e.g.
```ini
@@ -251,7 +251,7 @@ constrain_days = mon,tue,wed
Callback constraints can also be applied to individual callbacks within Apps, see later for more details.
-## A Note on Threading
+## {% linkable_title A Note on Threading %}
AppDaemon is multithreaded. This means that any time code within an App is executed, it is executed by one of many threads. This is generally not a particularly important consideration for this application; in general, the execution time of callbacks is expected to be far quicker than the frequency of events causing them. However, it should be noted for completeness, that it is certainly possible for different pieces of code within the App to be executed concurrently, so some care may be necessary if different callback for instance inspect and change shared variables. This is a fairly standard caveat with concurrent programming, and if you know enough to want to do this, then you should know enough to put appropriate safeguards in place. For the average user however this shouldn't be an issue. If there are sufficient use cases to warrant it, I will consider adding locking to the function invocations to make the entire infrastructure threadsafe, but I am not convinced that it is necessary.
@@ -259,9 +259,9 @@ An additional caveat of a threaded worker pool environment is that it is the exp
Given the above, NEVER use Python's `time.sleep()` if you want to perform an operation some time in the future, as this will tie up a thread for the period of the sleep. Instead use the scheduler's `run_in()` function which will allow you to delay without blocking any threads.
-## State Operations
+## {% linkable_title State Operations %}
-### A note on Home Assistant State
+### {% linkable_title A note on Home Assistant State %}
State within Home Assistant is stored as a collection of dictionaries, one for each entity. Each entity's dictionary will have some common fields and a number of entity type specific fields The state for an entity will always have the attributes:
@@ -275,9 +275,9 @@ Also bear in mind that some attributes such as brightness for a light, will not
In most cases, the attribute `state` has the most important value in it, e.g. for a light or switch this will be `on` or `off`, for a sensor it will be the value of that sensor. Many of the AppDaemon API calls and callbacks will implicitly return the value of state unless told to do otherwise.
-### get_state()
+### {% linkable_title get_state() %}
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
get_state(entity = None, attribute = None)
@@ -285,27 +285,27 @@ get_state(entity = None, attribute = None)
`get_state()` is used to query the state of any component within Home Assistant. State updates are continuously tracked so this call runs locally and does not require AppDaemon to call back to Home Assistant and as such is very efficient.
-#### Returns
+#### {% linkable_title Returns %}
`get_state()` returns a `dictionary` or single value, the structure of which varies according to the parameters used.
-#### Parameters
+#### {% linkable_title Parameters
All parameters are optional, and if `get_state()` is called with no parameters it will return the entire state of Home Assistant at that given time. This will consist of a dictionary with a key for each entity. Under that key will be the standard entity state information.
-##### entity
+##### {% linkable_title entity %}
This is the name of an entity or device type. If just a device type is provided, e.g. `light` or `binary_sensor`, `get_state()` will return a dictionary of all devices of that type, indexed by the entity_id, containing all the state for each entity.
If a fully qualified `entity_id` is provided, `get_state()` will return the state attribute for that entity, e.g. `on` or `off` for a light.
-##### attribute
+##### {% linkable_title attribute %}
Name of an attribute within the entity state object. If this parameter is specified in addition to a fully qualified `entity_id`, a single value representing the attribute will be returned, or `None` if it is not present.
The value `all` for attribute has special significance and will return the entire state dictionary for the specified entity rather than an individual attribute value.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Return state for the entire system
@@ -324,7 +324,7 @@ state = self.get_state("light.office_1", "brightness")
state = self.get_state("light.office_1", "all")
```
-### set_state()
+### {% linkable_title set_state() %}
`set_state()` will make a call back to Home Assistant and make changes to the internal state of Home Assistant. This is not something that you would usually want to do and the applications are limited however the call is included for completeness. Note that for instance, setting the state of a light to `on` won't actually switch the device on, it will merely change the state of the device in Home Assistant so that it no longer reflects reality. In most cases, the state will be corrected the next time Home Assistant polls the device or someone causes a state change manually. To effect actual changes of devices use one of the service call functions.
@@ -332,33 +332,33 @@ One possible use case for `set_state()` is for testing. If for instance you are
At the time of writing, it appears that no checking is done as to whether or not the entity exists, so it is possible to add entirely new entries to Home Assistant's state with this call.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
set_state(entity_id, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
`set_state()` returns a dictionary representing the state of the device after the call has completed.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Entity id for which the state is to be set, e.g. `light.office_1`.
-##### values
+##### {% linkable_title values %}
A list of keyword values to be changed or added to the entities state. e.g. `state = "off"`. Note that any optional attributes such as colors for bulbs etc, need to reside in a dictionary called `attributes`; see the example.
-#### Examples
+#### {% linkable_title Examples %}
```python
status = self.set_state("light.office_1", state = "on", attributes = {"color_name": "red"})
```
-### About Callbacks
+### {% linkable_title About Callbacks %}
A large proportion of home automation revolves around waiting for something to happen and then reacting to it; a light level drops, the sun rises, a door opens etc. Home Assistant keeps track of every state change that occurs within the system and streams that information to AppDaemon almost immediately.
@@ -372,7 +372,7 @@ There are 3 types of callbacks within AppDaemon:
All callbacks allow the user to specify additional parameters to be handed to the callback via the standard Python `**kwargs` mechanism for greater flexibility.
-### About Registering Callbacks
+### {% linkable_title About Registering Callbacks %}
Each of the various types of callback have their own function or functions for registering the callback:
@@ -382,7 +382,7 @@ Each of the various types of callback have their own function or functions for r
Each type of callback shares a number of common mechanisms that increase flexibility.
-#### Callback Level Constraints
+#### {% linkable_title Callback Level Constraints %}
When registering a callback, you can add constraints identical to the Application level constraints described earlier. The difference is that a constraint applied to an individual callback only affects that callback and no other. The constraints are applied by adding Python keyword-value style arguments after the positional arguments. The parameters themselves are named identically to the previously described constraints and have identical functionality. For instance, adding:
@@ -394,7 +394,7 @@ For example:
`self.listen_state(self.motion, "binary_sensor.drive", constrain_presence="everyone")`
-#### User Arguments
+#### {% linkable_title User Arguments %}
Any callback has the ability to allow the App creator to pass through arbitrary keyword arguments that will be presented to the callback when it is run. The arguments are added after the positional parameters just like the constraints. The only restriction is that they cannot be the same as any constraint name for obvious reasons. For example, to pass the parameter `arg1 = "home assistant"` through to a callback you would register a callback as follows:
@@ -407,11 +407,11 @@ def motion(self, entity, attribute, old, new, **kwargs):
self.log("Arg1 is {}".format(kwargs["arg1"]))
```
-### State Callbacks
+### {% linkable_title State Callbacks %}
AppDaemons's state callbacks allow an App to listen to a wide variety of events, from every state change in the system, right down to a change of a single attribute of a particular entity. Setting up a callback is done using a single API call `listen_state()` which takes various arguments to allow it to do all of the above. Apps can register as many or as few callbacks as they want.
-### About State Callback Functions
+### {% linkable_title About State Callback Functions %}
When calling back into the App, the App must provide a class function with a known signature for AppDaemon to call. The callback will provide various information to the function to enable the function to respond appropriately. For state callbacks, a class defined callback function should look like this:
@@ -424,77 +424,77 @@ You can call the function whatever you like - you will reference it in the `list
The parameters have the following meanings:
-#### self
+#### {% linkable_title self %}
A standard Python object reference.
-#### entity
+#### {% linkable_title entity %}
Name of the entity the callback was requested for or `None`.
-#### attribute
+#### {% linkable_title attribute %}
Name of the attribute the callback was requested for or `None`.
-#### old
+#### {% linkable_title old %}
The value of the state before the state change.
-#### new
+#### {% linkable_title new %}
The value of the state after the state change.
`old` and `new` will have varying types depending on the type of callback.
-#### \*\*kwargs
+#### {% linkable_title \*\*kwargs %}
A dictionary containing any constraints and/or additional user specific keyword arguments supplied to the `listen_state()` call.
-### listen_state()
+### l{% linkable_title isten_state()
`listen_state()` allows the user to register a callback for a wide variety of state changes.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
handle = listen_state(callback, entity = None, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A unique identifier that can be used to cancel the callback if required. Since variables created within object methods are local to the function they are created in, and in all likelihood the cancellation will be invoked later in a different function, it is recommended that handles are stored in the object namespace, e.g. `self.handle`.
-#### Parameters
+#### {% linkable_title Parameters %}
All parameters except `callback` are optional, and if `listen_state()` is called with no additional parameters it will subscribe to any state change within Home Assistant.
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard State Callback format documented above.
-##### entity
+##### {% linkable_title entity %}
This is the name of an entity or device type. If just a device type is provided, e.g. `light` or `binary_sensor`, `listen_state()` will subscribe to state changes of all devices of that type. If a fully qualified `entity_id` is provided, `listen_state()` will listen for state changes for just that entity.
When called, AppDaemon will supply the callback function, in old and new, with the state attribute for that entity, e.g. `on` or `off` for a light.
-##### attribute (optional)
+##### {% linkable_title attribute (optional) %}
Name of an attribute within the entity state object. If this parameter is specified in addition to a fully qualified `entity_id`, `listen_state()` will subscribe to changes for just that attribute within that specific entity. The new and old parameters in the callback function will be provided with a single value representing the attribute.
The value `all` for attribute has special significance and will listen for any state change within the specified entity, and supply the callback functions with the entire state dictionary for the specified entity rather than an individual attribute value.
-##### new = (optional)
+##### {% linkable_title new = (optional) %}
If `new` is supplied as a parameter, callbacks will only be made if the state of the selected attribute (usually `state`) in the new state match the value of `new`.
-##### old = (optional)
+##### {% linkable_title old = (optional) %}
If `old` is supplied as a parameter, callbacks will only be made if the state of the selected attribute (usually `state`) in the old state match the value of `old`.
Note: `old` and `new` can be used singly or together.
-##### duration = (optional)
+##### {% linkable_title duration = (optional) %}
If duration is supplied as a parameter, the callback will not fire unless the state listened for is maintained for that number of seconds. This makes the most sense if a specific attribute is specified (or the default os `state` is used), an in conjunction with the `old` or `new` parameters, or both. When the callback is called, it is supplied with the values of `entity`, `attr`, `old` and `new` that were current at the time the actual event occured, since the assumption is that none of them have changed in the intervening period.
@@ -505,11 +505,11 @@ If duration is supplied as a parameter, the callback will not fire unless the st
(Scheduler callbacks are documented in detail laer in this document)
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Zero or more keyword arguments that will be supplied to the callback when it is called.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Listen for any state change and return the state attribute
@@ -538,64 +538,63 @@ self.handle = self.listen_state(self.my_callback, "light.office_1", new = "on",
```
-
-### cancel_listen_state()
+### {% linkable_title cancel_listen_state() %}
Cancel a `listen_state()` callback. This will mean that the App will no longer be notified for the specific state change that has been cancelled. Other state changes will continue to be monitored.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
cancel_listen_state(handle)
```
-#### Returns
+#### {% linkable_title Returns %}
Nothing
-#### Parameters
+#### {% linkable_title Parameters %}
-##### handle
+##### {% linkable_title handle %}
The handle returned when the `listen_state()` call was made.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.cancel_listen_state(self.office_light_handle)
```
-### info_listen_state()
+### {% linkable_title info_listen_state() %}
Get information on state a callback from it's handle.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
entity, attribute, kwargs = self.info_listen_state(self.handle)
```
-#### Returns
+#### {% linkable_title Returns %}
entity, attribute, kwargs - the values supplied when the callback was initially created.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### handle
+##### {% linkable_title handle %}
The handle returned when the `listen_state()` call was made.
-#### Examples
+#### {% linkable_title Examples %}
```python
entity, attribute, kwargs = self.info_listen_state(self.handle)
```
-## Scheduler
+## {% linkable_title Scheduler %}
AppDaemon contains a powerful scheduler that is able to run with 1 second resolution to fire off specific events at set times, or after set delays, or even relative to sunrise and sunset. In general, events should be fired less than a second after specified but under certain circumstances there may be short additional delays.
-### About Schedule Callbacks
+### {% linkable_title About Schedule Callbacks %}
As with State Change callbacks, Scheduler Callbacks expect to call into functions with a known and specific signature and a class defined Scheduler callback function should look like this:
@@ -608,80 +607,80 @@ You can call the function whatever you like; you will reference it in the Schedu
The parameters have the following meanings:
-#### self
+#### {% linkable_title self %}
A standard Python object reference
-#### \*\*kwargs
+#### {% linkable_title \*\*kwargs %}
A dictionary containing Zero or more keyword arguments to be supplied to the callback.
-### Creation of Scheduler Callbacks
+### {% linkable_title Creation of Scheduler Callbacks %}
Scheduler callbacks are created through use of a number of convenience functions which can be used to suit the situation.
-#### run_in()
+#### {% linkable_title run_in() %}
Run the callback in a defined number of seconds. This is used to add a delay, for instance a 60 second delay before a light is turned off after it has been triggered by a motion detector. This callback should always be used instead of `time.sleep()` as discussed previously.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_in(callback, delay, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### vcallback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### delay
+##### {% linkable_title delay %}
Delay, in seconds before the callback is invoked.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.handle = self.run_in(self.run_in_c)
self.handle = self.run_in(self.run_in_c, title = "run_in5")
```
-#### run_once()
+#### {% linkable_title run_once() %}
Run the callback once, at the specified time of day. If the time of day is in the past, the callback will occur on the next day.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_once(callback, time, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### time
+##### {% linkable_title time %}
A Python `time` object that specifies when the callback will occur. If the time specified is in the past, the callback will occur the next day at the specified time.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run at 4pm today, or 4pm tomorrow if it is already after 4pm
@@ -691,35 +690,35 @@ runtime = datetime.time(16, 0, 0)
handle = self.run_once(self.run_once_c, runtime)
```
-#### run_at()
+#### {% linkable_title run_at() %}
Run the callback once, at the specified date and time.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_at(callback, datetime, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer. `run_at()` will raise an exception if the specified time is in the past.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### datetime
+##### {% linkable_title datetime %}
A Python `datetime` object that specifies when the callback will occur.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run at 4pm today
@@ -730,35 +729,35 @@ today = datetime.date.today()
event = datetime.datetime.combine(today, runtime)
handle = self.run_once(self.run_once_c, event)
```
-#### run_daily()
+#### {% linkable_title run_daily() %}
Execute a callback at the same time every day. If the time has already passed, the function will not be invoked until the following day at the specified time.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_daily(callback, time, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### time
+##### {% linkable_title time %}
A Python `time` object that specifies when the callback will occur. If the time specified is in the past, the callback will occur the next day at the specified time.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run daily at 7pm
@@ -768,35 +767,35 @@ time = datetime.time(19, 0, 0)
self.run_daily(self.run_daily_c, runtime)
```
-#### run_hourly()
+#### {% linkable_title run_hourly() %}
Execute a callback at the same time every hour. If the time has already passed, the function will not be invoked until the following hour at the specified time.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_hourly(callback, time = None, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### time
+##### {% linkable_title time %}
A Python `time` object that specifies when the callback will occur, the hour component of the time object is ignored. If the time specified is in the past, the callback will occur the next hour at the specified time. If time is not supplied, the callback will start an hour from the time that `run_hourly()` was executed.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run every hour, on the hour
@@ -805,35 +804,35 @@ import datetime
time = datetime.time(0, 0, 0)
self.run_daily(self.run_daily_c, runtime)
```
-#### run_minutely()
+#### {% linkable_title run_minutely() %}
Execute a callback at the same time every minute. If the time has already passed, the function will not be invoked until the following minute at the specified time.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_minutely(callback, time = None, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### time
+##### {% linkable_title time %}
A Python `time` object that specifies when the callback will occur, the hour and minute components of the time object are ignored. If the time specified is in the past, the callback will occur the next hour at the specified time. If time is not supplied, the callback will start a minute from the time that `run_minutely()` was executed.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run Every Minute on the minute
@@ -843,39 +842,39 @@ time = datetime.time(0, 0, 0)
self.run_minutely(self.run_minutely_c, time)
```
-#### run_every()
+#### {% linkable_title run_every() %}
Execute a repeating callback with a configurable delay starting at a specific time.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_every(callback, time, repeat, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### time
+##### {% linkable_title time %}
A Python `time` object that specifies when the initial callback will occur.
-##### repeat
+##### {% linkable_title repeat %}
After the initial callback has occurred, another will occur every `repeat` seconds.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Run every 17 minutes starting in 2 hours time
@@ -884,42 +883,42 @@ import datetime
self.run_every(self.run_every_c, time, 17 * 60)
```
-#### cancel_timer()
+#### {% linkable_title cancel_timer() %}
Cancel a previously created timer
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.cancel_timer(handle)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### handle
+##### {% linkable_title handle %}
A handle value returned from the original call to create the timer.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.cancel_timer(handle)
```
-### info_timer()
+### {% linkable_title info_timer() %}
Get information on a scheduler event from it's handle.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
time, interval, kwargs = self.info_timer(handle)
```
-#### Returns
+#### {% linkable_title Returns %}
time - datetime object representing the next time the callback will be fired
@@ -927,21 +926,19 @@ interval - repeat interval if applicable, `0` otherwise.
kwargs - the values supplied when the callback was initially created.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### handle
+##### {% linkable_title handle %}
The handle returned when the scheduler call was made.
-#### Examples
+#### {% linkable_title Examples %}
```python
time, interval, kwargs = self.info_timer(handle)
```
-
-
-### Scheduler Ransomization
+### {% linkable_title Scheduler Ransomization %}
All of the scheduler calls above support 2 additional optional arguments, `random_start` and `random_end`. Using these arguments it is possible to randomize the firing of callbacks to the degree desired by setting the appropriate number of seconds with the parameters.
@@ -961,39 +958,39 @@ self.handle = self.run_in(callback, 120, random_end = 60, **kwargs)
self.handle = self.run_in(callback, 120, random_start = -60, random_end = 60, **kwargs)
```
-## Sunrise and Sunset
+## {% linkable_title Sunrise and Sunset %}
AppDaemon has a number of features to allow easy tracking of sunrise and sunset as well as a couple of scheduler functions. Note that the scheduler functions also support the randomization parameters described above, but they cannot be used in conjunction with the `offset` parameter`.
-### run_at_sunrise()
+### {% linkable_title run_at_sunrise() %}
Run a callback at or around sunrise.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_at_sunrise(callback, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### offset =
+##### {% linkable_title offset = %}
The time in seconds that the callback should be delayed after sunrise. A negative value will result in the callback occurring before sunrise. This parameter cannot be combined with `random_start` or `random_end`
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
import datetime
@@ -1008,35 +1005,35 @@ self.run_at_sunrise(self.sun, random_start = -60*60, random_end = 60*60, "Sunris
self.run_at_sunrise(self.sun, random_start = -60*60, random_end = 30*60, "Sunrise, random - 30 - 60 mins")
```
-### run_at_sunset()
+### {% linkable_title run_at_sunset() %}
Run a callback at or around sunset.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.handle = self.run_at_sunset(callback, offset, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the timer.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### callback
+##### {% linkable_title callback %}
Function to be invoked when the requested state change occurs. It must conform to the standard Scheduler Callback format documented above.
-##### offset =
+##### {% linkable_title offset = %}
The time in seconds that the callback should be delayed after sunrise. A negative value will result in the callback occurring before sunrise. This parameter cannot be combined with `random_start` or `random_end`
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Arbitary keyword parameters to be provided to the callback function when it is invoked.
-#### Examples
+#### {% linkable_title Examples %}
```python
# Example using timedelta
@@ -1050,122 +1047,123 @@ self.run_at_sunset(self.sun, random_start = -60*60, random_end = 60*60, "Sunset,
# Run at a random time between 30 and 60 minutes before sunset
self.run_at_sunset(self.sun, random_start = -60*60, random_end = 30*60, "Sunset, random - 30 - 60 mins")
```
-### sunrise()
+### {% linkable_title sunrise() %}
Return the time that the next Sunrise will occur.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.sunrise()
```
-#### Returns
+#### {% linkable_title Returns %}
A Python datetime that represents the next time Sunrise will occur.
-#### Examples
+#### {% linkable_title Examples %}
```python
rise_time = self.sunrise()
```
-### sunset()
+### {% linkable_title sunset() %}
Return the time that the next Sunset will occur.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.sunset()
```
-#### Returns
+#### {% linkable_title Returns %}
A Python datetime that represents the next time Sunset will occur.
-#### Examples
+#### {% linkable_title Examples %}
```python
set_time = self.sunset()
```
-### sun_up()
+### {% linkable_title sun_up() %}
A function that allows you to determine if the sun is currently up.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
result = self.sun_up()
```
-#### Returns
+#### {% linkable_title Returns %}
`True` if the sun is up, False otherwise.
-#### Examples
+#### {% linkable_title Examples %}
```python
if self.sun_up():
do something
```
-### sun_down()
+### {% linkable_title sun_down() %}
A function that allows you to determine if the sun is currently down.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
result = self.sun_down()
```
-#### Returns
+#### {% linkable_title Returns %}
`True` if the sun is down, False otherwise.
-#### Examples
+#### {% linkable_title Examples %}
```python
if self.sun_down():
do something
```
-## Calling Services
-### About Services
+## {% linkable_title Calling Services %}
+
+### {% linkable_title About Services %}
Services within Home Assistant are how changes are made to the system and its devices. Services can be used to turn lights on and off, set thermostats and a whole number of other things. Home Assistant supplies a single interface to all these disparate services that take arbitrary parameters. AppDaemon provides the `call_service()` function to call into Home Assistant and run a service. In addition, it also provides convenience functions for some of the more common services making calling them a little easier.
-### call_service()
+### {% linkable_title call_service() %}
Call service is the basic way of calling a service within AppDaemon. It can call any service and provide any required parameters. Available services can be found using the developer tools in the UI. For listed services, the part before the first period is the domain, and the part after is the service name. For instance, `light.turn_on` has a domain of `light` and a service name of `turn_on`.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.call_service(self, service, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### service
+##### {% linkable_title service %}
The service name, e.g. `light.turn_on`.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Each service has different parameter requirements. This argument allows you to specify a comma separated list of keyword value pairs, e.g. `entity_id = light.office_1`. These parameters will be different for every service and can be discovered using the developer tools. Most if not all service calls require an `entity_id` however, so use of the above example is very common with this call.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.call_service("light.turn_on", entity_id = "light/office_lamp", color_name = "red")
self.call_service("notify/notify", title = "Hello", message = "Hello World")
```
-### turn_on()
+### {% linkable_title turn_on() %}
This is a convenience function for the `homassistant.turn_on` function. It is able to turn on pretty much anything in Home Assistant that can be turned on or run:
@@ -1176,27 +1174,27 @@ This is a convenience function for the `homassistant.turn_on` function. It is ab
And many more.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.turn_on(entity_id, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity_id of the thing to be turned on, e.g. `light.office_lamp` or ```scene.downstairs_on```
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
A comma separated list of key value pairs to allow specification of parameters over and above `entity_id`.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.turn_on("switch.patio_lights")
@@ -1204,152 +1202,152 @@ self.turn_on("scene.bedrrom_on")
self.turn_on("light.office_1", color_name = "green")
```
-### turn_off()
+### {% linkable_title turn_off() %}
This is a convenience function for the `homassistant.turn_off` function. Like `homeassistant.turn_on`, it is able to turn off pretty much anything in Home Assistant that can be turned off.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.turn_off(entity_id)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity_id of the thing to be turned off, e.g. `light.office_lamp` or `scene.downstairs_on`.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.turn_off("switch.patio_lights")
self.turn_off("light.office_1")
```
-### toggle()
+### {% linkable_title toggle() %}
This is a convenience function for the `homassistant.toggle` function. It is able to flip the state of pretty much anything in Home Assistant that can be turned on or off.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.toggle(entity_id)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity_id of the thing to be toggled, e.g. `light.office_lamp` or `scene.downstairs_on`.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.toggle("switch.patio_lights")
self.toggle("light.office_1", color_name = "green")
```
-### select_value()
+### {% linkable_title select_value() %}
This is a convenience function for the `input_slider.select_value` function. It is able to set the value of an input_slider in Home Assistant.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.select_value(entity_id, value)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity_id of the input_slider to be changed, e.g. `input_slider.alarm_hour`.
-##### value
+##### {% linkable_title value %}
The new value to set the input slider to.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.select_value("input_slider.alarm_hour", 6)
```
-### select_option()
+### {% linkable_title select_option() %}
This is a convenience function for the `input_select.select_option` function. It is able to set the value of an input_select in Home Assistant.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
self.select_option(entity_id, option)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity_id of the input_select to be changed, e.g. `input_select.mode`.
-##### value
+##### {% linkable_title value %}
The new value to set the input slider to.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.select_option("input_select.mode", "Day")
```
-### notify()
+### {% linkable_title notify() %}
This is a convenience function for the `notify.notify` service. It will send a notification to your defualt notification service. If you have more than one, use `call_service()` to call the specific notification service you require instead.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
notify(message, title=None)
```
-#### Returns
+#### {% linkable_title Returns %}
None
-#### Parameters
+#### {% linkable_title Parameters %}
-##### message
+##### {% linkable_title message %}
Message to be sent to the notification service.
-##### title
+##### {% linkable_title title %}
Title of the notification - optional.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.notify("", "Switching mode to Evening")
```
-## Events
+## {% linkable_title Events %}
-### About Events
+### {% linkable_title About Events %}
Events are a fundamental part of how Home Assistant works under the covers. HA has an event bus that all components can read and write to, enabling components to inform other components when important events take place. We have already seen how state changes can be propagated to AppDaemon - a state change however is merely an example of an event within Home Assistant. There are several other event types, among them are:
@@ -1369,7 +1367,7 @@ In addition to the Home Assistant supplied events, AppDaemon adds 2 more events.
- `appd_started` - fired once when AppDaemon is first started and after Apps are initialized
- `ha_started` - fired every time AppDaemon detects a Home Assistant restart
-### About Event Callbacks
+### {% linkable_title About Event Callbacks %}
As with State Change and Scheduler callbacks, Event Callbacks expect to call into functions with a known and specific signature and a class defined Scheduler callback function should look like this:
@@ -1382,46 +1380,46 @@ You can call the function whatever you like - you will reference it in the Sched
The parameters have the following meanings:
-#### self
+#### {% linkable_title self %}
A standard Python object reference.
-#### event_name
+#### {% linkable_title event_name %}
Name of the event that was called, e.g. `call_service`.
-#### data
+#### {% linkable_title data %}
Any data that the system supplied with the event as a dict.
-#### kwargs
+#### {% linkable_title kwargs %}
A dictionary containing Zero or more user keyword arguments to be supplied to the callback.
-### listen_event()
+### {% linkable_title listen_event() %}
Listen event sets up a callback for a specific event, or any event.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
handle = listen_event(function, event = None, **kwargs):
```
-#### Returns
+#### {% linkable_title Returns %}
A handle that can be used to cancel the callback.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### function
+##### {% linkable_title function %}
The function to be called when the event is fired.
-##### event
+##### {% linkable_title event %}
Name of the event to subscribe to. Can be a standard Home Assistant event such as `service_registered` or an arbitrary custom event such as `"MODE_CHANGE"`. If no event is specified, `listen_event()` will subscribe to all events.
-##### \*\*kwargs (optional)
+##### {% linkable_title \*\*kwargs (optional) %}
One or more keyword value pairs representing App specific parameters to supply to the callback. If the keywords match values within the event data, they will act as filters, meaning that if they don't match the values, the callback will not fire.
@@ -1429,7 +1427,8 @@ As an example of this, a Minimote controller when activated will generate an eve
Filtering will work with any event type, but it will be necessary to figure out the data associated with the event to understand what values can be filtered on. This can be achieved by examining Home Assistant's logfiles when the event fires.
-#### Examples
+#### {% linkable_title Examples %}
+
```python
self.listen_event(self.mode_event, "MODE_CHANGE")
# Listen for a minimote event activating scene 3:
@@ -1438,89 +1437,89 @@ self.listen_event(self.generic_event, "zwave.scene_activated", scene_id = 3)
self.listen_event(self.generic_event, "zwave.scene_activated", entity_id = "minimote_31", scene_id = 3)
```
-### cancel_listen_event()
+### {% linkable_title cancel_listen_event() %}
Cancels callbacks for a specific event.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
cancel_listen_event(handle)
```
-#### Returns
+#### {% linkable_title Returns %}
None.
-#### Parameters
+#### {% linkable_title Parameters %}
##### handle
A handle returned from a previous call to `listen_event()`.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.cancel_listen_event(handle)
```
-### info_listen_event()
+### {% linkable_title info_listen_event() %}
Get information on an event callback from it's handle.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
service, kwargs = self.info_listen_event(handle)
```
-#### Returns
+#### {% linkable_title Returns %}
service, kwargs - the values supplied when the callback was initially created.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### handle
+##### {% linkable_title handle %}
The handle returned when the `listen_event()` call was made.
-#### Examples
+#### {% linkable_title Examples %}
```python
service, kwargs = self.info_listen_event(handle)
```
-### fire_event()
+### {% linkable_title fire_event() %}
Fire an event on the HomeAssistant bus, for other components to hear.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
fire_event(event, **kwargs)
```
-#### Returns
+#### {% linkable_title Returns %}
None.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### event
+##### {% linkable_title event %}
Name of the event. Can be a standard Home Assistant event such as `service_registered` or an arbitrary custom event such as `"MODE_CHANGE"`.
-##### \*\*kwargs
+##### {% linkable_title \*\*kwargs %}
Zero or more keyword arguments that will be supplied as part of the event.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.fire_event("MY_CUSTOM_EVENT", jam="true")
```
-### Event Callback Function Signature
+### {% linkable_title Event Callback Function Signature %}
Functions called as an event callback will be supplied with 2 arguments:
@@ -1528,15 +1527,15 @@ Functions called as an event callback will be supplied with 2 arguments:
def service(self, event_name, data):
```
-#### event_name
+#### {% linkable_title event_name %}
The name of the event that caused the callback, e.g. `"MODE_CHANGE"` or `call_service`.
-#### data
+#### {% linkable_title data %}
A dictionary containing any additional information associated with the event.
-### Use of Events for Signalling between Home Assistant and AppDaemon
+### {% linkable_title Use of Events for Signalling between Home Assistant and AppDaemon %}
Home Assistant allows for the creation of custom events and existing components can send and receive them. This provides a useful mechanism for signaling back and forth between Home Assistant and AppDaemon. For instance, if you would like to create a UI Element to fire off some code in Home Assistant, all that is necessary is to create a script to fire a custom event, then subscribe to that event in AppDaemon. The script would look something like this:
@@ -1571,24 +1570,24 @@ This can be triggered with a call to AppDaemon's fire_event() as follows:
self.fire_event("MODE_CHANGE", mode = "Day")
```
-## Presence
+## {% linkable_title Presence %}
Presence in Home Assistant is tracked using Device Trackers. The state of all device trackers can be found using the `get_state()` call, however AppDaemon provides several convenience functions to make this easier.
-### get_trackers()
+### {% linkable_title get_trackers()
Return a list of all device trackers. This is designed to be iterated over.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
tracker_list = get_trackers()
```
-#### Returns
+#### {% linkable_title Returns %}
An iterable list of all device trackers.
-#### Examples
+#### {% linkable_title Examples %}
```python
trackers = self.get_trackers()
@@ -1596,7 +1595,7 @@ for tracker in trackers:
do something
```
-### get_tracker_state()
+### {% linkable_title get_tracker_state() %}
Get the state of a tracker. The values returned depend in part on the configuration and type of device trackers in the system. Simpler tracker types like `Locative` or `NMAP` will return one of 2 states:
@@ -1605,23 +1604,23 @@ Get the state of a tracker. The values returned depend in part on the configurat
Some types of device tracker are in addition able to supply locations that have been configured as Geofences, in which case the name of that location can be returned.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
location = self.get_tracker_state(tracker_id)
```
-#### Returns
+#### {% linkable_title Returns %}
A string representing the location of the tracker.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### tracker_id
+##### {% linkable_title tracker_id %}
Fully qualified entity_id of the device tracker to query, e.g. `device_tracker.andrew`.
-#### Examples
+#### {% linkable_title Examples %}
```python
trackers = self.get_trackers()
@@ -1629,187 +1628,187 @@ for tracker in trackers:
self.log("{} is {}".format(tracker, self.get_tracker_state(tracker)))
```
-### everyone_home()
+### {% linkable_title everyone_home() %}
A convenience function to determine if everyone is home. Use this in preference to getting the state of `group.all_devices()` as it avoids a race condition when using state change callbacks for device trackers.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
result = self.everyone_home()
```
-#### Returns
+#### {% linkable_title Returns %}
Returns `True` if everyone is at home, `False` otherwise.
-#### Examples
+#### {% linkable_title Examples %}
```python
if self.everyone_home():
do something
```
-### anyone_home()
+### {% linkable_title anyone_home() %}
A convenience function to determine if one or more person is home. Use this in preference to getting the state of `group.all_devices()` as it avoids a race condition when using state change callbacks for device trackers.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
result = self.anyone_home()
```
-#### Returns
+#### {% linkable_title Returns %}
Returns `True` if anyone is at home, `False` otherwise.
-#### Examples
+#### {% linkable_title Examples %}
```python
if self.anyone_home():
do something
```
-### noone_home()
+### {% linkable_title noone_home() %}
A convenience function to determine if no people are at home. Use this in preference to getting the state of group.all_devices() as it avoids a race condition when using state change callbacks for device trackers.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
result = self.noone_home()
```
-#### Returns
+#### {% linkable_title Returns %}
Returns `True` if no one is home, `False` otherwise.
-#### Examples
+#### {% linkable_title Examples %}
```python
if self.noone_home():
do something
```
-## Miscellaneous Helper Functions
+## {% linkable_title Miscellaneous Helper Functions %}
-### time()
+### {% linkable_title time() %}
Returns a python `time` object representing the current time. Use this in preference to the standard Python ways to discover the current time, especially when using the "Time Travel" feature for testing.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
time()
```
-#### Returns
+#### {% linkable_title Returns %}
A localised Python time object representing the current AppDaemon time.
-#### Parameters
+#### {% linkable_title Parameters %}
None
-#### Example
+#### {% linkable_title Example %}
```python
now = self.time()
```
-### date()
+### {% linkable_title date() %}
Returns a python `date` object representing the current date. Use this in preference to the standard Python ways to discover the current date, especially when using the "Time Travel" feature for testing.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
date()
```
-#### Returns
+#### {% linkable_title Returns %}
A localised Python time object representing the current AppDaemon date.
-#### Parameters
+#### {% linkable_title Parameters %}
None
-#### Example
+#### {% linkable_title Example %}
```python
today = self.date()
```
-### datetime()
+### {% linkable_title datetime() %}
Returns a python `datetime` object representing the current date and time. Use this in preference to the standard Python ways to discover the current time, especially when using the "Time Travel" feature for testing.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
datetime()
```
-#### Returns
+#### {% linkable_title Returns %}
A localised Python datetime object representing the current AppDaemon date and time.
-#### Parameters
+#### {% linkable_title Parameters %}
None
-#### Example
+#### {% linkable_title Example %}
```python
now = self.datetime()
```
-### convert_utc()
+### {% linkable_title convert_utc() %}
Home Assistant provides timestamps of several different sorts that may be used to gain additional insight into state changes. These timestamps are in UTC and are coded as ISO 8601 Combined date and time strings. `convert_utc()` will accept one of these strings and convert it to a localised Python datetime object representing the timestamp
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
convert_utc(utc_string)
```
-#### Returns
+#### {% linkable_title Returns %}
`convert_utc(utc_string)` returns a localised Python datetime object representing the timestamp.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### utc_string
+##### {% linkable_title utc_string %}
An ISO 8601 encoded date and time string in the following format: `2016-07-13T14:24:02.040658-04:00`
-#### Example
+#### {% linkable_title Example %}
-### parse_time()
+###{% linkable_title parse_time() %}
Takes a string representation of a time, or sunrise or sunset offset and converts it to a `datetime.time` object.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
parse_time(time_string)
```
-#### Returns
+#### {% linkable_title Returns %}
A `datetime.time` object, representing the time given in the `time_string` argument.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### time_string
+##### {% linkable_title time_string %}
A representation of the time in a string format with one of the following formats:
- HH:MM:SS - the time in Hours Minutes and Seconds, 24 hour format.
- sunrise | sunset [+ | - HH:MM:SS]- time of the next sunrise or sunset with an optional positive or negative offset in Hours Minutes and seconds
-#### Example
+#### {% linkable_title Example %}
```python
time = self.parse_time("17:30:00")
@@ -1818,30 +1817,30 @@ time = self.parse_time("sunset + 00:30:00")
time = self.parse_time("sunrise + 01:00:00")
```
-### now_is_between()
+### {% linkable_title now_is_between() %}
Takes two string representations of a time, or sunrise or sunset offset and returns true if the current time is between those 2 times. `now_is_between()` can correctly handle transitions across midnight.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
now_is_between(start_time_string, end_time_string)
```
-#### Returns
+#### {% linkable_title Returns %}
`True` if the current time is within the specified start and end times, `False` otherwise.
-#### Parameters
+#### {% linkable_title Parameters %}
-##### start_time_string, end_time_string
+##### {% linkable_title start_time_string, end_time_string %}
A representation of the start and end time respectively in a string format with one of the following formats:
- HH:MM:SS - the time in Hours Minutes and Seconds, 24 hour format.
- sunrise | sunset [+ | - HH:MM:SS]- time of the next sunrise or sunset with an optional positive or negative offset in Hours Minutes and seconds
-#### Example
+#### {% linkable_title Example %}
```python
if self.now_is_between("17:30:00", "08:00:00"):
@@ -1850,48 +1849,48 @@ if self.now_is_between("sunset - 00:45:00", "sunrise + 00:45:00"):
do something
```
-### friendly_name()
+### {% linkable_title friendly_name() %}
`frindly_name()` will return the Friendly Name of an entity if it has one.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
Name = self.friendly_name(entity_id)
```
-#### Returns
+#### {% linkable_title Returns %}
The friendly name of the entity if it exists or the entity id if not.
-#### Example
+#### {% linkable_title Example %}
```python
tracker = "device_tracker.andrew"
self.log("{} ({}) is {}".format(tracker, self.friendly_name(tracker), self.get_tracker_state(tracker)))
```
-### split_entity()
+### {% linkable_title split_entity() %}
`split_entity()` will take a fully qualified entity id of the form `light.hall_light` and split it into 2 values, the device and the entity, e.g. `light` and `hall_light`.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
device, entity = self.split_entity(entity_id)
```
-#### Parameters
+#### {% linkable_title Parameters %}
-##### entity_id
+##### {% linkable_title entity_id %}
Fully qualified entity id to be split.
-#### Returns
+#### {% linkable_title Returns %}
A list with 2 entries, the device and entity respectively.
-#### Example
+#### {% linkable_title Example %}
```python
device, entity = self.split_entity(entity_id)
@@ -1900,46 +1899,46 @@ if device == "scene":
```
-### get_app()
+### {% linkable_title get_app() %}
`get_app()` will return the instantiated object of another app running within the system. This is useful for calling functions or accessing variables that reside in different apps without requiring duplication of code.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
get_app(self, name)
```
-#### Parameters
+#### {% linkable_title Parameters %}
-##### name
+##### {% linkable_title name %}
Name of the app required. This is the name specified in header section of the config file, not the module or class.
-#### Returns
+#### {% linkable_title Returns %}
An object reference to the class.
-#### Example
+#### {% linkable_title Example %}
```python
MyApp = self.get_app("MotionLights")
MyApp.turn_light_on()
```
-### split_device_list()
+### {% linkable_title split_device_list() %}
`split_device_list()` will take a comma separated list of device types (or anything else for that matter) and return them as an iterable list. This is intended to assist in use cases where the App takes a list of entities from an argument, e.g. a list of sensors to monitor. If only one entry is provided, an iterable list will still be returned to avoid the need for special processing.
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
devices = split_device_list(list)
```
-#### Returns
+#### {% linkable_title Returns %}
A list of split devices with 1 or more entries.
-#### Example
+#### {% linkable_title Example %}
```python
for sensor in self.split_device_list(self.args["sensors"]):
@@ -1947,68 +1946,68 @@ for sensor in self.split_device_list(self.args["sensors"]):
```
-### Writing to Logfiles
+### {% linkable_title Writing to Logfiles %}
AppDaemon uses 2 separate logs - the general log and the error log. An AppDaemon App can write to either of these using the supplied convenience methods `log()` and `error()`, which are provided as part of parent `AppDaemon` class, and the call will automatically pre-pend the name of the App making the call. The `-D` option of AppDaemon can be used to specify what level of logging is required and the logger objects will work as expected.
-### log()
+### {% linkable_title log() %}
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
log(message, level = "INFO")
```
-#### Returns
+#### {% linkable_title Returns %}
Nothing
-#### Parameters
+#### {% linkable_title Parameters %}
-##### Message
+##### {% linkable_title Message %}
The message to log.
-##### level
+##### {% linkable_title level %}
The log level of the message - takes a string representing the standard logger levels.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.log("Log Test: Parameter is {}".format(some_variable))
self.log("Log Test: Parameter is {}".format(some_variable), level = "ERROR")
```
-### error()
+### {% linkable_title error() %}
-#### Synopsis
+#### {% linkable_title Synopsis %}
```python
error(message, level = "WARNING")
```
-#### Returns
+#### {% linkable_title Returns %}
Nothing
-#### Parameters
+#### {% linkable_title Parameters %}
-##### Message
+##### {% linkable_title Message %}
The message to log.
-##### level
+##### {% linkable_title level %}
The log level of the message - takes a string representing the standard logger levels.
-#### Examples
+#### {% linkable_title Examples %}
```python
self.error("Some Warning string")
self.error("Some Critical string", level = "CRITICAL")
```
-## Sharing information between Apps
+## {% linkable_title Sharing information between Apps %}
Sharing information between different Apps is very simple if required. Each app gets access to a global dictionary stored in a class attribute called `self.global_vars`. Any App can add or read any key as required. This operation is not however threadsafe so some car is needed.
@@ -2040,7 +2039,7 @@ Then access it as follows:
my_global_var = conf.config["AppDaemon"]["global_var"]
```
-## Development Workflow
+## {% linkable_title Development Workflow %}
Developing Apps is intended to be fairly simple but is an exercise in programming like any other kind of Python programming. As such, it is expected that apps will contain syntax errors and will generate exceptions during the development process. AppDaemon makes it very easy to iterate through the development process as it will automatically reload code that has changed and also will reload code if any of the parameters in the configuration file change as well.
@@ -2054,11 +2053,11 @@ With this setup, you will see that every time you write the file, AppDaemon will
If there is an error in the compilation or a runtime error, this will be directed to the `error.log` file to enable you to see the error and correct it. When an error occurs, there will also be a warning message in `appdaemon.log` to tell you to check the error log.
-## Time Travel
+## {% linkable_title Time Travel %}
OK, time travel sadly isn't really possible but it can be very useful when testing Apps. For instance, imagine you have an App that turns a light on every day at sunset. It might be nice to test it without waiting for Sunset - and with AppDaemon's "Time Travel" features you can.
-### Choosing a Start Time
+### {% linkable_title Choosing a Start Time %}
Internally, AppDaemon keeps track of it's own time relative to when it was started. This make is possible to start AppDaemon with a different start time and date to the current time. For instance to test that sunset App, start AppDaemon at a time just before sunset and see if it works as expected. To do this, simply use the "-s" argument on AppDaemon's command line. e,g,:
@@ -2072,7 +2071,7 @@ $ appdaemon -s "2016-06-06 19:16:00"
Note the timestamps in the log - AppDaemon believes it is now just before sunset and will process any callbacks appropriately.
-### Speeding things up
+### {% linkable_title Speeding things up %}
Some Apps need to run for periods of a day or two for you to test all aspects. This can be time consuming, but Time Travel can also help here in two ways. The first is by speeding up time. To do this, simply use the `-t` option on the command line. This specifies the amount of time a second lasts while time travelling. The default of course is 1 second, but if you change it to `0.1` for instance, AppDaemon will work 10x faster. If you set it to `0`, AppDaemon will work as fast as possible and, depending in your hardware, may be able to get through an entire day in a matter of minutes. Bear in mind however, due to the threaded nature of AppDaemon, when you are running with `-t 0` you may see actual events firing a little later than expected as the rest of the system tries to keep up with the timer. To set the tick time, start AppDaemon as follows:
@@ -2105,6 +2104,6 @@ $ appdaemon -s "2016-06-06 19:16:00" -s "2016-06-06 20:16:00" -t 0
```
-### A Note on Times
+### {% linkable_title A Note on Times %}
-Some Apps you write may depend on checking times of events relative to the current time. If you are time travelling this will not work if you use standard python library calls to get the current time and date etc. For this reason, always use the AppDamon supplied `time()`, `date()` and `datetime()` calls, documented earlier. These calls will consult with AppDaemon's internal time rather than the actual time and give you the correct values.
\ No newline at end of file
+Some Apps you write may depend on checking times of events relative to the current time. If you are time travelling this will not work if you use standard python library calls to get the current time and date etc. For this reason, always use the AppDamon supplied `time()`, `date()` and `datetime()` calls, documented earlier. These calls will consult with AppDaemon's internal time rather than the actual time and give you the correct values.
diff --git a/source/_ecosystem/appdaemon/configuration.markdown b/source/_ecosystem/appdaemon/configuration.markdown
index d17504ea4ed..27aac6fd82e 100644
--- a/source/_ecosystem/appdaemon/configuration.markdown
+++ b/source/_ecosystem/appdaemon/configuration.markdown
@@ -43,7 +43,7 @@ class = HelloWorld
The `#Apps` section is the configuration for the Hello World program and should be left in place for initial testing but can be removed later if desired, as other Apps are added, App configuration is described in the [API doc](API.md).
-## Docker
+## {% linkable_title Docker %}
For Docker Configuration you need to take a couple of extra things into consideration.
@@ -77,4 +77,4 @@ elevation =
```
-You can run Docker and point the conf volume to that directory.
\ No newline at end of file
+You can run Docker and point the conf volume to that directory.
diff --git a/source/_ecosystem/appdaemon/installation.markdown b/source/_ecosystem/appdaemon/installation.markdown
index f313035ae49..14f46148441 100644
--- a/source/_ecosystem/appdaemon/installation.markdown
+++ b/source/_ecosystem/appdaemon/installation.markdown
@@ -11,9 +11,9 @@ regenerate: true
hide_github_edit: true
---
-Installation is either by pip3 or Docker.
+Installation is either by `pip3` or Docker.
-## Clone the Repository
+## {% linkable_title Clone the Repository %}
For either method you will need to clone the **AppDaemon** repository to the current local directory on your machine.
@@ -27,7 +27,7 @@ Change your working directory to the repository root. Moving forward, we will be
$ cd appdaemon
```
-## Install using Docker
+## {% linkable_title Install using Docker %}
To build the Docker image run the following:
@@ -37,7 +37,7 @@ $ docker build -t appdaemon .
(Note the period at the end of the above command)
-## Install Using PIP3
+## {% linkable_title Install using `pip3` %}
Before running `AppDaemon` you will need to install the package:
diff --git a/source/_ecosystem/appdaemon/operation.markdown b/source/_ecosystem/appdaemon/operation.markdown
index e928ec4babd..499bca70ef3 100644
--- a/source/_ecosystem/appdaemon/operation.markdown
+++ b/source/_ecosystem/appdaemon/operation.markdown
@@ -11,4 +11,4 @@ regenerate: true
hide_github_edit: true
---
-Since AppDaemon under the covers uses the exact same APIs as the frontend UI, you typically see it react at about the same time to a given event. Calling back to Home Assistant is also pretty fast especially if they are running on the same machine. In action, observed latency above the built in automation component is usually sub-second.
+Since `AppDaemon` under the covers uses the exact same APIs as the frontend UI, you typically see it react at about the same time to a given event. Calling back to Home Assistant is also pretty fast especially if they are running on the same machine. In action, observed latency above the built in automation component is usually sub-second.
diff --git a/source/_ecosystem/appdaemon/running.markdown b/source/_ecosystem/appdaemon/running.markdown
index 62e8718f9b8..f447a73895d 100755
--- a/source/_ecosystem/appdaemon/running.markdown
+++ b/source/_ecosystem/appdaemon/running.markdown
@@ -11,9 +11,9 @@ regenerate: true
hide_github_edit: true
---
-As configured, AppDaemon comes with a single HelloWorld App that will send a greeting to the logfile to show that everything is working correctly.
+As configured, `AppDaemon` comes with a single HelloWorld App that will send a greeting to the logfile to show that everything is working correctly.
-## Docker
+## {% linkable_title Docker %}
Assuming you have set the config up as described above for Docker, you can run it with the command:
@@ -42,7 +42,7 @@ $ docker logs appdaemon
Note that for Docker, the error and regular logs are combined.
-## PIP3
+## {% linkable_title `pip3` %}
You can then run AppDaemon from the command line as follows:
@@ -61,7 +61,7 @@ $ appdaemon -c conf/appdaemon.cfg
2016-08-22 10:08:16,584 INFO You are now ready to run Apps!
```
-## AppDaemon arguments
+## {% linkable_title AppDaemon arguments %}
```
usage: appdaemon [-h] [-c CONFIG] [-p PIDFILE] [-t TICK] [-s STARTTIME]
@@ -87,8 +87,8 @@ optional arguments:
-d, --daemon run as a background process
```
--c is the path to the configuration file. If not specified, AppDaemon will look for a file named `appdaemon.cfg` first in `~/.homeassistant` then in `/etc/appdaemon`. If the file is not specified and it is not found in either location, AppDaemon will raise an exception.
-
+-c is the path to the configuration file. If not specified, AppDaemon will look for a file named `appdaemon.cfg` first in `~/.homeassistant` then in `/etc/appdaemon`. If the file is not specified and it is not found in either location, AppDaemon will raise an exception.
+
-d and -p are used by the init file to start the process as a daemon and are not required if running from the command line.
-D can be used to increase the debug level for internal AppDaemon operations as well as apps using the logging function.
diff --git a/source/_ecosystem/appdaemon/tutorial.markdown b/source/_ecosystem/appdaemon/tutorial.markdown
index 7c23e38a95d..fe9f6ee8718 100755
--- a/source/_ecosystem/appdaemon/tutorial.markdown
+++ b/source/_ecosystem/appdaemon/tutorial.markdown
@@ -11,7 +11,7 @@ regenerate: true
hide_github_edit: true
---
-## Another Take on Automation
+## {% linkable_title Another Take on Automation %}
If you haven't yet read Paulus' excellent Blog entry on [Perfect Home Automation](https://home-assistant.io/blog/2016/01/19/perfect-home-automation/) I would encourage you to take a look. As a veteran of several Home Automation systems with varying degrees success, it was this article more than anything else that convinced me that Home Assistant had the right philosophy behind it and was on the right track. One of the most important points made is that being able to control your lights from your phone, 9 times out of 10 is harder than using a lightswitch - where Home Automation really comes into its own is when you start removing the need to use a phone or the switch - the "Automation" in Home Automation. A surprisingly large number of systems out there miss this essential point and have limited abilities to automate anything which is why a robust and open system such as Home Assistant is such an important part of the equation in bring this all together in the vast and chaotic ecosystem that is the "Internet of Things".
@@ -38,11 +38,11 @@ So why `AppDaemon`? AppDaemon is not meant to replace Home Assistant Automations
It is in fact a testament to Home Assistant's open nature that a component like `AppDaemon` can be integrated so neatly and closely that it acts in all ways like an extension of the system, not a second class citizen. Part of the strength of Home Assistant's underlying design is that it makes no assumptions whatever about what it is controlling or reacting to, or reporting state on. This is made achievable in part by the great flexibility of Python as a programming environment for Home Assistant, and carrying that forward has enabled me to use the same philosophy for `AppDaemon` - it took surprisingly little code to be able to respond to basic events and call services in a completely open ended manner - the bulk of the work after that was adding additonal functions to make things that were already possible easier.
-## How it Works
+## {% linkable_title How it Works %}
The best way to show what AppDaemon does is through a few simple examples.
-### Sunrise/Sunset Lighting
+### {% linkable_title Sunrise/Sunset Lighting %}
Lets start with a simple App to turn a light on every night at sunset and off every morning at sunrise. Every App when first started will have its `initialize()` function called which gives it a chance to register a callback for AppDaemons's scheduler for a specific time. In this case we are using `run_at_sunrise()` and `run_at_sunset()` to register 2 separate callbacks. The argument `0` is the number of seconds offset from sunrise or sunset and can be negative or positive. For complex intervals it can be convenient to use Python's `datetime.timedelta` class for calculations. When sunrise or sunset occurs, the appropriate callback function, `sunrise_cb()` or `sunset_cb()` is called which then makes a call to Home Assistant to turn the porch light on or off by activating a scene. The variables `args["on_scene"]` and `args["off_scene"]` are passed through from the configuration of this particular App, and the same code could be reused to activate completely different scenes in a different version of the App.
@@ -127,4 +127,3 @@ If this has whet your appetite, feel free to give it a try.
Happy Automating!
-
diff --git a/source/_ecosystem/appdaemon/windows.markdown b/source/_ecosystem/appdaemon/windows.markdown
index 9b443f77534..3ec32879838 100755
--- a/source/_ecosystem/appdaemon/windows.markdown
+++ b/source/_ecosystem/appdaemon/windows.markdown
@@ -18,6 +18,6 @@ AppDaemon runs under windows and has been tested with the official 3.5.2 release
AppDaemon can be installed exactly as per the instructions for every other version using pip3.
-## Windows Under the Linux Subsystem
+## {% linkable_title Windows Under the Linux Subsystem %}
-Windows 10 now supports a full Linux bash environment that is capable of running Python. This is essentially an Ubuntu distribution and works extremely well. It is possible to run AppDaemon in exactly the same way as for Linux distributions, and none of the above Windows Caveats apply to this version. This is the reccomended way to run AppDaemon in a Windows 10 and later environment.
\ No newline at end of file
+Windows 10 now supports a full Linux bash environment that is capable of running Python. This is essentially an Ubuntu distribution and works extremely well. It is possible to run AppDaemon in exactly the same way as for Linux distributions, and none of the above Windows Caveats apply to this version. This is the reccomended way to run AppDaemon in a Windows 10 and later environment.
From c6d4101536c39fb0129d81e8c53d92d7ffb17b63 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 10:41:05 +0100
Subject: [PATCH 010/123] Update configuration sample
---
source/_components/alarm_control_panel.manual.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/alarm_control_panel.manual.markdown b/source/_components/alarm_control_panel.manual.markdown
index 2b6746cbaea..fd1807019fd 100644
--- a/source/_components/alarm_control_panel.manual.markdown
+++ b/source/_components/alarm_control_panel.manual.markdown
@@ -18,7 +18,7 @@ This platform enables you to set manual alarms in Home Assistant.
```yaml
# Example configuration.yaml entry
alarm_control_panel:
- platform: manual
+ - platform: manual
```
Configuration variables:
From 50bb203de39b56b1216902f26c7e096a62e5abbf Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 10:42:18 +0100
Subject: [PATCH 011/123] Fix link
---
.../_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index 9790da7ddc8..31c8cbe2a2f 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -121,7 +121,7 @@ Experiencing issues introduced by this release? Please report them in our [issue
[vlc-media]: https://home-assistant.io/components/media_player.vlc/
[aquostv]: https://home-assistant.io/components/media_player.aquostv/
[digitalloggers]: https://home-assistant.io/components/switch.digitalloggers/
-[netdata]: https://home-assistant.io/components/switch.digitalloggers/
+[netdata]: https://home-assistant.io/components/sensor.netdata/
[bl-sensor]: https://home-assistant.io/components/sensor.broadlink/
[bl-switch]: https://home-assistant.io/components/switch.broadlink/
[hikvision]: https://home-assistant.io/components/binary_sensor.hikvision/
From 19cc13df72b54719ce6f6e8b6918dcfbb9c671fa Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 11:02:33 +0100
Subject: [PATCH 012/123] Fix liquid errors
---
source/_ecosystem/appdaemon/api.markdown | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/source/_ecosystem/appdaemon/api.markdown b/source/_ecosystem/appdaemon/api.markdown
index 346c0546881..8615d751248 100755
--- a/source/_ecosystem/appdaemon/api.markdown
+++ b/source/_ecosystem/appdaemon/api.markdown
@@ -72,7 +72,7 @@ class NightLight(appapi.AppDaemon):
To summarize - an App's lifecycle consists of being initialized, which allows it to set one or more state and/or schedule callbacks. When those callbacks are activated, the App will typically use one of the Service Calling calls to effect some change to the devices of the system and then wait for the next relevant state change. That's all there is to it!
-## {% linkable_title About the API
+## {% linkable_title About the API %}
The implementation of the API is located in the AppDaemon class that Apps are derived from. The code for the functions is therefore available to the App simply by invoking the name of the function from the object namespace using the `self` keyword, as in the above examples. `self.turn_on()` for example is just a method defined in the parent class and made available to the child. This design decision was made to simplify some of the implementation and hide passing of unnecessary variables during the API invocation.
@@ -289,7 +289,7 @@ get_state(entity = None, attribute = None)
`get_state()` returns a `dictionary` or single value, the structure of which varies according to the parameters used.
-#### {% linkable_title Parameters
+#### {% linkable_title Parameters %}
All parameters are optional, and if `get_state()` is called with no parameters it will return the entire state of Home Assistant at that given time. This will consist of a dictionary with a key for each entity. Under that key will be the standard entity state information.
@@ -450,7 +450,7 @@ The value of the state after the state change.
A dictionary containing any constraints and/or additional user specific keyword arguments supplied to the `listen_state()` call.
-### l{% linkable_title isten_state()
+### {% linkable_title listen_state() %}
`listen_state()` allows the user to register a callback for a wide variety of state changes.
@@ -1574,7 +1574,7 @@ self.fire_event("MODE_CHANGE", mode = "Day")
Presence in Home Assistant is tracked using Device Trackers. The state of all device trackers can be found using the `get_state()` call, however AppDaemon provides several convenience functions to make this easier.
-### {% linkable_title get_trackers()
+### {% linkable_title get_trackers() %}
Return a list of all device trackers. This is designed to be iterated over.
From c24d0a9159935ad55a32244b21d143769ff92968 Mon Sep 17 00:00:00 2001
From: Fredrik Lindqvist
Date: Sun, 18 Dec 2016 11:30:13 +0100
Subject: [PATCH 013/123] Import #1563 into updated docs (#1621)
Fixes merge conflict in #1563
Documentation by @armills .
---
source/_components/binary_sensor.flic.markdown | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/source/_components/binary_sensor.flic.markdown b/source/_components/binary_sensor.flic.markdown
index 30feabe6e92..769c9c6cf0f 100644
--- a/source/_components/binary_sensor.flic.markdown
+++ b/source/_components/binary_sensor.flic.markdown
@@ -30,12 +30,14 @@ Configuration variables:
- **host** (*Optional*): The IP or hostname of the flic service server. (default: `localhost`)
- **port** (*Optional*): The port of the flic service. (default: `5551`)
- **discovery** (*Optional*): If `true`, the component is configured to constantly scan for new buttons. (default: `true`)
-
+- **timeout** (*Optional*): Maximum time in seconds an event can be queued locally on a button before discarding the event. (default: `3`)
#### {% linkable_title Discovery %}
If discovery is enabled, you can add a new button by pressing it for at least 7s. The button will be paired with the flic service and added to Home Assistant. Otherwise, you have to manually pair it with the flic service. The Home Assistant platform will not scan for new buttons and will only connect to buttons already paired.
+#### {% linkable_title Timeout %}
+ +When the flic button is triggered while disconnected from flic service, it will queue all events and try to connect and transmit them as soon as possible. The timeout variable can be used to stop events from triggering if too much time passed between the action and the notification in Home Assistant.
#### {% linkable_title Events %}
@@ -61,3 +63,4 @@ Event data:
- **button_name**: The name of the button, that triggered the event.
- **button_address**: The bluetooth address of the button, that triggered the event.
- **click_type**: The type of click. Possible values are `single`, `double` and `hold`.
+- **queued_time**: The amount of time this event was queued on the button, in seconds.
From 40ca29b2e93b7fe5ecefc5693877a92fb22bd5d6 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 16:42:12 +0100
Subject: [PATCH 014/123] Escaping
---
source/_components/device_tracker.owntracks.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/_components/device_tracker.owntracks.markdown b/source/_components/device_tracker.owntracks.markdown
index 3e9b2da076a..dd78f4f261d 100644
--- a/source/_components/device_tracker.owntracks.markdown
+++ b/source/_components/device_tracker.owntracks.markdown
@@ -51,7 +51,7 @@ Owntracks can also be used with other device trackers, such as [Nmap](/component
An example showing the inclusion of the `mac` field for multiple component tracking. The `mac` field will need to be added to the `owntracks` device and will enable tracking by all components that track via the `mac` address.
```yaml
-_:
+\_:
name: Friendly Name
mac: EA:AA:55:E7:C6:94
picture: https://home-assistant.io/images/favicon-192x192.png
@@ -65,7 +65,7 @@ Owntracks can track regions, and send region entry and exit information to Home
Home Assistant will use the enter and leave messages to set your zone location. Your location will be set to the center of zone when you enter. Location updates from OwnTracks will be ignored while you are inside a zone.
-When you exit a zone, Home Assistant will start using location updates to track you again. To make sure that Home Assistant correctly exits a zone (which it calculates based on your GPS co-ordinates), you may want to set your Zone radius in HA to be slightly smaller that the Owntracks region radius.
+When you exit a zone, Home Assistant will start using location updates to track you again. To make sure that Home Assistant correctly exits a zone (which it calculates based on your GPS coordinates), you may want to set your Zone radius in HA to be slightly smaller that the Owntracks region radius.
### {% linkable_title Using Owntracks regions - forcing Owntracks to update using %}iBeacons
When run in the usual *significant changes mode* (which is kind to your phone battery), Owntracks sometimes doesn't update your location as quickly as you'd like when you arrive at a zone. This can be annoying if you want to trigger an automation when you get home. You can improve the situation using iBeacons.
From f9ebbd271b4a806e704dacd39151f9ee30439bdd Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 16:47:46 +0100
Subject: [PATCH 015/123] Update variables
---
source/_components/device_tracker.owntracks.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/device_tracker.owntracks.markdown b/source/_components/device_tracker.owntracks.markdown
index dd78f4f261d..364b917e93e 100644
--- a/source/_components/device_tracker.owntracks.markdown
+++ b/source/_components/device_tracker.owntracks.markdown
@@ -51,7 +51,7 @@ Owntracks can also be used with other device trackers, such as [Nmap](/component
An example showing the inclusion of the `mac` field for multiple component tracking. The `mac` field will need to be added to the `owntracks` device and will enable tracking by all components that track via the `mac` address.
```yaml
-\_:
+USERNAME_DEVICE_ID:
name: Friendly Name
mac: EA:AA:55:E7:C6:94
picture: https://home-assistant.io/images/favicon-192x192.png
From 5efe180893ae24f9c261a4330c448864c4002469 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 10:21:05 -0800
Subject: [PATCH 016/123] Update tts.markdown
---
source/_components/tts.markdown | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/source/_components/tts.markdown b/source/_components/tts.markdown
index 1c28b44ff9d..4057a0cb4dd 100644
--- a/source/_components/tts.markdown
+++ b/source/_components/tts.markdown
@@ -50,13 +50,14 @@ tts:
Say to all `media_player` device entities:
```yaml
-service: tts.platform_say
+# Replace google_say with _say when you use a different platform.
+service: tts.google_say
data:
message: 'May the Force be with you.'
```
```yaml
-service: tts.platform_say
+service: tts.google_say
entity_id: media_player.floor
data:
message: 'May the Force be with you.'
@@ -65,7 +66,7 @@ data:
With a template:
```yaml
-service: tts.platform_say
+service: tts.google_say
data_template:
message: 'Temperature is {% raw %}{{ sensor.temperature }}{% endraw %}.'
cache: false
From 3c72339abfea545b66263248dbcadf395e4a91e4 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 14:23:37 -0800
Subject: [PATCH 017/123] Release 0.35.1
---
_config.yml | 6 +++---
source/_components/http.markdown | 1 +
...17-text-to-speech-aquostv-flic-zamg.markdown | 17 ++++++++++++++---
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/_config.yml b/_config.yml
index ebbba408766..b5dae514da0 100644
--- a/_config.yml
+++ b/_config.yml
@@ -131,9 +131,9 @@ social:
# Home Assistant release details
current_major_version: 0
current_minor_version: 35
-current_patch_version: 0
-date_released: 2016-12-17
+current_patch_version: 1
+date_released: 2016-12-18
# Either # or the anchor link to latest release notes in the blog post.
# Must be prefixed with a # and have double quotes around it.
-patch_version_notes: "#release-0345---december-12"
+patch_version_notes: "#release-0351---december-18"
diff --git a/source/_components/http.markdown b/source/_components/http.markdown
index 89463c89801..7bc0384c31c 100644
--- a/source/_components/http.markdown
+++ b/source/_components/http.markdown
@@ -28,6 +28,7 @@ Configuration variables:
- **api_password** (*Optional*): Protect Home Assistant with a password.
- **server_host** (*Optional*): Only listen to incoming requests on specific ip/host (default: accept all)
- **server_port** (*Optional*): Let you set a port to use. Defaults to 8123.
+- **base_url** (*Optional*): The url that Home Assistant is available on the internet. Defaults to local IP address.
- **development** (*Optional*): Disable caching and load unvulcanized assets. Useful for Frontend development.
- **ssl_certificate** (*Optional*): Path to your TLS/SSL certificate to serve Home Assistant over a secure connection.
- **ssl_key** (*Optional*): Path to your TLS/SSL key to serve Home Assistant over a secure connection.
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index 31c8cbe2a2f..bf0832e6fb0 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -62,6 +62,20 @@ The [GPSLogger](https://home-assistant.io/components/device_tracker.gpslogger/)
- Media player - Kodi: Authentification fixed and fan art ([@balloob], [@joopert])
- Minor and not so minor features and bug fixes by [@pvizeli], [@jminn], [@magicus], [@teodoc], [@fabaff], [@technicalpickles], [@balloob], [@lukas-hetzenecker], [@rubund], [@dasos], [@trisk], [@armills], [@auduny], [@lwis], [@nkgilley], [@janLo], [@keatontaylor], [@stefan-jonasson], [@Jypy], [@jawilson], [@DavidLP], [@molobrakos], [@jabesq], [@joerocklin], [@kellerza], [@kirichkov], and [@danielperna84].
+### {% linkable_title Release 0.35.1 - December 18 %}
+
+Some issues have been reported with TTS that will be addressed by 0.35.1. The TTS component had issues linking the media player to the right media file if you were using Docker or SSL certificates. This can be fixed by exposing to your HTTP config what url you use for hosting Home Assistant:
+
+```yaml
+http:
+ base_url: example.duckdns.org
+```
+
+ - Fix exit hanging on OS X with async logging ([@balloob])
+ - Fix Text to speech clearing cache ([@pvizeli])
+ - Allow setting a base API url in HTTP component ([@balloob])
+ - Fix occasional errors in automation ([@pvizeli])
+
### {% linkable_title If you need help... %}
...don't hesitate to use our [Forum](https://community.home-assistant.io/) or join us for a little [chat](https://gitter.im/home-assistant/home-assistant). The release notes have comments enabled but it's preferred if you use the former communication channels. Thanks.
@@ -85,8 +99,6 @@ Experiencing issues introduced by this release? Please report them in our [issue
[@farminf]: https://github.com/farminf
[@jabesq]: https://github.com/jabesq
[@janLo]: https://github.com/janLo
-[@janLo]: https://github.com/janLo
-[@jawilson]: https://github.com/jawilson
[@jawilson]: https://github.com/jawilson
[@jminn]: https://github.com/jminn
[@joerocklin]: https://github.com/joerocklin
@@ -107,7 +119,6 @@ Experiencing issues introduced by this release? Please report them in our [issue
[@nkgilley]: https://github.com/nkgilley
[@pvizeli]: https://github.com/pvizeli
[@rubund]: https://github.com/rubund
-[@rubund]: https://github.com/rubund
[@soldag]: https://github.com/soldag
[@stefan-jonasson]: https://github.com/stefan-jonasson
[@tchellomello]: https://github.com/tchellomello
From 61213e566fb09b35ad910f92b3f57551acfb836c Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 17:38:13 -0800
Subject: [PATCH 018/123] Release 0.35.2
---
_config.yml | 6 +++---
.../2016-12-17-text-to-speech-aquostv-flic-zamg.markdown | 4 ++++
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/_config.yml b/_config.yml
index b5dae514da0..028e14a72cd 100644
--- a/_config.yml
+++ b/_config.yml
@@ -131,9 +131,9 @@ social:
# Home Assistant release details
current_major_version: 0
current_minor_version: 35
-current_patch_version: 1
-date_released: 2016-12-18
+current_patch_version: 2
+date_released: 2016-12-19
# Either # or the anchor link to latest release notes in the blog post.
# Must be prefixed with a # and have double quotes around it.
-patch_version_notes: "#release-0351---december-18"
+patch_version_notes: "#release-0352---december-19"
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index bf0832e6fb0..5842cf86b4d 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -76,6 +76,10 @@ http:
- Allow setting a base API url in HTTP component ([@balloob])
- Fix occasional errors in automation ([@pvizeli])
+### {% linkable_title Release 0.35.2 - December 19 %}
+
+ - When base url specified, do not combine it with `server_port` ([@balloob])
+
### {% linkable_title If you need help... %}
...don't hesitate to use our [Forum](https://community.home-assistant.io/) or join us for a little [chat](https://gitter.im/home-assistant/home-assistant). The release notes have comments enabled but it's preferred if you use the former communication channels. Thanks.
From bdb9d55d2c4c576b748979606ae228baf105c347 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 17:49:03 -0800
Subject: [PATCH 019/123] Add r-jordan to bug fix contributors
---
.../2016-12-17-text-to-speech-aquostv-flic-zamg.markdown | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index 5842cf86b4d..2e76ea2fbd8 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -60,7 +60,7 @@ The [GPSLogger](https://home-assistant.io/components/device_tracker.gpslogger/)
- Media player - Emby: New support for trailer and media position ([@mezz64])
- Camera - Amcrest: Support for resolution ([@tchellomello])
- Media player - Kodi: Authentification fixed and fan art ([@balloob], [@joopert])
-- Minor and not so minor features and bug fixes by [@pvizeli], [@jminn], [@magicus], [@teodoc], [@fabaff], [@technicalpickles], [@balloob], [@lukas-hetzenecker], [@rubund], [@dasos], [@trisk], [@armills], [@auduny], [@lwis], [@nkgilley], [@janLo], [@keatontaylor], [@stefan-jonasson], [@Jypy], [@jawilson], [@DavidLP], [@molobrakos], [@jabesq], [@joerocklin], [@kellerza], [@kirichkov], and [@danielperna84].
+- Minor and not so minor features and bug fixes by [@pvizeli], [@jminn], [@magicus], [@teodoc], [@fabaff], [@technicalpickles], [@balloob], [@lukas-hetzenecker], [@rubund], [@dasos], [@trisk], [@armills], [@auduny], [@lwis], [@nkgilley], [@janLo], [@keatontaylor], [@stefan-jonasson], [@Jypy], [@jawilson], [@DavidLP], [@molobrakos], [@jabesq], [@joerocklin], [@kellerza], [@kirichkov], [@r-jordan] and [@danielperna84].
### {% linkable_title Release 0.35.1 - December 18 %}
@@ -87,6 +87,7 @@ http:
Experiencing issues introduced by this release? Please report them in our [issue tracker](https://github.com/home-assistant/home-assistant/issues). Make sure to fill in all fields of the issue template.
+[@r-jordan]: https://github.com/r-jordan
[@aequitas]: https://github.com/aequitas
[@armills]: https://github.com/armills
[@auduny]: https://github.com/auduny
From 6a496d4d76d3bf490dd19b0d3ac5aa6da4366589 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 21:19:39 -0800
Subject: [PATCH 020/123] Add base url example
---
source/_components/http.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/http.markdown b/source/_components/http.markdown
index 7bc0384c31c..a72714bb264 100644
--- a/source/_components/http.markdown
+++ b/source/_components/http.markdown
@@ -28,7 +28,7 @@ Configuration variables:
- **api_password** (*Optional*): Protect Home Assistant with a password.
- **server_host** (*Optional*): Only listen to incoming requests on specific ip/host (default: accept all)
- **server_port** (*Optional*): Let you set a port to use. Defaults to 8123.
-- **base_url** (*Optional*): The url that Home Assistant is available on the internet. Defaults to local IP address.
+- **base_url** (*Optional*): The url that Home Assistant is available on the internet. For example: `hass-example.duckdns.org:8123`. Defaults to local IP address.
- **development** (*Optional*): Disable caching and load unvulcanized assets. Useful for Frontend development.
- **ssl_certificate** (*Optional*): Path to your TLS/SSL certificate to serve Home Assistant over a secure connection.
- **ssl_key** (*Optional*): Path to your TLS/SSL key to serve Home Assistant over a secure connection.
From c105fd6cbd6036f319a2687834e39cf4e7cc03ca Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 21:19:49 -0800
Subject: [PATCH 021/123] Update asyncio docs
---
.../asyncio_working_with_async.markdown | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/source/developers/asyncio_working_with_async.markdown b/source/developers/asyncio_working_with_async.markdown
index 48839b4648e..1ae52aeff24 100644
--- a/source/developers/asyncio_working_with_async.markdown
+++ b/source/developers/asyncio_working_with_async.markdown
@@ -19,7 +19,22 @@ So if you are making calls to the core (the hass object) from within a callback
## {% linkable_title Implementing an async component %}
-We currently do not support async setup for components. We do however support using async functions as service handlers. Just define your handlers as a callback or coroutine and register them as usual.
+To make a component async, implement an async_setup.
+
+```python
+def setup(hass, config):
+ # Setup your component outside of the event loop.
+```
+
+Will turn into:
+
+```python
+import asyncio
+
+@asyncio.coroutine
+def async_setup(hass, config):
+ # Setup your component inside of the event loop.
+```
## {% linkable_title Implementing an async platform %}
From f5c55bbab5a912ced43d6c1fed22420bc6b938d8 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Sun, 18 Dec 2016 21:20:00 -0800
Subject: [PATCH 022/123] Add TTS link to base_url in HTTP
---
source/_components/tts.markdown | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/source/_components/tts.markdown b/source/_components/tts.markdown
index 4057a0cb4dd..8fb93e67aa1 100644
--- a/source/_components/tts.markdown
+++ b/source/_components/tts.markdown
@@ -12,10 +12,6 @@ ha_release: 0.35
Text-to-speech (TTS) enables Home Assistant to speak to you.
-## {% linkable_title Cache %}
-
-The component have two caches. Both caches can be controlled with the `cache` option in the platform configuration or the service call `say`. A long time cache will be located on the file system. The in-memory cache for fast responses to media players will be auto-cleaned after a short period.
-
## {% linkable_title Configuring a `tts` platform %}
To get started, add the following lines to your `configuration.yaml` (example for google):
@@ -45,6 +41,10 @@ tts:
time_memory: 300
```
+
+If you are running Home Assistant over SSL or from within a container, you will have to setup a base url inside the [http component](/components/http/).
+
+
## {% linkable_title Service say %}
Say to all `media_player` device entities:
@@ -72,3 +72,6 @@ data_template:
cache: false
```
+## {% linkable_title Cache %}
+
+The component have two caches. Both caches can be controlled with the `cache` option in the platform configuration or the service call `say`. A long time cache will be located on the file system. The in-memory cache for fast responses to media players will be auto-cleaned after a short period.
From fcb25db7009a5df42cb64305280c7333f336e8fd Mon Sep 17 00:00:00 2001
From: Jeremy
Date: Mon, 19 Dec 2016 10:36:06 -0600
Subject: [PATCH 023/123] Update nest.markdown (#1627)
Added note to leave the Redirect URI blank.
---
source/_components/nest.markdown | 1 +
1 file changed, 1 insertion(+)
diff --git a/source/_components/nest.markdown b/source/_components/nest.markdown
index 482e42013eb..15e578396f5 100644
--- a/source/_components/nest.markdown
+++ b/source/_components/nest.markdown
@@ -25,6 +25,7 @@ The Nest component is the main component to integrate all [Nest](https://nest.co
6. Fill in details:
- Product name must be unique. We recommend [email] - Home Assistant.
- The description, users, urls can all be anything you want.
+ - Leave the "Redirect URI" Field blank
7. For permissions check every box and if it's an option select the read/write option.
- The description requires a specific format to be accepted.
- Use "[Home Assistant] [Edit] [For Home Automation]" as the description as it is not super important.
From 4fa59d24e423e31765987869e0aecc6f0cedff23 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Mon, 19 Dec 2016 09:05:06 -0800
Subject: [PATCH 024/123] Add thank you blog post
---
source/_posts/2016-12-19-thank-you.markdown | 37 +++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 source/_posts/2016-12-19-thank-you.markdown
diff --git a/source/_posts/2016-12-19-thank-you.markdown b/source/_posts/2016-12-19-thank-you.markdown
new file mode 100644
index 00000000000..805ea6c4265
--- /dev/null
+++ b/source/_posts/2016-12-19-thank-you.markdown
@@ -0,0 +1,37 @@
+---
+layout: post
+title: "Thank You"
+description: "Thanks to all our contributors, dependencies and companies that help make Home Assistant awesome."
+date: 2016-12-18 21:04:05 -0800
+date_formatted: "December 19, 2016"
+author: Paulus Schoutsen
+author_twitter: balloob
+comments: true
+categories: Community
+og_image:
+---
+
+A year ago [Home Assistant 0.10][0.10] landed. Last weekend we released 0.35. Doing 25 releases in a year is a big accomplishment by the community and each release has moved us forwards leaps and bounds. In this year alone we have seen 2800 pull requests on the main repo alone, that's more than 7 a day!
+
+One of the things that Jon Walker, the founder of the company I work for ([AppFolio]), has taught me is that the biggest advantage that you can create for yourself compared to your competitors is to release more often. Everytime you release you are able to get the new features into the hands of the users and developers. The faster people start using it, the faster you get feedback on the good and bad parts and thus the faster can you evolve.
+
+That's why I structured Home Assistant around a two week release cycle. It makes sure that features get out fast and it also forces us to not accumulate a backlog of things to document or test properly. Every two weeks we can start fresh. This makes it easy for new people to start contributing because it's clear when things go out and people are not afraid to miss a release.
+
+However, being on a two week release cycle also means that the community has to rally each two weeks to make sure everything is ready to go. A lot of people are involved in making sure that all pieces are in place, to all of those: thank you! Thank you for all the time and effort you put in to make Home Assistant the best home automation software out there.
+
+Another big thanks goes out to the developers of the Python language and all the open source libraries and tools that Home Assistant depends on. Making quality software is not a small feat and all of you can be proud of yourself.
+
+Also a big thanks for the companies that offer their services for free to open source projects. Without these we would not be able to operate at our speed or scale. Thanks [GitHub], [TravisCI], [CloudFlare] and [Discourse]!
+
+And finally thank you community for being so helpful and awesome 🙇.
+
+We're taking a well deserved break and we will be back again in 2017 with more awesomeness. Happy holidays!
+
+– Paulus
+
+[0.10]: https://home-assistant.io/blog/2015/12/22/amazon-echo-icloud-and-templates/
+[AppFolio]: http://www.appfolioinc.com/
+[GitHub]: https://GitHub.com
+[TravisCI]: https://Travis-ci.org
+[CloudFlare]: https://CloudFlare.com
+[Discourse]: https://Discourse.com
From 34771d067407af85a38a5ab11e053e4e5e010a9d Mon Sep 17 00:00:00 2001
From: Terry Carlin
Date: Mon, 19 Dec 2016 11:14:12 -0700
Subject: [PATCH 025/123] Removing references to URL and replacing with IP
address (#1629)
The homeassistant/components/media_player/itunes.py requires an IP address not a URL
---
source/_components/media_player.itunes.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/_components/media_player.itunes.markdown b/source/_components/media_player.itunes.markdown
index c046755ae2a..fd357eb8ec7 100644
--- a/source/_components/media_player.itunes.markdown
+++ b/source/_components/media_player.itunes.markdown
@@ -24,10 +24,10 @@ To add iTunes to your installation, add the following to your `configuration.yam
# Example configuration.yaml entry
media_player:
- platform: itunes
- host: http://192.168.1.50
+ host: 192.168.1.50
```
Configuration variables:
-- **host** (*Required*): The URL of the itunes-api API, eg. http://192.168.1.50
+- **host** (*Required*): The IP of the itunes-api API, eg. 192.168.1.50
- **port** (*Optional*): The port where itunes-api is accessible, eg. 8181.
From f478ea0798c0cce2750d7d9166990ed2db27190d Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 18 Dec 2016 16:59:44 +0100
Subject: [PATCH 026/123] Fix sample
---
source/_components/device_tracker.owntracks.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/device_tracker.owntracks.markdown b/source/_components/device_tracker.owntracks.markdown
index 364b917e93e..e8562f8b307 100644
--- a/source/_components/device_tracker.owntracks.markdown
+++ b/source/_components/device_tracker.owntracks.markdown
@@ -58,7 +58,7 @@ USERNAME_DEVICE_ID:
gravatar: test@example.com
track: yes
hide_if_away: no
- ```
+```
### {% linkable_title Using Owntracks regions %}
Owntracks can track regions, and send region entry and exit information to Home Assistant (HA). You set up a region in the Owntracks app which you should name the same as your HA Zone, and then make sure to turn on the `share` option for the region in the owntracks app. Please see the [owntracks documentation](http://owntracks.org/booklet/guide/waypoints/).
From 45fc9c13564ff630196ced57ca880a8f27624adf Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Mon, 19 Dec 2016 21:11:43 +0100
Subject: [PATCH 027/123] Make it more clear
---
source/_posts/2016-04-07-static-website.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_posts/2016-04-07-static-website.markdown b/source/_posts/2016-04-07-static-website.markdown
index 9e81ef881fe..911e442b635 100644
--- a/source/_posts/2016-04-07-static-website.markdown
+++ b/source/_posts/2016-04-07-static-website.markdown
@@ -11,7 +11,7 @@ categories: How-To
og_image: /images/blog/2016-04-display/ha-display.png
---
-The frontend of Home Assistant is served with the help of a local web server. If you have [customized](/getting-started/devices/#customizing-devices-and-services) your installation you already use this functionality. The content of your folder `www` in your Home Assistant configuration directory (`.homeassistant`) is available under `/local` (eg. [https://localhost:8123/local](https://localhost:8123/local)).
+The frontend of Home Assistant is served with the help of a local web server. If you have [customized](/getting-started/devices/#customizing-devices-and-services) your installation you already use this functionality. The content of your folder `www` in your Home Assistant configuration directory (`.homeassistant`) is available under `/local` (eg. [http://localhost:8123/local](https://localhost:8123/local/index.html) for an `index.html` file).
But there is more you can do! You can not only host images for customization there but HTML files or even web applications including CSS and Javascript.
From dd5a1767a2b6633bfcc16c6db3ce69b1965ff15c Mon Sep 17 00:00:00 2001
From: Carlo Costanzo
Date: Tue, 20 Dec 2016 02:24:47 -0500
Subject: [PATCH 028/123] Clarification time. (#1598)
Just some clarification based on https://github.com/home-assistant/home-assistant/issues/4904#issuecomment-267426303
---
source/getting-started/automation-trigger.markdown | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/source/getting-started/automation-trigger.markdown b/source/getting-started/automation-trigger.markdown
index f47c0a1c695..79bbf3681fa 100644
--- a/source/getting-started/automation-trigger.markdown
+++ b/source/getting-started/automation-trigger.markdown
@@ -113,7 +113,7 @@ automation:
platform: time
# Matches every hour at 5 minutes past whole
minutes: 5
- seconds: 0
+ seconds: 00
automation 2:
trigger:
@@ -127,8 +127,11 @@ automation 3:
platform: time
# You can also match on interval. This will match every 5 minutes
minutes: '/5'
- seconds: 0
+ seconds: 00
```
+
+ Rememebr that if you are using matching to include both `minutes` and `seconds`. Without `seconds`, your automation will trigger 60 times during the matching minute.
+
### {% linkable_title Zone trigger %}
From 0429b5e38e7bab8acc149faae49102ce6ac155d3 Mon Sep 17 00:00:00 2001
From: hokagegano
Date: Tue, 20 Dec 2016 17:45:40 +0100
Subject: [PATCH 029/123] Update sensor.rfxtrx.markdown
---
source/_components/sensor.rfxtrx.markdown | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/source/_components/sensor.rfxtrx.markdown b/source/_components/sensor.rfxtrx.markdown
index dabffea8e95..3c8aa88b952 100644
--- a/source/_components/sensor.rfxtrx.markdown
+++ b/source/_components/sensor.rfxtrx.markdown
@@ -29,8 +29,8 @@ Then when the sensor emits a signal it will be automatically added:
-Here the name is `0a52080000301004d240259` and you can verify that it works from the frontend.
-Then you should update your configuration to:
+Here the name is `0a52080000301004d240259` or `0a52080000301004d240259_temperature` and you can verify that it works from the frontend.
+Then you should update your configuration to (_temperature is not needed):
```yaml
# Example configuration.yaml entry
@@ -54,6 +54,17 @@ sensor:
- Humidity
- Temperature
```
+Only these data_type are valid :
+- *Temperature*
+- *Humidity*
+- *Barometer*
+- *Wind direction*
+- *Rain rate*
+- *Energy usage*
+- *Total usage*
+- *Sound*
+- *Sensor Status*
+- *Counter value*
Example configuration:
@@ -79,3 +90,5 @@ Configuration variables:
- **automatic_add** (*Optional*): To enable the automatic addition of new lights.
- **data_type** (*Optional*): Which data type the sensor should show
- **fire_event** (*Optional*): Fires an event even if the state is the same as before. Can be used for automations.
+
+
From 3f72924e41e6eb32358d371886eee6be8e0969e8 Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Tue, 20 Dec 2016 17:36:41 -0500
Subject: [PATCH 030/123] Update installation-raspberry-pi-all-in-one.markdown
updated username and paths to reflect updated usage.
---
...tallation-raspberry-pi-all-in-one.markdown | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 7acb8286c3c..5cfef840572 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -9,13 +9,13 @@ footer: true
The [Raspberry Pi All-In-One Installer](https://github.com/home-assistant/fabric-home-assistant) deploys a complete Home Assistant server including support for MQTT with websockets, Z-Wave, and the Open-Zwave Control Panel.
-The only requirement is that you have a Raspberry Pi with a fresh installation of [Raspbian Jessie](https://www.raspberrypi.org/downloads/raspbian/) connected to your network.
+The only requirement is that you have a Raspberry Pi with a fresh installation of [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) connected to your network.
* Login to Raspberry Pi. For example with `ssh pi@your_raspberry_pi_ip`
* Run the following command
```bash
-$ wget -Nnv https://raw.githubusercontent.com/home-assistant/fabric-home-assistant/master/hass_rpi_installer.sh && bash hass_rpi_installer.sh
+$ wget -Nnv https://raw.githubusercontent.com/home-assistant/fabric-home-assistant/master/hass_rpi_installer.sh && chown pi:pi hass_rpi_installer.sh && bash hass_rpi_installer.sh
```
Note this command is one-line and not run as sudo.
@@ -27,7 +27,7 @@ Installation will take approx. 1-2 hours depending on the Raspberry Pi model the
Once rebooted, your Raspberry Pi will be up and running with Home Assistant. You can access it at [http://your_raspberry_pi_ip:8123](http://your_raspberry_pi_ip:8123).
-The Home Assistant configuration is located at `/home/hass/.homeassistant`. The [virtualenv](https://virtualenv.pypa.io/en/latest/) with the Home Assistant installation is located at `/srv/hass/hass_venv`. As part of the secure installation, a new user (**hass**) is added to your Raspberry Pi to run Home Assistant. This is a system account and does not have login or other abilities by design. When editing your `configuration.yaml` files, you will need to run the commands with `sudo` or by switching user.
+The Home Assistant configuration is located at `/home/hass/.homeassistant`. The [virtualenv](https://virtualenv.pypa.io/en/latest/) with the Home Assistant installation is located at `/srv/homeassistant/homeassistant_venv`. As part of the secure installation, a new user (**homeassistant**) is added to your Raspberry Pi to run Home Assistant. This is a system account and does not have login or other abilities by design. When editing your `configuration.yaml` files, you will need to run the commands with `sudo` or by switching user.
*Windows users*: Setting up WinSCP to allow this seemlessly is at the end of this page.
@@ -41,20 +41,20 @@ The All-In-One Installer script will do the following automatically:
* Create needed service accounts
* Install OS and Python dependencies
* Setup a python virtualenv to run Home Assistant and components inside.
-* Run as `hass` service account
+* Run as `homeassistant` service account
* Install Home Assistant in a virtualenv
-* Build and install Mosquitto v1.4.9 from source with websocket support running on ports 1883 and 9001
+* Install Mosquitto with websocket support running on ports 1883 and 9001
* Build and Install Python-openzwave in the Home Assistant virtualenv
-* Build openzwave-control-panel in `/srv/hass/src/open-zwave-control-panel`
-* Add both Home Assistant and Mosquitto to systemd services to start at boot
+* Build openzwave-control-panel in `/srv/homeassistant/src/open-zwave-control-panel`
+* Add Home Assistant to systemd services to start at boot
### {% linkable_title Upgrading %}
To upgrade the All-In-One setup manually:
* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
-* Change to hass user `sudo su -s /bin/bash hass`
-* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
+* Change to homeassistant user `sudo su -s /bin/bash homeassistant`
+* Change to virtual enviroment `source /srv/homeassistant/homeassistant_venv/bin/activate`
* Update HA `pip3 install --upgrade homeassistant`
* Type `exit` to logout the hass user and return to the `pi` user.
@@ -75,7 +75,7 @@ To launch the OZWCP web application:
* Make sure Home Assistant is not running! So stop that first
* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
-* Change to the ozwcp directory `cd /srv/hass/src/open-zwave-control-panel/`
+* Change to the ozwcp directory `cd /srv/homeassistant/src/open-zwave-control-panel/`
* Launch the control panel `sudo ./ozwcp -p 8888`
* Open a web browser to `http://your_pi_ip:8888`
* Specify your zwave controller, for example `/dev/ttyACM0` and hit initialize
From 1d7e520f3c2e7c4b04ec85a300914d5719115118 Mon Sep 17 00:00:00 2001
From: Carlo Costanzo
Date: Wed, 21 Dec 2016 00:48:36 -0500
Subject: [PATCH 031/123] Added section To change the MQTT default password
(#1632)
* Added section To change the MQTT default password
---
.../installation-raspberry-pi-all-in-one.markdown | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 5cfef840572..c5b310312d7 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -69,6 +69,13 @@ After upgrading, you can restart Home Assistant a few different ways:
* Restarting the Raspberry Pi `sudo reboot`
* Restarting the Home-Assistant Service `sudo systemctl restart home-assistant.service`
+### {% linkable_title To change the MQTT default password %}
+
+* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
+* Change password `sudo mosquitto_passwd /etc/mosquitto/pwfile pi`
+* Restart mosquitto `sudo systemctl restart mosquitto.service`
+* Be sure to update your `configuration.yaml` to reflect the new password.
+
### {% linkable_title Using the OZWCP web application %}
To launch the OZWCP web application:
From 2aac86dae7f19f670534bb9dcb76629cf07746d1 Mon Sep 17 00:00:00 2001
From: Carlo Costanzo
Date: Wed, 21 Dec 2016 01:03:42 -0500
Subject: [PATCH 032/123] Added an example of a control option. (#1635)
Inclusion of hiding control option of groups.
---
source/_components/group.markdown | 1 +
1 file changed, 1 insertion(+)
diff --git a/source/_components/group.markdown b/source/_components/group.markdown
index 1ff8a8f457d..731ab582781 100644
--- a/source/_components/group.markdown
+++ b/source/_components/group.markdown
@@ -74,6 +74,7 @@ Notice in the example below that in order to refer to the group "Living Room", y
```yaml
# Example configuration.yaml entry that shows two groups, referred to in a view group (tab)
Living Room:
+ control: hidden
entities:
- light.light_family_1
- binary_sensor.motion_living
From 3998fb6c18a49da4c3f26a02d782cc6949fce324 Mon Sep 17 00:00:00 2001
From: Pascal Vizeli
Date: Wed, 21 Dec 2016 09:59:01 +0100
Subject: [PATCH 033/123] Update media_player.markdown
---
source/_components/media_player.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/media_player.markdown b/source/_components/media_player.markdown
index ae8639342ec..1850653639b 100644
--- a/source/_components/media_player.markdown
+++ b/source/_components/media_player.markdown
@@ -32,7 +32,7 @@ Available services: `turn_on`, `turn_off`, `toggle`, `volume_up`, `volume_down`,
| Service data attribute | Optional | Description |
|------------------------|----------|--------------------------------------------------|
| `entity_id` | yes | Target a specific media player. Defaults to all. |
-| `volume_level` | no | Integer for volume level |
+| `volume_level` | no | Float for volume level |
#### {% linkable_title Service `media_player/media_seek` %}
From ddc0ba2a581c5c5e7f69f8dbd61c297de5e3b681 Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Wed, 21 Dec 2016 10:05:00 -0500
Subject: [PATCH 034/123] Update installation-raspberry-pi-all-in-one.markdown
updated GPIO details
---
.../installation-raspberry-pi-all-in-one.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index c5b310312d7..0c588b9b5c8 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -93,7 +93,7 @@ To launch the OZWCP web application:
### {% linkable_title Using the GPIOs %}
-Please note that if you are using any components for Home Assistant that would use the GPIOs on the RPI, you will need to grant the default AiO user `hass` access to the GPIOs. Run the following command `sudo adduser hass gpio` while in a terminal session on your Pi. This is a one time configuration change to allow All In One Installer based Home Assistant access to the GPIOs.
+The (**homeassistant**)user is added to the GPIO group as part of the install now.
### {% linkable_title WinSCP %}
From 41626643d32e14ea63bbcaf377d9ec72e66a385e Mon Sep 17 00:00:00 2001
From: Xx-Ness-xX
Date: Wed, 21 Dec 2016 13:23:00 -0600
Subject: [PATCH 035/123] Configuration variables: units (optional)
Added descriptions for units, and an example entry for configuration.yaml
---
source/_components/sensor.google_travel_time.markdown | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/source/_components/sensor.google_travel_time.markdown b/source/_components/sensor.google_travel_time.markdown
index 7c904d9d540..16c08a70988 100644
--- a/source/_components/sensor.google_travel_time.markdown
+++ b/source/_components/sensor.google_travel_time.markdown
@@ -37,6 +37,7 @@ Configuration variables:
- **options** (*Optional*): A dictionary containing parameters to add to all requests to the Distance Matrix API. A full listing of available options can be found [here](https://developers.google.com/maps/documentation/distance-matrix/intro#RequestParameters).
- **departure_time** (*Optional*): Can be `now`, a Unix timestamp, or a 24 hour time string like `08:00:00`. If you provide a time string, it will be combined with the current date to get travel time for that moment.
- **arrival_time** (*Optional*): See notes above for `departure_time`. `arrival_time` can not be `now`, only a Unix timestamp or time string. You can not provide both `departure_time` and `arrival_time`. If you do provide both, `arrival_time` will be removed from the request.
+ - **units** (*Optional*): Set the unit for the sensor in metric or imperial, otherwise the default unit the same as the unit set in `unit_system:`.
##### {% linkable_title Dynamic Configuration %}
@@ -58,6 +59,14 @@ sensor:
api_key: XXXX_XXXXX_XXXXX
origin: zone.home
destination: Eddies House # Friendly name of a zone
+
+ # Tracking entity in imperial unit
+ - platform: google_travel_time
+ api_key: XXXX_XXXXX_XXXXX
+ destination: zone.home
+ options:
+ units: imperial # 'metric' for Metric, 'imperial' for Imperial
+
```
#### {% linkable_title Entity Tracking %}
From 9d1f01f0e95f667a648dede594ab72939d44e0bc Mon Sep 17 00:00:00 2001
From: Kyle Gordon
Date: Wed, 21 Dec 2016 20:03:31 +0000
Subject: [PATCH 036/123] Remove superfluous - (#1638)
broadcast doesn't need -
---
source/_components/light.lifx.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/light.lifx.markdown b/source/_components/light.lifx.markdown
index f326e3cb95e..d4be5e91192 100644
--- a/source/_components/light.lifx.markdown
+++ b/source/_components/light.lifx.markdown
@@ -19,7 +19,7 @@ The `lifx` platform allows you to integrate your [LIFX](http://www.lifx.com) int
# Example configuration.yaml entry
light:
- platform: lifx
- - broadcast: 192.168.1.255
+ broadcast: 192.168.1.255
```
Configuration variables:
From a948767b40d598c727a35ba384b1e68e8dcd89c9 Mon Sep 17 00:00:00 2001
From: Krasimir Chariyski
Date: Wed, 21 Dec 2016 22:04:09 +0200
Subject: [PATCH 037/123] Add proper markdown for links (#1637)
Add proper markdown for links.
---
source/_components/notify.html5.markdown | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/source/_components/notify.html5.markdown b/source/_components/notify.html5.markdown
index 9821dec6df6..a5c935f16a1 100644
--- a/source/_components/notify.html5.markdown
+++ b/source/_components/notify.html5.markdown
@@ -32,9 +32,9 @@ Configuration variables:
- **gcm_sender_id** (*Required if pushing to Chrome*): The sender ID provided to you by Google for Google Cloud Messaging (GCM). Required to push to Chrome.
### {% linkable_title Getting ready for Chrome %}
-Create new project at https://console.cloud.google.com/home/dashboard
-Go to https://console.cloud.google.com/apis/credentials/domainverification and verify your domain
-After that, go to https://console.firebase.google.com and select import Google project, select the project you created
+Create new project at [https://console.cloud.google.com/home/dashboard](https://console.cloud.google.com/home/dashboard)
+Go to [https://console.cloud.google.com/apis/credentials/domainverification](https://console.cloud.google.com/apis/credentials/domainverification) and verify your domain
+After that, go to [https://console.firebase.google.com](https://console.firebase.google.com) and select import Google project, select the project you created
Then, click the clogwheel on top left and select Project settings
Select Cloud messaging tab
if under server key is button Regenerate key, click that
From e3ae3e3698c63e8e8e561ca6234c571b0b87e11f Mon Sep 17 00:00:00 2001
From: Will Heid
Date: Fri, 23 Dec 2016 03:02:06 -0800
Subject: [PATCH 038/123] Add note about new default Raspbian behavior (#1640)
* Add note about new default Raspbian behavior
---
.../installation-raspberry-pi-all-in-one.markdown | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 0c588b9b5c8..30ce58992bd 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -11,6 +11,10 @@ The [Raspberry Pi All-In-One Installer](https://github.com/home-assistant/fabric
The only requirement is that you have a Raspberry Pi with a fresh installation of [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) connected to your network.
+
+Note that as of 2016-11-30 SSH is disabled by default in the official Raspbian images. Adding an empty file called `ssh` to `/boot/` or the FAT32 partition will enable it. More information is on the Raspberry Pi Foundation [Blog](https://www.raspberrypi.org/blog/page/2/?fish#a-security-update-for-raspbian-pixel)
+
+
* Login to Raspberry Pi. For example with `ssh pi@your_raspberry_pi_ip`
* Run the following command
From 7b172c98f86c7a50aa8ebf04aff2d548a81d483c Mon Sep 17 00:00:00 2001
From: Chris Monteiro
Date: Fri, 23 Dec 2016 06:02:40 -0500
Subject: [PATCH 039/123] Added new model (#1645)
---
source/_components/media_player.aquostv.markdown | 1 +
1 file changed, 1 insertion(+)
diff --git a/source/_components/media_player.aquostv.markdown b/source/_components/media_player.aquostv.markdown
index 601ca86896b..ed017b0a223 100644
--- a/source/_components/media_player.aquostv.markdown
+++ b/source/_components/media_player.aquostv.markdown
@@ -44,5 +44,6 @@ Currently known supported models:
- LC-60LE830U
- LC-52LE925UN
- LC-60LE925UN
+- LC-60LE857U
If your model is not on the list then give it a test, if everything works correctly then add it to the list on [GitHub](https://github.com/home-assistant/home-assistant.github.io/tree/current/source/_components/media_player.aquostv.markdown).
From 514a68a315804d3770f0a979af0491567e63e7e3 Mon Sep 17 00:00:00 2001
From: andrey-git
Date: Fri, 23 Dec 2016 13:10:08 +0200
Subject: [PATCH 040/123] Describe using language in a TTS service. (#1644)
* Following pull request #5047 describe using language in a service.
* Update tts.markdown
Added descriptors for yaml examples .
---
source/_components/tts.markdown | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/source/_components/tts.markdown b/source/_components/tts.markdown
index 8fb93e67aa1..8e9fac152f7 100644
--- a/source/_components/tts.markdown
+++ b/source/_components/tts.markdown
@@ -55,6 +55,7 @@ service: tts.google_say
data:
message: 'May the Force be with you.'
```
+Say to the `media_player.floor` device entitie:
```yaml
service: tts.google_say
@@ -63,6 +64,16 @@ data:
message: 'May the Force be with you.'
```
+Say to the `media_player.floor` device entitie in french:
+
+```yaml
+service: tts.google_say
+entity_id: media_player.floor
+data:
+ message: 'Que la force soit avec toi.'
+ language: 'fr'
+```
+
With a template:
```yaml
@@ -72,6 +83,7 @@ data_template:
cache: false
```
+
## {% linkable_title Cache %}
The component have two caches. Both caches can be controlled with the `cache` option in the platform configuration or the service call `say`. A long time cache will be located on the file system. The in-memory cache for fast responses to media players will be auto-cleaned after a short period.
From 91cbaff9254a216d8559c8a9f706e5613caa4015 Mon Sep 17 00:00:00 2001
From: Fredrik Lindqvist
Date: Fri, 23 Dec 2016 14:17:18 +0100
Subject: [PATCH 041/123] Revert "Describe using language in a TTS service."
(#1647)
Merged by mistake.
---
source/_components/tts.markdown | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/source/_components/tts.markdown b/source/_components/tts.markdown
index 8e9fac152f7..8fb93e67aa1 100644
--- a/source/_components/tts.markdown
+++ b/source/_components/tts.markdown
@@ -55,7 +55,6 @@ service: tts.google_say
data:
message: 'May the Force be with you.'
```
-Say to the `media_player.floor` device entitie:
```yaml
service: tts.google_say
@@ -64,16 +63,6 @@ data:
message: 'May the Force be with you.'
```
-Say to the `media_player.floor` device entitie in french:
-
-```yaml
-service: tts.google_say
-entity_id: media_player.floor
-data:
- message: 'Que la force soit avec toi.'
- language: 'fr'
-```
-
With a template:
```yaml
@@ -83,7 +72,6 @@ data_template:
cache: false
```
-
## {% linkable_title Cache %}
The component have two caches. Both caches can be controlled with the `cache` option in the platform configuration or the service call `say`. A long time cache will be located on the file system. The in-memory cache for fast responses to media players will be auto-cleaned after a short period.
From 380c5f6eefa84b2b06f7e421b778f48995153f5f Mon Sep 17 00:00:00 2001
From: Pascal Vizeli
Date: Fri, 23 Dec 2016 16:18:02 +0100
Subject: [PATCH 042/123] Update
2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
---
.../2016-12-17-text-to-speech-aquostv-flic-zamg.markdown | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
index 2e76ea2fbd8..eb68d517486 100644
--- a/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
+++ b/source/_posts/2016-12-17-text-to-speech-aquostv-flic-zamg.markdown
@@ -80,6 +80,13 @@ http:
- When base url specified, do not combine it with `server_port` ([@balloob])
+### {% linkable_title Release 0.35.3 - December 23 %}
+
+ - Fix issue with voicerrs and post api ([@pvizeli])
+ - Fix async component update on service calls ([@pvizeli])
+ - Fix async log handle do not close ([@pvizeli])
+ - Fix nest component with various KeyError exceptions ([@technicalpickles])
+
### {% linkable_title If you need help... %}
...don't hesitate to use our [Forum](https://community.home-assistant.io/) or join us for a little [chat](https://gitter.im/home-assistant/home-assistant). The release notes have comments enabled but it's preferred if you use the former communication channels. Thanks.
From 9db0a34a2b3a8b1551a2b502db8dec210ed8f841 Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Fri, 23 Dec 2016 16:03:32 -0500
Subject: [PATCH 043/123] Update installation-raspberry-pi.markdown
fixed service account user listed under AiO notes.
---
source/getting-started/installation-raspberry-pi.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/getting-started/installation-raspberry-pi.markdown b/source/getting-started/installation-raspberry-pi.markdown
index fadd27addc6..c30b177cfcd 100644
--- a/source/getting-started/installation-raspberry-pi.markdown
+++ b/source/getting-started/installation-raspberry-pi.markdown
@@ -14,7 +14,7 @@ footer: true
There's currently three documented ways to install Home Assistant on a Raspberry Pi.
- [Manual installation](/getting-started/installation-raspberry-pi/#Manual-Installation). Following this guide doing each step manually. This is highly recommended as a first installation since you get a good overview of the installation.
- [Hassbian image](/getting-started/installation-raspberry-pi-image). Basic installation with the same settings as following the manual installation guide. Some additional software is preinstalled to make installation quicker and easier. Installation uses `homeassistant` user.
- - [All-in-One Installer](/getting-started/installation-raspberry-pi-all-in-one/). Fabric based installation script that installs and compiles many of the things an advanced Home Assistant install is likely to need. Installation uses `hass` user.
+ - [All-in-One Installer](/getting-started/installation-raspberry-pi-all-in-one/). Fabric based installation script that installs and compiles many of the things an advanced Home Assistant install is likely to need. Installation uses `homeassistant` user.
Since each installation type uses a different user for Home Assistant, be sure to note and use the correct username for the `adduser` commands listed below for camera and GPIO extensions.
From 2f2127dd8b992213b6a2dc2a280d04999eb215ba Mon Sep 17 00:00:00 2001
From: smolz
Date: Sat, 24 Dec 2016 00:12:32 -0800
Subject: [PATCH 044/123] Update group_visibility.markdown (#1649)
---
source/_topics/group_visibility.markdown | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/source/_topics/group_visibility.markdown b/source/_topics/group_visibility.markdown
index 64b01be9e78..a312ced7588 100644
--- a/source/_topics/group_visibility.markdown
+++ b/source/_topics/group_visibility.markdown
@@ -56,7 +56,7 @@ automation 2:
## {% linkable_title Easier automations %}
-One of the most common uses cases are to show groups during certain times of day, maybe commuting information durig a work day morning or light switches when it is getting dark. The complexity of automations needed to make this happen will quickly get out of hands. So, one way to make the automations easier is to create a sensor that alters its state depending on time of day. One way of doing that is using a `command_line` sensor and a script:
+One of the most common uses cases are to show groups during certain times of day, maybe commuting information during a work day morning or light switches when it is getting dark. The complexity of automations needed to make this happen will quickly get out of hand. So, one way to make the automations easier is to create a sensor that alters its state depending on time of day. One way of doing that is using a `command_line` sensor and a script:
```python
#!/usr/bin/env python3
@@ -103,7 +103,7 @@ sensor:
command: "python3 occasion.py"
```
-To simplify things, we create a Home Assistant script that change visibility of a group but also verifies that an entity is in a specific state:
+To simplify things, we create a Home Assistant script that changes the visibility of a group, but also verifies that an entity is in a specific state:
```yaml
script:
@@ -176,4 +176,4 @@ automation:
entity_id: group.work_sensors
cond: sensor.occasion
visible_state: 'work_morning'
-```
\ No newline at end of file
+```
From 5a3c04697a789a31ad52a9dc8334effdfae9cca9 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sun, 25 Dec 2016 19:56:03 +0100
Subject: [PATCH 045/123] Update frontpage for 0.35.3
---
_config.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/_config.yml b/_config.yml
index 028e14a72cd..9cd44b84333 100644
--- a/_config.yml
+++ b/_config.yml
@@ -131,8 +131,8 @@ social:
# Home Assistant release details
current_major_version: 0
current_minor_version: 35
-current_patch_version: 2
-date_released: 2016-12-19
+current_patch_version: 3
+date_released: 2016-12-23
# Either # or the anchor link to latest release notes in the blog post.
# Must be prefixed with a # and have double quotes around it.
From ad83231605c95a3bca8cd59437d5b015bce57fd6 Mon Sep 17 00:00:00 2001
From: atorralba
Date: Sun, 25 Dec 2016 23:14:52 +0100
Subject: [PATCH 046/123] Update media_player.samsungtv.markdown
Added JU6400 model as showing in GUI but unable to control.
---
source/_components/media_player.samsungtv.markdown | 1 +
1 file changed, 1 insertion(+)
diff --git a/source/_components/media_player.samsungtv.markdown b/source/_components/media_player.samsungtv.markdown
index be9dd04a1a5..0374c23a708 100644
--- a/source/_components/media_player.samsungtv.markdown
+++ b/source/_components/media_player.samsungtv.markdown
@@ -54,6 +54,7 @@ Currently tested but not working models:
- KU6300 - Shows in GUI but unable to control.
- H6400 - Shows in GUI but unable to control.
- J5200 - Unable to see state and unable to control
+- JU6400 - Shows in GUI but unable to control
If your model is not on the list then give it a test, if everything works correctly then add it to the list on [GitHub](https://github.com/home-assistant/home-assistant.github.io/tree/current/source/_components/media_player.samsungtv.markdown).
The first letter (U, P, L, H & K) represent the screen type, e.g. LED or Plasma. The second letter represents the region, E is Europe, N is North America and A is Asia & Australia. The two numbers following that represent the screen size.
From 12ba9ed751930400c42ecad64e811a8c5303de8f Mon Sep 17 00:00:00 2001
From: smolz
Date: Mon, 26 Dec 2016 05:16:24 -0800
Subject: [PATCH 047/123] Update dweet.markdown (#1654)
---
source/_components/dweet.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/dweet.markdown b/source/_components/dweet.markdown
index 37cf9a82e0c..ae2fabf2376 100644
--- a/source/_components/dweet.markdown
+++ b/source/_components/dweet.markdown
@@ -21,7 +21,7 @@ The `dweet` component makes it possible to transfer details collected with Home
- The publishing interval is limited to 1 second. This means that it's possible to missing fast changes.
+ The publishing interval is limited to 1 second. This means that it's possible to miss fast changes.
To use the `deweet` component in your installation, add the following to your `configuration.yaml` file:
From bfb89cab9746e581b3140440e89377d0d361176a Mon Sep 17 00:00:00 2001
From: PuckStar
Date: Mon, 26 Dec 2016 17:05:42 +0100
Subject: [PATCH 048/123] "homeassistant" changed in "hass" (#1656)
Couldn't update Home Assistant.
Found out the word homeassistant was changed in hass.
After changing that the update worked.
---
.../installation-raspberry-pi-all-in-one.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 30ce58992bd..56925fb687f 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -57,8 +57,8 @@ The All-In-One Installer script will do the following automatically:
To upgrade the All-In-One setup manually:
* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
-* Change to homeassistant user `sudo su -s /bin/bash homeassistant`
-* Change to virtual enviroment `source /srv/homeassistant/homeassistant_venv/bin/activate`
+* Change to homeassistant user `sudo su -s /bin/bash hass`
+* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
* Update HA `pip3 install --upgrade homeassistant`
* Type `exit` to logout the hass user and return to the `pi` user.
From 3f93d28d54d1809400b4e03ce81c09267a45a369 Mon Sep 17 00:00:00 2001
From: Fredrik Lindqvist
Date: Mon, 26 Dec 2016 17:32:37 +0100
Subject: [PATCH 049/123] Revert ""homeassistant" changed in "hass"" (#1657)
---
.../installation-raspberry-pi-all-in-one.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 56925fb687f..30ce58992bd 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -57,8 +57,8 @@ The All-In-One Installer script will do the following automatically:
To upgrade the All-In-One setup manually:
* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
-* Change to homeassistant user `sudo su -s /bin/bash hass`
-* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
+* Change to homeassistant user `sudo su -s /bin/bash homeassistant`
+* Change to virtual enviroment `source /srv/homeassistant/homeassistant_venv/bin/activate`
* Update HA `pip3 install --upgrade homeassistant`
* Type `exit` to logout the hass user and return to the `pi` user.
From 145b8cb0068f1af5a096431649655841e5d33643 Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Mon, 26 Dec 2016 11:36:27 -0500
Subject: [PATCH 050/123] Update
fixed upgrade notes to reflect correct commands.
added alt commands for prior aio paths and user.
---
.../installation-raspberry-pi-all-in-one.markdown | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 30ce58992bd..cdaeb2f2985 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -61,6 +61,16 @@ To upgrade the All-In-One setup manually:
* Change to virtual enviroment `source /srv/homeassistant/homeassistant_venv/bin/activate`
* Update HA `pip3 install --upgrade homeassistant`
* Type `exit` to logout the hass user and return to the `pi` user.
+
+
+If you deployed Home Assistant via the AiO installer prior to December 2016, you will need to use the following commands:
+* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
+* Change to homeassistant user `sudo su -s /bin/bash hass`
+* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
+* Update HA `pip3 install --upgrade homeassistant`
+* Type `exit` to logout the hass user and return to the `pi` user.
+
+
To upgrade with fabric:
From 23985c34e212f12052e0ec80e03b49f7f7c14ffb Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Mon, 26 Dec 2016 11:41:20 -0500
Subject: [PATCH 051/123] Update installation-raspberry-pi-all-in-one.markdown
fixed formatting.
---
.../installation-raspberry-pi-all-in-one.markdown | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index cdaeb2f2985..88cb9b5a985 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -64,11 +64,13 @@ To upgrade the All-In-One setup manually:
If you deployed Home Assistant via the AiO installer prior to December 2016, you will need to use the following commands:
+
* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
* Change to homeassistant user `sudo su -s /bin/bash hass`
* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
* Update HA `pip3 install --upgrade homeassistant`
* Type `exit` to logout the hass user and return to the `pi` user.
+
From 8f3b40da1ab8f437eb9e1548e1a78c7db503bbcb Mon Sep 17 00:00:00 2001
From: Jonathan Baginski
Date: Mon, 26 Dec 2016 11:50:06 -0500
Subject: [PATCH 052/123] Update installation-raspberry-pi-all-in-one.markdown
formatting fix
---
.../installation-raspberry-pi-all-in-one.markdown | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/source/getting-started/installation-raspberry-pi-all-in-one.markdown b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
index 88cb9b5a985..3bcbe0eac49 100644
--- a/source/getting-started/installation-raspberry-pi-all-in-one.markdown
+++ b/source/getting-started/installation-raspberry-pi-all-in-one.markdown
@@ -63,15 +63,7 @@ To upgrade the All-In-One setup manually:
* Type `exit` to logout the hass user and return to the `pi` user.
-If you deployed Home Assistant via the AiO installer prior to December 2016, you will need to use the following commands:
-
-* Login to Raspberry Pi `ssh pi@your_raspberry_pi_ip`
-* Change to homeassistant user `sudo su -s /bin/bash hass`
-* Change to virtual enviroment `source /srv/hass/hass_venv/bin/activate`
-* Update HA `pip3 install --upgrade homeassistant`
-* Type `exit` to logout the hass user and return to the `pi` user.
-
-
+If you deployed Home Assistant via the AiO installer prior to December 2016, replace `sudo su -s /bin/bash homeassistant` with `sudo su -s /bin/bash hass` and `source /srv/homeassistant/homeassistant_venv/bin/activate` with `source /srv/hass/hass_venv/bin/activate`
To upgrade with fabric:
From d1c59651210d386351f38f6a61c92a2ad4c9591e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?=
Date: Tue, 27 Dec 2016 13:30:19 +0100
Subject: [PATCH 053/123] Fixes copy/paste error in lock.mqtt (#1661)
---
source/_components/lock.mqtt.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/lock.mqtt.markdown b/source/_components/lock.mqtt.markdown
index 89f013b999d..4bec5e48e4d 100644
--- a/source/_components/lock.mqtt.markdown
+++ b/source/_components/lock.mqtt.markdown
@@ -71,7 +71,7 @@ lock:
Keep an eye on ratining messages to keep the state as you don't want to unlock your door by accident when you restart something.
-For a check you can use the command line tools `mosquitto_pub` shipped with `mosquitto` to send MQTT messages. This allows you to operate your cover manually:
+For a check you can use the command line tools `mosquitto_pub` shipped with `mosquitto` to send MQTT messages. This allows you to operate your lock manually:
```bash
$ mosquitto_pub -h 127.0.0.1 -t home-assistant/frontdoor/set -m "LOCK"
From 209e01e5654a0c835ca4c5a956e059acd00c6e5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?=
Date: Tue, 27 Dec 2016 13:31:07 +0100
Subject: [PATCH 054/123] Small doc layout fixes in switch.mqtt (#1660)
---
source/_components/switch.mqtt.markdown | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/source/_components/switch.mqtt.markdown b/source/_components/switch.mqtt.markdown
index 1b87f9639e8..14a1f7e6ba8 100644
--- a/source/_components/switch.mqtt.markdown
+++ b/source/_components/switch.mqtt.markdown
@@ -15,9 +15,9 @@ ha_iot_class: depends
The `mqtt` switch platform let you control your MQTT enabled switch.
-In an ideal scenario, the MQTT device will have a state topic to publish state changes. If these messages are published with RETAIN flag, the MQTT switch will receive an instant state update after subscription and will start with correct state. Otherwise, the initial state of the switch will be false/off.
+In an ideal scenario, the MQTT device will have a `state_topic` to publish state changes. If these messages are published with `RETAIN` flag, the MQTT switch will receive an instant state update after subscription and will start with correct state. Otherwise, the initial state of the switch will be false/off.
-When a state topic is not available, the switch will work in optimistic mode. In this mode, the switch will immediately change state after every command. Otherwise, the switch will wait for state confirmation from device (message from `state_topic`).
+When a `state_topic` is not available, the switch will work in optimistic mode. In this mode, the switch will immediately change state after every command. Otherwise, the switch will wait for state confirmation from device (message from `state_topic`).
Optimistic mode can be forced, even if state topic is available. Try to enable it, if experiencing incorrect switch operation.
@@ -37,7 +37,7 @@ Configuration variables:
- **command_topic** (*Required*): The MQTT topic to publish commands to change the switch state.
- **payload_on** (*Optional*): The payload that represents enabled state. Default is "ON".
- **payload_off** (*Optional*): The payload that represents disabled state. Default is "OFF".
-- **optimistic** (*Optional*): Flag that defines if switch works in optimistic mode. Default is true if no state topic defined, else false.
+- **optimistic** (*Optional*): Flag that defines if switch works in optimistic mode. Default is `true` if no `state_topic` defined, else `false`.
- **qos** (*Optional*): The maximum QoS level of the state topic. Default is 0 and will also be used to publishing messages.
- **retain** (*Optional*): If the published message should have the retain flag on or not.
- **value_template** (*Optional*): Defines a [template](/topics/templating/) to extract a value from the payload.
@@ -68,7 +68,7 @@ switch:
retain: true
```
-For a check you can use the command line tools `mosquitto_pub` shipped with `mosquitto` to send MQTT messages. This allows you to operate your cover manually:
+For a check you can use the command line tools `mosquitto_pub` shipped with `mosquitto` to send MQTT messages. This allows you to operate your switch manually:
```bash
$ mosquitto_pub -h 127.0.0.1 -t home/bedroom/switch1set -m "ON"
From ba3fdbb8e7dc9c788827b565a6e5aff9246ef8c3 Mon Sep 17 00:00:00 2001
From: smolz
Date: Tue, 27 Dec 2016 04:31:30 -0800
Subject: [PATCH 055/123] Update lock.mqtt.markdown (#1659)
---
source/_components/lock.mqtt.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/lock.mqtt.markdown b/source/_components/lock.mqtt.markdown
index 4bec5e48e4d..2705ea61509 100644
--- a/source/_components/lock.mqtt.markdown
+++ b/source/_components/lock.mqtt.markdown
@@ -69,7 +69,7 @@ lock:
value_template: '{% raw %}{{ value.x }}{% endraw %}'
```
-Keep an eye on ratining messages to keep the state as you don't want to unlock your door by accident when you restart something.
+Keep an eye on retaining messages to keep the state as you don't want to unlock your door by accident when you restart something.
For a check you can use the command line tools `mosquitto_pub` shipped with `mosquitto` to send MQTT messages. This allows you to operate your lock manually:
From ec4a528e9a1f45158663ea9669599a1a3ae102d3 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Tue, 27 Dec 2016 20:31:50 +0100
Subject: [PATCH 056/123] Replace 'validate_config' with voluptuous
---
.../python_component_basic_state.markdown | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/source/_cookbook/python_component_basic_state.markdown b/source/_cookbook/python_component_basic_state.markdown
index 1720868ed4d..b2c1e838e56 100644
--- a/source/_cookbook/python_component_basic_state.markdown
+++ b/source/_cookbook/python_component_basic_state.markdown
@@ -99,22 +99,20 @@ hello_state:
text: 'Hello, World!'
```
-Thanks to `DEFAULT_TEXT` variable the component will launch even if no `text:` field is used in the `configuration.yaml` file. Quite often there are variables which are required. It's important to check if all mandatory configuration variables are provided. If not, the setup should fail. We will use the `validate_config` function as a helper to achive this. The next listing shows the essential parts.
+Thanks to `DEFAULT_TEXT` variable the component will launch even if no `text:` field is used in the `configuration.yaml` file. Quite often there are variables which are required. It's important to check if all mandatory configuration variables are provided. If not, the setup should fail. We will use `voluptuous` as a helper to achive this. The next listing shows the essential parts.
```python
-from homeassistant.helpers import validate_config
+import voluptuous as vol
+
+import homeassistant.helpers.config_validation as cv
[...]
- if not validate_config(config, {DOMAIN: [CONF_TEXT]}, _LOGGER):
- return False
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+ vol.Required(CONF_TEXT): cv.string,
+})
```
If `text:` is missing, there will be a warning in the log file.
-```bash
-16-03-12 14:37:37 ERROR (MainThread) [custom_components.hello_state] Missing required configuration items in hello_state: text
-16-03-12 14:37:37 ERROR (MainThread) [homeassistant.bootstrap] component hello_state failed to initialize
-```
-
After a start or a restart of Home Assistant the component will be visible in the frontend if the `configuration.yaml` file is up-to-date.
From 68c7833fe88c81bdd5a734a8b2c03382654d62f3 Mon Sep 17 00:00:00 2001
From: Steven Webb
Date: Thu, 29 Dec 2016 09:24:30 -0500
Subject: [PATCH 057/123] Create configuration_yaml_by_cy1701 (#1667)
* Create configuration_yaml_by_cy1701
my configuration for the cookbook
* Update configuration_yaml_by_cy1701
Corrected format.
---
source/_cookbook/configuration_yaml_by_cy1701 | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 source/_cookbook/configuration_yaml_by_cy1701
diff --git a/source/_cookbook/configuration_yaml_by_cy1701 b/source/_cookbook/configuration_yaml_by_cy1701
new file mode 100644
index 00000000000..783fa134afe
--- /dev/null
+++ b/source/_cookbook/configuration_yaml_by_cy1701
@@ -0,0 +1,10 @@
+layout: page
+title: "Configuration.yaml by cy1701"
+description: ""
+date: 2016-12-28 20:00
+sidebar: true
+comments: false
+sharing: true
+footer: true
+ha_category: Example configuration.yaml
+ha_external_link: https://github.com/cy1701/Home-Assistant-Configuration
From e0cab64772ae987890b4e1c741a15249a16939d2 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Thu, 29 Dec 2016 19:27:22 +0100
Subject: [PATCH 058/123] Fix header
---
source/_cookbook/configuration_yaml_by_cy1701 | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source/_cookbook/configuration_yaml_by_cy1701 b/source/_cookbook/configuration_yaml_by_cy1701
index 783fa134afe..34e1c4cab4a 100644
--- a/source/_cookbook/configuration_yaml_by_cy1701
+++ b/source/_cookbook/configuration_yaml_by_cy1701
@@ -1,3 +1,4 @@
+---
layout: page
title: "Configuration.yaml by cy1701"
description: ""
@@ -8,3 +9,4 @@ sharing: true
footer: true
ha_category: Example configuration.yaml
ha_external_link: https://github.com/cy1701/Home-Assistant-Configuration
+---
From 9283b0eddea2943167cdb0247c574b6ec2f65723 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Thu, 29 Dec 2016 19:50:32 +0100
Subject: [PATCH 059/123] Update /apt/history
---
source/developers/rest_api.markdown | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/source/developers/rest_api.markdown b/source/developers/rest_api.markdown
index 1903af9863d..2bdf49b707b 100644
--- a/source/developers/rest_api.markdown
+++ b/source/developers/rest_api.markdown
@@ -192,7 +192,7 @@ $ curl -X GET -H "x-ha-access: YOUR_PASSWORD" \
-H "Content-Type: application/json" http://localhost:8123/api/services
```
-#### {% linkable_title GET /api/history %}
+#### {% linkable_title GET /api/history/period/<timestamp> %}
Returns an array of state changes in the past. Each object contains further details for the entities.
```json
@@ -227,13 +227,13 @@ Sample `curl` commands:
```bash
$ curl -X GET -H "x-ha-access: YOUR_PASSWORD" \
-H "Content-Type: application/json" \
- http://localhost:8123/api/history/period/2016-02-06
+ http://localhost:8123/api/history/period/2016-12-29T00:00:00+02:00
```
```bash
$ curl -X GET -H "x-ha-access: YOUR_PASSWORD" \
-H "Content-Type: application/json" \
- http://localhost:8123/api/history/period/2016-02-06?filter_entity_id=sensor.temperature
+ http://localhost:8123/api/history/period/2016-12-29T00:00:00+02:00?filter_entity_id=sensor.temperature
```
#### {% linkable_title GET /api/states %}
From ad1cd15cda9a7abda422e6b30c3c469736484cb6 Mon Sep 17 00:00:00 2001
From: linuxlurak
Date: Fri, 30 Dec 2016 13:47:12 +0100
Subject: [PATCH 060/123] Update logger.markdown (#1674)
---
source/_components/logger.markdown | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/source/_components/logger.markdown b/source/_components/logger.markdown
index 3cb7a36d2f5..c6b2bc1072b 100644
--- a/source/_components/logger.markdown
+++ b/source/_components/logger.markdown
@@ -15,7 +15,11 @@ The logger component lets you define the level of logging activities in Home Ass
To enable the logger in your installation, add the following to your `configuration.yaml` file:
-By default log all messages and ignore events lower than critical for specified components.
+To have a full log and log everything only this entry is needed (without any qualifier):
+```yaml
+logger:
+```
+To log all messages and ignore events lower than critical for specified components.
```yaml
# Example configuration.yaml entry
@@ -26,7 +30,7 @@ logger:
homeassistant.components.camera: critical
```
-By default ignore all messages lower than critical and log event for specified components.
+To ignore all messages lower than critical and log event for specified components.
```yaml
# Example configuration.yaml entry
From b62bec2c92bc89980af3aae0d3e81b688bdb66c4 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Fri, 30 Dec 2016 13:57:14 +0100
Subject: [PATCH 061/123] Font size headings
---
source/_components/device_tracker.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/_components/device_tracker.markdown b/source/_components/device_tracker.markdown
index 25691c3fb6d..754e89ef401 100644
--- a/source/_components/device_tracker.markdown
+++ b/source/_components/device_tracker.markdown
@@ -15,7 +15,7 @@ There are also trackers available which use different technologies like [MQTT](/
An [event](/getting-started/automation-trigger/#event-trigger) (`device_tracker_new_device`) will be fired when a device is discovered for the first time.
-# {% linkable_title Configuring a `device_tracker` platform %}
+## {% linkable_title Configuring a `device_tracker` platform %}
To get started add the following lines to your `configuration.yaml` (example for Netgear):
@@ -51,7 +51,7 @@ device_tracker:
Multiple device trackers can be used in parallel, such as [Owntracks](/components/device_tracker.owntracks/#using-owntracks-with-other-device-trackers) and [Nmap](/components/device_tracker.nmap_tracker/). The state of the device will be determined by the source that reported last.
-# {% linkable_title `known_devices.yaml` %}
+## {% linkable_title `known_devices.yaml` %}
Once `device_tracker` is enabled, a file will be created in your config dir named `known_devices.yaml`. Edit this file to adjust which devices to be tracked.
From 96d90ec7fece9834b14ff77d1ae2b53ab611ec3b Mon Sep 17 00:00:00 2001
From: Dennis Sutch
Date: Fri, 30 Dec 2016 09:20:05 -0500
Subject: [PATCH 062/123] Fix link to Manual Installation (#1676)
Link to Manual Installation had incorrect capitalization and when used was not opening page to the Manual Installation linkable title.
---
source/getting-started/installation-raspberry-pi.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/getting-started/installation-raspberry-pi.markdown b/source/getting-started/installation-raspberry-pi.markdown
index c30b177cfcd..0158ec82858 100644
--- a/source/getting-started/installation-raspberry-pi.markdown
+++ b/source/getting-started/installation-raspberry-pi.markdown
@@ -12,7 +12,7 @@ footer: true
### {% linkable_title Installation %}
There's currently three documented ways to install Home Assistant on a Raspberry Pi.
- - [Manual installation](/getting-started/installation-raspberry-pi/#Manual-Installation). Following this guide doing each step manually. This is highly recommended as a first installation since you get a good overview of the installation.
+ - [Manual installation](/getting-started/installation-raspberry-pi/#manual-installation). Following this guide doing each step manually. This is highly recommended as a first installation since you get a good overview of the installation.
- [Hassbian image](/getting-started/installation-raspberry-pi-image). Basic installation with the same settings as following the manual installation guide. Some additional software is preinstalled to make installation quicker and easier. Installation uses `homeassistant` user.
- [All-in-One Installer](/getting-started/installation-raspberry-pi-all-in-one/). Fabric based installation script that installs and compiles many of the things an advanced Home Assistant install is likely to need. Installation uses `homeassistant` user.
From f7a97eb0c9d8a4ad77cd0f2b2ac9fba5a841733a Mon Sep 17 00:00:00 2001
From: chris-thorn
Date: Fri, 30 Dec 2016 19:39:56 +0000
Subject: [PATCH 063/123] Corrected code block formatting for yaml in step 8
(#1678)
---
source/_components/switch.wake_on_lan.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/switch.wake_on_lan.markdown b/source/_components/switch.wake_on_lan.markdown
index bc7e314ccb1..510299accbc 100644
--- a/source/_components/switch.wake_on_lan.markdown
+++ b/source/_components/switch.wake_on_lan.markdown
@@ -51,7 +51,7 @@ from Home Assistant running on another Linux computer (the **server**).
6. On the **target**, we need to let the hass user execute the program needed to suspend/shut down the target computer. I'm using `pm-suspend`, use `poweroff` to turn off the computer. First, get the full path: `which pm-suspend`. On my system, this is `/usr/sbin/pm-suspend`.
7. On the **target**, using an account with sudo access (typically your main account), `sudo visudo`. Add this line last in the file: `hass ALL=NOPASSWD:/usr/sbin/pm-suspend`, where you replace `hass` with the name of your user on the target, if different, and `/usr/sbin/pm-suspend` with the command of your choice, if different.
8. On the **server**, add the following to your configuration, replacing TARGET with the target's name:
-``` yaml
+```yaml
switch:
- platform: wake_on_lan
name: "TARGET"
From aaf4f7b05204ad1a90918fb0bffb4303ab8cfee7 Mon Sep 17 00:00:00 2001
From: MrMep
Date: Fri, 30 Dec 2016 20:40:41 +0100
Subject: [PATCH 064/123] Update keyboard_remote.markdown (#1677)
The option name is "type", not "key_value", see source.
---
source/_components/keyboard_remote.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/_components/keyboard_remote.markdown b/source/_components/keyboard_remote.markdown
index f5fdf73b1e0..62dd5844035 100644
--- a/source/_components/keyboard_remote.markdown
+++ b/source/_components/keyboard_remote.markdown
@@ -24,13 +24,13 @@ The `evdev` package is used to interface with the keyboard and thus this is Linu
# Example configuration.yaml entry
keyboard_remote:
device_descriptor: '/dev/input/by-id/foo'
- key_value: 'key_up'
+ type: 'key_up'
```
Configuration variables:
- **device_descriptor** (*Required*): List of URLS for your feeds.
-- **key_value** (*Required*): Possible values are `key_up`, `key_down`, and `key_hold`. Be careful, `key_hold` will fire a lot of events.
+- **type** (*Required*): Possible values are `key_up`, `key_down`, and `key_hold`. Be careful, `key_hold` will fire a lot of events.
And an automation rule to breathe life into it:
From ce743ec933c304ccd908ab5eebd428f3f2a4cc64 Mon Sep 17 00:00:00 2001
From: chris-thorn
Date: Fri, 30 Dec 2016 19:42:33 +0000
Subject: [PATCH 065/123] Changed "sensor.sab_..." to "sensor.sabnzbd_..."
(#1679)
---
source/_components/sensor.sabnzbd.markdown | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/source/_components/sensor.sabnzbd.markdown b/source/_components/sensor.sabnzbd.markdown
index f769a862920..8b2a7fe0c8c 100644
--- a/source/_components/sensor.sabnzbd.markdown
+++ b/source/_components/sensor.sabnzbd.markdown
@@ -48,15 +48,15 @@ Configuration variables:
- **disk_size**: Disk size of the storage location
- **disk_free**: Free disk space at the storage location
-Note that this will create sensors under the name 'sab' and NOT 'sabnzbd' as follows:
+Note that this will create the following sensors:
```
- - sensor.sab_status
- - sensor.sab_speed
- - sensor.sab_queue
- - sensor.sab_left
- - sensor.sab_disk
- - sensor.sab_disk_free
+ - sensor.sabnzbd_status
+ - sensor.sabnzbd_speed
+ - sensor.sabnzbd_queue
+ - sensor.sabnzbd_left
+ - sensor.sabnzbd_disk
+ - sensor.sabnzbd_disk_free
```
As always, you can determine the names of sensors by looking at the dev-state page `< >` in the web interface.
From 37fa5aa2e1947dbef55d94d0387a4df16272bba5 Mon Sep 17 00:00:00 2001
From: Jim Rollenhagen
Date: Sun, 1 Jan 2017 14:59:00 -0500
Subject: [PATCH 066/123] Fix formatting error in Samsung TV component docs
---
source/_components/media_player.samsungtv.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/media_player.samsungtv.markdown b/source/_components/media_player.samsungtv.markdown
index 0374c23a708..7d7daa2b89b 100644
--- a/source/_components/media_player.samsungtv.markdown
+++ b/source/_components/media_player.samsungtv.markdown
@@ -29,7 +29,7 @@ media_player:
Configuration variables:
-- **host** (*Required*): The IP of the Samsung Smart TV, eg. `192.168.0.10^.
+- **host** (*Required*): The IP of the Samsung Smart TV, eg. `192.168.0.10`.
- **port** (*Optional*): The port of the Samsung Smart TV. Defaults to 55000.
- **name** (*Optional*): The name you would like to give to the Samsung Smart TV.
- **timeout** (*Optional*): The time-out for the communication with the TV. Defaults to 0.
From 174a5019150ab7b14f256990f66d69010c4b4b84 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Tue, 3 Jan 2017 19:33:38 +0100
Subject: [PATCH 067/123] Add script s we no longer host it in the main repo
(fixes #1682)
---
.../autostart-upstart.markdown | 108 +++++++++++++++++-
1 file changed, 107 insertions(+), 1 deletion(-)
diff --git a/source/getting-started/autostart-upstart.markdown b/source/getting-started/autostart-upstart.markdown
index f11daea8cff..3db73e5591f 100644
--- a/source/getting-started/autostart-upstart.markdown
+++ b/source/getting-started/autostart-upstart.markdown
@@ -17,7 +17,113 @@ $ ps -p 1 -o comm=
If the preceding command returns the string `init`, you are likely using Upstart.
-Upstart will launch init scripts that are located in the directory `/etc/init.d/`. A sample init script for systems using Upstart is maintained by this project.
+Upstart will launch init scripts that are located in the directory `/etc/init.d/`. A sample init script for systems using Upstart could look like the sample below.
+
+```bash
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: hass
+# Required-Start: $local_fs $network $named $time $syslog
+# Required-Stop: $local_fs $network $named $time $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Description: Home\ Assistant
+### END INIT INFO
+
+# /etc/init.d Service Script for Home Assistant
+# Created with: https://gist.github.com/naholyr/4275302#file-new-service-sh
+#
+# Installation:
+# 1) If any commands need to run before executing hass (like loading a
+# virutal environment), put them in PRE_EXEC. This command must end with
+# a semicolon.
+# 2) Set RUN_AS to the username that should be used to execute hass.
+# 3) Copy this script to /etc/init.d/
+# sudo cp hass-daemon /etc/init.d/hass-daemon
+# sudo chmod +x /etc/init.d/hass-daemon
+# 4) Register the daemon with Linux
+# sudo update-rc.d hass-daemon defaults
+# 5) Install this service
+# sudo service hass-daemon install
+# 6) Restart Machine
+#
+# After installation, HA should start automatically. If HA does not start,
+# check the log file output for errors.
+# /var/opt/homeassistant/home-assistant.log
+
+PRE_EXEC=""
+RUN_AS="USER"
+PID_FILE="/var/run/hass.pid"
+CONFIG_DIR="/var/opt/homeassistant"
+FLAGS="-v --config $CONFIG_DIR --pid-file $PID_FILE --daemon"
+REDIRECT="> $CONFIG_DIR/home-assistant.log 2>&1"
+
+start() {
+ if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE) 2> /dev/null; then
+ echo 'Service already running' >&2
+ return 1
+ fi
+ echo 'Starting service…' >&2
+ local CMD="$PRE_EXEC hass $FLAGS $REDIRECT;"
+ su -c "$CMD" $RUN_AS
+ echo 'Service started' >&2
+}
+
+stop() {
+ if [ ! -f "$PID_FILE" ] || ! kill -0 $(cat "$PID_FILE") 2> /dev/null; then
+ echo 'Service not running' >&2
+ return 1
+ fi
+ echo 'Stopping service…' >&2
+ kill -3 $(cat "$PID_FILE")
+ while ps -p $(cat "$PID_FILE") > /dev/null 2>&1; do sleep 1;done;
+ echo 'Service stopped' >&2
+}
+
+install() {
+ echo "Installing Home Assistant Daemon (hass-daemon)"
+ echo "999999" > $PID_FILE
+ chown $RUN_AS $PID_FILE
+ mkdir -p $CONFIG_DIR
+ chown $RUN_AS $CONFIG_DIR
+}
+
+uninstall() {
+ echo -n "Are you really sure you want to uninstall this service? That cannot be undone. [yes|No] "
+ local SURE
+ read SURE
+ if [ "$SURE" = "yes" ]; then
+ stop
+ rm -fv "$PID_FILE"
+ echo "Notice: The config directory has not been removed"
+ echo $CONFIG_DIR
+ update-rc.d -f hass-daemon remove
+ rm -fv "$0"
+ echo "Home Assistant Daemon has been removed. Home Assistant is still installed."
+ fi
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ install)
+ install
+ ;;
+ uninstall)
+ uninstall
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|install|uninstall}"
+esac
+```
To install this script, download it, tweak it to you liking, and install it by following the directions in the header. This script will setup Home Assistant to run when the system boots. To start/stop Home Assistant manually, issue the following commands:
From 0b4b22688f9a5491fc5904331bd96852a29c1eb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?=
Date: Tue, 3 Jan 2017 19:41:12 +0100
Subject: [PATCH 068/123] Overriding build-components within subfolders.
(#1705)
It was not explained that you had to create a folder inside `config/custom_components` if the original component was also in a subfolder.
---
source/developers/component_loading.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/developers/component_loading.markdown b/source/developers/component_loading.markdown
index 35b50ee975c..f57148ec8b5 100644
--- a/source/developers/component_loading.markdown
+++ b/source/developers/component_loading.markdown
@@ -17,7 +17,7 @@ A component will be loaded on start if a section (ie. `light:`) for it exists in
Once loaded, a component will only be setup if all dependencies can be loaded and are able to setup. Keep an eye on the logs to see if your component could be loaded and initialized.
-You can override a built-in component by having a component with the same name in your config/custom_components
folder. This is not recommended and will probably break things!
+You can override a built-in component by having a component with the same name in your config/custom_components
folder. If the build-in component is inside a subfolder, take care to place your customization in a folder with the same name in config/custom_components/*folder*
. Note that overriding build-in components is not recommended and will probably break things!
From c48b484265246406828ca2f106e8a6bb4fde6ec8 Mon Sep 17 00:00:00 2001
From: Joeboyc2
Date: Tue, 3 Jan 2017 18:41:51 +0000
Subject: [PATCH 069/123] Updated Configuration variables text (#1704)
The text for the configuration variables was not relevent to the listed variables.
i have updated it in my own words, happy for this to be changed to conform to other documents, are there other variables to be added?
---
source/_components/light.hyperion.markdown | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/_components/light.hyperion.markdown b/source/_components/light.hyperion.markdown
index 576f486f1ac..c2a79f5e95a 100644
--- a/source/_components/light.hyperion.markdown
+++ b/source/_components/light.hyperion.markdown
@@ -24,5 +24,5 @@ light:
Configuration variables:
-- **host** (*Optional*): To enable the automatic addition of lights on startup.
-- **port** (*Optional*): A list of devices with their ip address and a custom name to use in the frontend.
+- **host** (*Optional*): IP Address of the device the Hyperion service is running on.
+- **port** (*Optional*): The Port used to comunicate with the Hyperion service (defualt is 19444).
From a827eb3e15d5637fad9c782e297c6327afdcd091 Mon Sep 17 00:00:00 2001
From: Joeboyc2
Date: Tue, 3 Jan 2017 18:44:52 +0000
Subject: [PATCH 070/123] Final Example was incorrect (#1702)
i have updated the example as this was not working in my config
---
source/_components/sensor.speedtest.markdown | 22 ++++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/source/_components/sensor.speedtest.markdown b/source/_components/sensor.speedtest.markdown
index 4bba84884fa..0727b371e2e 100644
--- a/source/_components/sensor.speedtest.markdown
+++ b/source/_components/sensor.speedtest.markdown
@@ -74,15 +74,15 @@ Everyday at 12:30AM, 6:30AM, 12:30PM, 6:30PM:
```yaml
# Example configuration.yaml entry
sensor:
- platform: speedtest
- minute: 30
- hour:
- - 0
- - 6
- - 12
- - 18
- monitored_conditions:
- - ping
- - download
- - upload
+ - platform: speedtest
+ minute: 30
+ hour:
+ - 0
+ - 6
+ - 12
+ - 18
+ monitored_conditions:
+ - ping
+ - download
+ - upload
```
From a1de0cd920c0204b8d6d7ca8f08905bb510772be Mon Sep 17 00:00:00 2001
From: Huw Davies
Date: Tue, 3 Jan 2017 18:48:17 +0000
Subject: [PATCH 071/123] Renamed service in description (#1695)
Units variable description still referred to forecast.io rather than Dark Sky
---
source/_components/sensor.darksky.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/sensor.darksky.markdown b/source/_components/sensor.darksky.markdown
index b878a599d74..153c29cb12f 100644
--- a/source/_components/sensor.darksky.markdown
+++ b/source/_components/sensor.darksky.markdown
@@ -84,7 +84,7 @@ Configuration variables:
- **apparent_temperature_min**: Today's expected apparent low temperature.
- **precip_intensity_max**: Today's expected maximum intensity of precipitation.
- **units** (*Optional*): Specify the unit system. Default to `si` or `us` based on the temperature preference in Home Assistant. Other options are `auto`, `us`, `si`, `ca`, and `uk2`.
-`auto` will let forecast.io decide the unit system based on location.
+`auto` will let Dark Sky decide the unit system based on location.
- **update_inverval** (*Optional*): Minimum time interval between updates. Default is 2 minutes. Supported formats:
- `update_interval: 'HH:MM:SS'`
- `update_interval: 'HH:MM'`
From 07ee743baa929e15142156d4a6d260aed948e498 Mon Sep 17 00:00:00 2001
From: Georgi Kirichkov
Date: Tue, 3 Jan 2017 20:49:13 +0200
Subject: [PATCH 072/123] Updates alarm_control_panel Example (#1688)
The example was out of date using 'platform: state' in the condition, instead of 'condition: state'
---
source/_components/alarm_control_panel.manual.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/alarm_control_panel.manual.markdown b/source/_components/alarm_control_panel.manual.markdown
index fd1807019fd..e500f2603ec 100644
--- a/source/_components/alarm_control_panel.manual.markdown
+++ b/source/_components/alarm_control_panel.manual.markdown
@@ -54,7 +54,7 @@ automation:
entity_id: sensor.window
state: 'open'
condition:
- - platform: state
+ - condition: state
entity_id: alarm_control_panel.ha_alarm
state: armed_away
action:
From 3ed332e8b31990ef075c4287c87f11770e52afb6 Mon Sep 17 00:00:00 2001
From: LightIsLife
Date: Tue, 3 Jan 2017 19:50:53 +0100
Subject: [PATCH 073/123] AiO install requires "pip install lxml" (#1686)
In addition to the added packages, it is necessary to install lxml within the virtual environment if AiO install has been chosen (I did not verify with other installs). Otherwise HASS will not start and not give any error messages when device_tracker with Fritz is used.
---
source/_components/device_tracker.fritz.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/device_tracker.fritz.markdown b/source/_components/device_tracker.fritz.markdown
index fa7ae119426..40d7bfa985d 100644
--- a/source/_components/device_tracker.fritz.markdown
+++ b/source/_components/device_tracker.fritz.markdown
@@ -17,7 +17,7 @@ The `fritz` platform offers presence detection by looking at connected devices t
It might be necessary to install additional packages: $ sudo apt-get install libxslt-dev libxml2-dev python3-lxml
-
+If you are working with the All-in-One installation, you may also need to execute also within your virtual environment the command pip install lxml
; be patient this will take a while.
To use an Fritz!Box router in your installation, add the following to your `configuration.yaml` file:
From 6f31eef44dfe34c9d94bce8911571746f966a8d7 Mon Sep 17 00:00:00 2001
From: Eric Thompson
Date: Tue, 3 Jan 2017 11:10:50 -0800
Subject: [PATCH 074/123] fix "Sonos" typo in discovery docs (#1684)
---
source/_components/discovery.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/_components/discovery.markdown b/source/_components/discovery.markdown
index 276571e3f81..4a97acd53e4 100644
--- a/source/_components/discovery.markdown
+++ b/source/_components/discovery.markdown
@@ -21,7 +21,7 @@ Home Assistant can discover and automatically configure zeroconf/mDNS and uPnP d
* Plex media server
* Panasonic Viera
* Roku media player
- * Sono Speaker
+ * Sonos Speakers
* Yamaha media player
* Logitech media server (Squeezebox)
* DirecTV
From 04e111372d89bcd2f527817004d4ab1d83e7257f Mon Sep 17 00:00:00 2001
From: Brian J King
Date: Tue, 3 Jan 2017 13:11:48 -0600
Subject: [PATCH 075/123] Fixing broken formatting on Alexa component page
(#1685)
Fixes broken formatting on [Alexa Component Page](https://home-assistant.io/components/alexa/) flash briefing skill
---
source/_components/alexa.markdown | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/source/_components/alexa.markdown b/source/_components/alexa.markdown
index 1c32f9d02fd..db6115b352b 100644
--- a/source/_components/alexa.markdown
+++ b/source/_components/alexa.markdown
@@ -355,7 +355,7 @@ Please refer to the [Amazon documentation][flash-briefing-api-docs] for more inf
- Hit "Next"
- Test
- Having passed all validations to reach this screen you can now click on "< Back to All Skills" as your flash briefing is now available as in "Development" service.
-- To invoke your flash briefing, open the Alexa app on your phone or go to [Alexa|http://alexa.amazon.com/], open the "Skills" configuration section, select "Your Skills", scroll to the bottom, tap on the Flash Briefing Skill you just created, enable it, then manage Flash Briefing and adjust ordering as necessary. Finally ask your Echo for your "news","flash briefing", or "briefing".
+- To invoke your flash briefing, open the Alexa app on your phone or go to the [Alexa Setings Site][alexa-settings-site], open the "Skills" configuration section, select "Your Skills", scroll to the bottom, tap on the Flash Briefing Skill you just created, enable it, then manage Flash Briefing and adjust ordering as necessary. Finally ask your Echo for your "news","flash briefing", or "briefing".
[amazon-dev-console]: https://developer.amazon.com
[flash-briefing-api]: https://developer.amazon.com/alexa-skills-kit/flash-briefing
@@ -364,3 +364,4 @@ Please refer to the [Amazon documentation][flash-briefing-api-docs] for more inf
[small-icon]: /images/components/alexa/alexa-108x108.png
[templates]: /topics/templating/
[zero-three-one]: /blog/2016/10/22/flash-briefing-updater-hacktoberfest/
+[alexa-settings-site]: http://alexa.amazon.com/
From 51ade405ac95251f1373e32e224ab11b41f91d04 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 3 Jan 2017 22:52:44 +0100
Subject: [PATCH 076/123] Add bruh tree post
---
...3-control-my-christmas-tree-stats.markdown | 44 ++++++++++++++++++
.../2017-01-bruh-christmas/christmas-tree.gif | Bin 0 -> 4007019 bytes
.../blog/2017-01-bruh-christmas/stats.png | Bin 0 -> 18828 bytes
3 files changed, 44 insertions(+)
create mode 100644 source/_posts/2017-01-03-control-my-christmas-tree-stats.markdown
create mode 100644 source/images/blog/2017-01-bruh-christmas/christmas-tree.gif
create mode 100644 source/images/blog/2017-01-bruh-christmas/stats.png
diff --git a/source/_posts/2017-01-03-control-my-christmas-tree-stats.markdown b/source/_posts/2017-01-03-control-my-christmas-tree-stats.markdown
new file mode 100644
index 00000000000..8303520eedd
--- /dev/null
+++ b/source/_posts/2017-01-03-control-my-christmas-tree-stats.markdown
@@ -0,0 +1,44 @@
+---
+layout: post
+title: "Control My Christmas Tree Stats"
+description: "I used Home Assistant for a publicly controllable Christmas tree and it worked great!"
+date: 2017-01-03 18:00:00 +0000
+date_formatted: "January 3, 2017"
+author: Ben
+author_twitter: bruhautomation
+comments: true
+categories: community
+og_image: /images/blog/2017-01-bruh-christmas/christmas-tree.gif
+---
+
+Hello and Happy New Year!
+
+I am not Paulus. My name is Ben. I’m the creator of the [BRUH Automation YouTube channel][bruh]. If you’ve ever seen any of my videos then you’ll know I love home automation and Home Assistant.
+
+I wanted to share some exciting stats from one of my latest projects - [Control My Christmas tree!](https://github.com/bruhautomation/BRUH-Christmas-Tree-2016) For this project, I created a Home Assistant instance on a Raspberry Pi 2 that was publically accessible via DuckDNS. Paulus was great in helping me disable several of the developer services that could have been exploited to disable the Home Assistant instance.
+
+I added three devices to the Home Assistant instance - a Wemo Insight, Sonoff Switch (running MQTT firmware), and a DIY MQTT Digital LED strip. After adding a few 3D printed Star War decorations, the tree was ready to go!
+
+
+
+ The Christmas tree in action.
+
+
+During the time by tree was set up, I had 7,366 visitors from 88 countries connect to my Home Assistant instance. This generated an estimated 100,000 clicks on the switches in my Home Assistant instance - thanks in part to some folks who posted up with macros for 6-8 hour stretches.
+
+
+
+ View counts of the video.
+
+
+I’m very excited to report that Home Assistant didn’t crash a single time. In fact, it didn’t even bog down or need to be restarted. The Wemo Insight and Sonoff relays were incredibly resilient, as well. I only had one Sonoff failure and two Wemo Insight failures both of which were resolved by power cycling them. The digital LED strips also fared pretty well, hanging up only twice, aside from a loose connector.
+
+I would like to send a big thank you to the Home Assistant developers who have worked so hard on making Home Assistant run so well. This project is a testament to your hard work on making Home Assistant one of the best home automation platforms out there. Thank you!
+
+I’m looking forward to continuing my home automation adventures with Home Assistant and I can’t wait for next Christmas with an even bigger and badder tree. :)
+
+Wishing everyone the best in 2017!
+
+Cheers! -Ben
+
+[bruh]: https://www.youtube.com/channel/UCLecVrux63S6aYiErxdiy4w
diff --git a/source/images/blog/2017-01-bruh-christmas/christmas-tree.gif b/source/images/blog/2017-01-bruh-christmas/christmas-tree.gif
new file mode 100644
index 0000000000000000000000000000000000000000..28b6e2b19ba89a0cdb5f884b7954539a57dca7e1
GIT binary patch
literal 4007019
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