From a5fda6c17f5c9b8d16625d45b215a2ec1c779d48 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 18 Dec 2016 10:40:27 +0100 Subject: [PATCH] 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.