mirror of
https://github.com/home-assistant/home-assistant.io.git
synced 2025-07-23 17:27:19 +00:00
Merge branch 'current' into next
This commit is contained in:
commit
86ae27b9ff
@ -101,8 +101,8 @@ social:
|
|||||||
# Home Assistant release details
|
# Home Assistant release details
|
||||||
current_major_version: 0
|
current_major_version: 0
|
||||||
current_minor_version: 103
|
current_minor_version: 103
|
||||||
current_patch_version: 5
|
current_patch_version: 6
|
||||||
date_released: 2019-12-28
|
date_released: 2020-01-06
|
||||||
|
|
||||||
# Either # or the anchor link to latest release notes in the blog post.
|
# Either # or the anchor link to latest release notes in the blog post.
|
||||||
# Must be prefixed with a # and have double quotes around it.
|
# Must be prefixed with a # and have double quotes around it.
|
||||||
|
@ -42,15 +42,15 @@ That's it. You should no be able to use the Google Voice assistant.
|
|||||||
|
|
||||||
{% configuration %}
|
{% configuration %}
|
||||||
client_secrets:
|
client_secrets:
|
||||||
description: The file downloaded from the [Google Actions Console][GActionsConsole], you can redownload them under the "Develop - Device registration" tab. By default the add-on look in the "hassio/share" folder.
|
description: The file downloaded from the [Google Actions Console](https://console.actions.google.com/), you can redownload them under the "Develop - Device registration" tab. By default the add-on look in the "hassio/share" folder.
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
project_id:
|
project_id:
|
||||||
description: The project id can be found in your "google_assistant.json" file or under project settings in the [Google Actions Console][GActionsConsole].
|
description: The project id can be found in your "google_assistant.json" file or under project settings in the [Google Actions Console](https://console.actions.google.com/).
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
model_id:
|
model_id:
|
||||||
description: The model id can also be found under the "Develop - Device registration tab" in the [Google Actions Console][GActionsConsole].
|
description: The model id can also be found under the "Develop - Device registration tab" in the [Google Actions Console](https://console.actions.google.com/).
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
{% endconfiguration %}
|
{% endconfiguration %}
|
||||||
|
@ -41,12 +41,20 @@ import voluptuous as vol
|
|||||||
import homeassistant.components as core
|
import homeassistant.components as core
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.components import device_tracker, light
|
from homeassistant.components import device_tracker, light
|
||||||
from homeassistant.const import (ATTR_ENTITY_ID, SERVICE_TURN_OFF,
|
from homeassistant.const import (
|
||||||
SERVICE_TURN_ON, STATE_HOME, STATE_NOT_HOME,
|
ATTR_ENTITY_ID,
|
||||||
STATE_OFF, STATE_ON)
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
STATE_HOME,
|
||||||
|
STATE_NOT_HOME,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
)
|
||||||
from homeassistant.core import split_entity_id
|
from homeassistant.core import split_entity_id
|
||||||
from homeassistant.helpers.event import (async_track_state_change,
|
from homeassistant.helpers.event import (
|
||||||
async_track_time_change)
|
async_track_state_change,
|
||||||
|
async_track_time_change,
|
||||||
|
)
|
||||||
|
|
||||||
# The domain of your component. Should be equal to the name of your component.
|
# The domain of your component. Should be equal to the name of your component.
|
||||||
DOMAIN = "example"
|
DOMAIN = "example"
|
||||||
@ -54,26 +62,26 @@ DOMAIN = "example"
|
|||||||
# List of integration names (string) your integration depends upon.
|
# List of integration names (string) your integration depends upon.
|
||||||
# We depend on group because group will be loaded after all the integrations that
|
# We depend on group because group will be loaded after all the integrations that
|
||||||
# initialize devices have been setup.
|
# initialize devices have been setup.
|
||||||
DEPENDENCIES = ['group', 'device_tracker', 'light']
|
DEPENDENCIES = ["group", "device_tracker", "light"]
|
||||||
|
|
||||||
# Configuration key for the entity id we are targeting.
|
# Configuration key for the entity id we are targeting.
|
||||||
CONF_TARGET = 'target'
|
CONF_TARGET = "target"
|
||||||
|
|
||||||
# Variable for storing configuration parameters.
|
# Variable for storing configuration parameters.
|
||||||
TARGET_ID = None
|
TARGET_ID = None
|
||||||
|
|
||||||
# Name of the service that we expose.
|
# Name of the service that we expose.
|
||||||
SERVICE_FLASH = 'flash'
|
SERVICE_FLASH = "flash"
|
||||||
|
|
||||||
# Shortcut for the logger
|
# Shortcut for the logger
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Validate that all required config options are given.
|
# Validate that all required config options are given.
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
DOMAIN: vol.Schema({
|
{DOMAIN: vol.Schema({vol.Optional(CONF_TARGET): cv.entity_id})},
|
||||||
vol.Optional(CONF_TARGET): cv.entity_id
|
extra=vol.ALLOW_EXTRA,
|
||||||
})
|
)
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Setup example component."""
|
"""Setup example component."""
|
||||||
@ -84,8 +92,7 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
# Validate that the target entity id exists.
|
# Validate that the target entity id exists.
|
||||||
if hass.states.get(TARGET_ID) is None:
|
if hass.states.get(TARGET_ID) is None:
|
||||||
_LOGGER.error("Target entity id %s does not exist",
|
_LOGGER.error("Target entity id %s does not exist", TARGET_ID)
|
||||||
TARGET_ID)
|
|
||||||
|
|
||||||
# Tell the bootstrapper that we failed to initialize and clear the
|
# Tell the bootstrapper that we failed to initialize and clear the
|
||||||
# stored target id so our functions don't run.
|
# stored target id so our functions don't run.
|
||||||
@ -120,7 +127,7 @@ async def async_setup(hass, config):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if device_tracker.is_on(hass) and not core.is_on(hass, TARGET_ID):
|
if device_tracker.is_on(hass) and not core.is_on(hass, TARGET_ID):
|
||||||
_LOGGER.info('People home at 7AM, turning target on')
|
_LOGGER.info("People home at 7AM, turning target on")
|
||||||
await hass.services.async_call(domain, SERVICE_TURN_ON, data)
|
await hass.services.async_call(domain, SERVICE_TURN_ON, data)
|
||||||
|
|
||||||
async def async_flash_service(service):
|
async def async_flash_service(service):
|
||||||
@ -133,7 +140,9 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
if core.is_on(hass, TARGET_ID):
|
if core.is_on(hass, TARGET_ID):
|
||||||
# We need this call to run blocking, as we want to wait 10s after it finished
|
# We need this call to run blocking, as we want to wait 10s after it finished
|
||||||
await hass.services.async_call(domain, SERVICE_TURN_OFF, data, blocking=True)
|
await hass.services.async_call(
|
||||||
|
domain, SERVICE_TURN_OFF, data, blocking=True
|
||||||
|
)
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
await hass.services.async_call(domain, SERVICE_TURN_ON, data)
|
await hass.services.async_call(domain, SERVICE_TURN_ON, data)
|
||||||
else:
|
else:
|
||||||
@ -146,15 +155,26 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
# If all lights turn off, turn off.
|
# If all lights turn off, turn off.
|
||||||
async_track_state_change(
|
async_track_state_change(
|
||||||
hass, light.ENTITY_ID_ALL_LIGHTS, async_switch_off, STATE_ON, STATE_OFF)
|
hass, light.ENTITY_ID_ALL_LIGHTS, async_switch_off, STATE_ON, STATE_OFF
|
||||||
|
)
|
||||||
|
|
||||||
# If all people leave the house and the entity is on, turn it off.
|
# If all people leave the house and the entity is on, turn it off.
|
||||||
async_track_state_change(
|
async_track_state_change(
|
||||||
hass, device_tracker.ENTITY_ID_ALL_DEVICES, async_switch_off, STATE_HOME, STATE_NOT_HOME)
|
hass,
|
||||||
|
device_tracker.ENTITY_ID_ALL_DEVICES,
|
||||||
|
async_switch_off,
|
||||||
|
STATE_HOME,
|
||||||
|
STATE_NOT_HOME,
|
||||||
|
)
|
||||||
|
|
||||||
# If anyone comes home and the entity is not on, turn it on.
|
# If anyone comes home and the entity is not on, turn it on.
|
||||||
async_track_state_change(
|
async_track_state_change(
|
||||||
hass, device_tracker.ENTITY_ID_ALL_DEVICES, async_switch_on, STATE_NOT_HOME, STATE_HOME)
|
hass,
|
||||||
|
device_tracker.ENTITY_ID_ALL_DEVICES,
|
||||||
|
async_switch_on,
|
||||||
|
STATE_NOT_HOME,
|
||||||
|
STATE_HOME,
|
||||||
|
)
|
||||||
|
|
||||||
# Call wakeup callback at 7 AM
|
# Call wakeup callback at 7 AM
|
||||||
async_track_time_change(hass, async_wake_up, hour=7, minute=00, second=00)
|
async_track_time_change(hass, async_wake_up, hour=7, minute=00, second=00)
|
||||||
|
@ -18,21 +18,21 @@ This example follows a topic on MQTT and updates the state of an entity to the l
|
|||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
|
|
||||||
# The domain of your component. Should be equal to the name of your component.
|
# The domain of your component. Should be equal to the name of your component.
|
||||||
DOMAIN = 'hello_mqtt'
|
DOMAIN = "hello_mqtt"
|
||||||
|
|
||||||
# List of integration names (string) your integration depends upon.
|
# List of integration names (string) your integration depends upon.
|
||||||
DEPENDENCIES = ['mqtt']
|
DEPENDENCIES = ["mqtt"]
|
||||||
|
|
||||||
|
|
||||||
CONF_TOPIC = 'topic'
|
CONF_TOPIC = "topic"
|
||||||
DEFAULT_TOPIC = 'home-assistant/hello_mqtt'
|
DEFAULT_TOPIC = "home-assistant/hello_mqtt"
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Set up the Hello MQTT component."""
|
"""Set up the Hello MQTT component."""
|
||||||
mqtt = hass.components.mqtt
|
mqtt = hass.components.mqtt
|
||||||
topic = config[DOMAIN].get(CONF_TOPIC, DEFAULT_TOPIC)
|
topic = config[DOMAIN].get(CONF_TOPIC, DEFAULT_TOPIC)
|
||||||
entity_id = 'hello_mqtt.last_message'
|
entity_id = "hello_mqtt.last_message"
|
||||||
|
|
||||||
# Listener to be called when we receive a message.
|
# Listener to be called when we receive a message.
|
||||||
# The msg parameter is a Message object with the following members:
|
# The msg parameter is a Message object with the following members:
|
||||||
@ -45,15 +45,15 @@ def setup(hass, config):
|
|||||||
mqtt.subscribe(topic, message_received)
|
mqtt.subscribe(topic, message_received)
|
||||||
|
|
||||||
# Set the initial state.
|
# Set the initial state.
|
||||||
hass.states.set(entity_id, 'No messages')
|
hass.states.set(entity_id, "No messages")
|
||||||
|
|
||||||
# Service to publish a message on MQTT.
|
# Service to publish a message on MQTT.
|
||||||
def set_state_service(call):
|
def set_state_service(call):
|
||||||
"""Service to send a message."""
|
"""Service to send a message."""
|
||||||
mqtt.publish(topic, call.data.get('new_state'))
|
mqtt.publish(topic, call.data.get("new_state"))
|
||||||
|
|
||||||
# Register our service with Home Assistant.
|
# Register our service with Home Assistant.
|
||||||
hass.services.register(DOMAIN, 'set_state', set_state_service)
|
hass.services.register(DOMAIN, "set_state", set_state_service)
|
||||||
|
|
||||||
# Return boolean to indicate that initialization was successfully.
|
# Return boolean to indicate that initialization was successfully.
|
||||||
return True
|
return True
|
||||||
|
@ -43,19 +43,19 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
DOMAIN = 'simple_alarm"'
|
DOMAIN = 'simple_alarm"'
|
||||||
|
|
||||||
DEPENDENCIES = ['group', 'device_tracker', 'light']
|
DEPENDENCIES = ["group", "device_tracker", "light"]
|
||||||
|
|
||||||
# Attribute to tell which light has to flash when a known person comes home
|
# Attribute to tell which light has to flash when a known person comes home
|
||||||
# If omitted will flash all.
|
# If omitted will flash all.
|
||||||
CONF_KNOWN_LIGHT = 'known_light'
|
CONF_KNOWN_LIGHT = "known_light"
|
||||||
|
|
||||||
# Attribute to tell which light has to flash when an unknown person comes home
|
# Attribute to tell which light has to flash when an unknown person comes home
|
||||||
# If omitted will flash all.
|
# If omitted will flash all.
|
||||||
CONF_UNKNOWN_LIGHT = 'unknown_light'
|
CONF_UNKNOWN_LIGHT = "unknown_light"
|
||||||
|
|
||||||
# Services to test the alarms
|
# Services to test the alarms
|
||||||
SERVICE_TEST_KNOWN_ALARM = 'test_known'
|
SERVICE_TEST_KNOWN_ALARM = "test_known"
|
||||||
SERVICE_TEST_UNKNOWN_ALARM = 'test_unknown'
|
SERVICE_TEST_UNKNOWN_ALARM = "test_unknown"
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
@ -66,8 +66,7 @@ def setup(hass, config):
|
|||||||
light_id = config[DOMAIN].get(conf_key, light.ENTITY_ID_ALL_LIGHTS)
|
light_id = config[DOMAIN].get(conf_key, light.ENTITY_ID_ALL_LIGHTS)
|
||||||
|
|
||||||
if hass.states.get(light_id) is None:
|
if hass.states.get(light_id) is None:
|
||||||
_LOGGER.error(
|
_LOGGER.error("Light id %s could not be found in state machine", light_id)
|
||||||
"Light id %s could not be found in state machine", light_id)
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -88,18 +87,19 @@ def setup(hass, config):
|
|||||||
def unknown_alarm():
|
def unknown_alarm():
|
||||||
""" Fire an alarm if the light turns on while no one is home. """
|
""" Fire an alarm if the light turns on while no one is home. """
|
||||||
light.turn_on(
|
light.turn_on(
|
||||||
hass, unknown_light_id,
|
hass, unknown_light_id, flash=light.FLASH_LONG, rgb_color=[255, 0, 0]
|
||||||
flash=light.FLASH_LONG, rgb_color=[255, 0, 0])
|
)
|
||||||
|
|
||||||
# Send a message to the user
|
# Send a message to the user
|
||||||
notify.send_message(
|
notify.send_message(
|
||||||
hass, "The lights just got turned on while no one was home.")
|
hass, "The lights just got turned on while no one was home."
|
||||||
|
)
|
||||||
|
|
||||||
# Setup services to test the effect
|
# Setup services to test the effect
|
||||||
|
hass.services.register(DOMAIN, SERVICE_TEST_KNOWN_ALARM, lambda call: known_alarm())
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_TEST_KNOWN_ALARM, lambda call: known_alarm())
|
DOMAIN, SERVICE_TEST_UNKNOWN_ALARM, lambda call: unknown_alarm()
|
||||||
hass.services.register(
|
)
|
||||||
DOMAIN, SERVICE_TEST_UNKNOWN_ALARM, lambda call: unknown_alarm())
|
|
||||||
|
|
||||||
def unknown_alarm_if_lights_on(entity_id, old_state, new_state):
|
def unknown_alarm_if_lights_on(entity_id, old_state, new_state):
|
||||||
"""Called when a light has been turned on."""
|
"""Called when a light has been turned on."""
|
||||||
@ -107,8 +107,12 @@ def setup(hass, config):
|
|||||||
unknown_alarm()
|
unknown_alarm()
|
||||||
|
|
||||||
track_state_change(
|
track_state_change(
|
||||||
hass, light.ENTITY_ID_ALL_LIGHTS,
|
hass,
|
||||||
unknown_alarm_if_lights_on, STATE_OFF, STATE_ON)
|
light.ENTITY_ID_ALL_LIGHTS,
|
||||||
|
unknown_alarm_if_lights_on,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
)
|
||||||
|
|
||||||
def ring_known_alarm(entity_id, old_state, new_state):
|
def ring_known_alarm(entity_id, old_state, new_state):
|
||||||
"""Called when a known person comes home."""
|
"""Called when a known person comes home."""
|
||||||
@ -117,8 +121,12 @@ def setup(hass, config):
|
|||||||
|
|
||||||
# Track home coming of each device
|
# Track home coming of each device
|
||||||
track_state_change(
|
track_state_change(
|
||||||
hass, hass.states.entity_ids(device_tracker.DOMAIN),
|
hass,
|
||||||
ring_known_alarm, STATE_NOT_HOME, STATE_HOME)
|
hass.states.entity_ids(device_tracker.DOMAIN),
|
||||||
|
ring_known_alarm,
|
||||||
|
STATE_NOT_HOME,
|
||||||
|
STATE_HOME,
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
```
|
```
|
||||||
|
@ -59,33 +59,39 @@ One of the most common uses cases are to show groups during certain times of day
|
|||||||
|
|
||||||
from datetime import time, datetime
|
from datetime import time, datetime
|
||||||
|
|
||||||
|
|
||||||
def mk_occasion(name, start, end, days=None):
|
def mk_occasion(name, start, end, days=None):
|
||||||
s = start.split(':')
|
s = start.split(":")
|
||||||
e = end.split(':')
|
e = end.split(":")
|
||||||
return {'name' : name,
|
return {
|
||||||
'start': time(int(s[0]), int(s[1]), int(s[2])),
|
"name": name,
|
||||||
'end' : time(int(e[0]), int(e[1]), int(e[2])),
|
"start": time(int(s[0]), int(s[1]), int(s[2])),
|
||||||
'days' : days}
|
"end": time(int(e[0]), int(e[1]), int(e[2])),
|
||||||
|
"days": days,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Matching is done from top to bottom
|
# Matching is done from top to bottom
|
||||||
OCCASIONS = [
|
OCCASIONS = [
|
||||||
# More specific occasions
|
# More specific occasions
|
||||||
mk_occasion('work_morning', '06:00:00', '07:10:00', range(5)),
|
mk_occasion("work_morning", "06:00:00", "07:10:00", range(5)),
|
||||||
|
|
||||||
# General matching
|
# General matching
|
||||||
mk_occasion('weekday', '00:00:00', '23:59:59', range(5)),
|
mk_occasion("weekday", "00:00:00", "23:59:59", range(5)),
|
||||||
mk_occasion('weekend', '00:00:00', '23:59:59', [5, 6])
|
mk_occasion("weekend", "00:00:00", "23:59:59", [5, 6]),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_current_occasion(occasion_list, default_occasion='normal'):
|
|
||||||
|
def get_current_occasion(occasion_list, default_occasion="normal"):
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
for occasion in OCCASIONS:
|
for occasion in OCCASIONS:
|
||||||
if occasion['start'] <= now.time() <= occasion['end'] and \
|
if occasion["start"] <= now.time() <= occasion["end"] and (
|
||||||
(occasion['days'] is None or now.weekday() in occasion['days']):
|
occasion["days"] is None or now.weekday() in occasion["days"]
|
||||||
return occasion['name']
|
):
|
||||||
|
return occasion["name"]
|
||||||
return default_occasion
|
return default_occasion
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
print(get_current_occasion(OCCASIONS))
|
print(get_current_occasion(OCCASIONS))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -31,18 +31,17 @@ Let's start with a simple App to turn a light on every night at sunset and off e
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
|
|
||||||
class OutsideLights(hass.Hass):
|
class OutsideLights(hass.Hass):
|
||||||
|
def initialize(self):
|
||||||
|
self.run_at_sunrise(self.sunrise_cb)
|
||||||
|
self.run_at_sunset(self.sunset_cb)
|
||||||
|
|
||||||
def initialize(self):
|
def sunrise_cb(self, kwargs):
|
||||||
self.run_at_sunrise(self.sunrise_cb)
|
self.turn_on(self.args["off_scene"])
|
||||||
self.run_at_sunset(self.sunset_cb)
|
|
||||||
|
|
||||||
def sunrise_cb(self, kwargs):
|
|
||||||
self.turn_on(self.args["off_scene"])
|
|
||||||
|
|
||||||
def sunset_cb(self, kwargs):
|
|
||||||
self.turn_on(self.args["on_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.
|
This is also fairly easy to achieve with Home Assistant automations, but we are just getting started.
|
||||||
@ -54,18 +53,18 @@ Our next example is to turn on a light when motion is detected and it is dark, a
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
class FlashyMotionLights(hass.Hass):
|
|
||||||
|
|
||||||
def initialize(self):
|
class FlashyMotionLights(hass.Hass):
|
||||||
self.listen_state(self.motion, "binary_sensor.drive", new="on")
|
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():
|
def motion(self, entity, attribute, old, new, kwargs):
|
||||||
self.turn_on("light.drive")
|
if self.sun_down():
|
||||||
self.run_in(self.light_off, 60)
|
self.turn_on("light.drive")
|
||||||
|
self.run_in(self.light_off, 60)
|
||||||
def light_off(self, kwargs):
|
|
||||||
self.turn_off("light.drive")
|
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.
|
This is starting to get a little more complex in Home Assistant automations, requiring an automation rule and two separate scripts.
|
||||||
@ -75,26 +74,26 @@ Now let's extend this with a somewhat artificial example to show something that
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
class MotionLights(hass.Hass):
|
|
||||||
|
|
||||||
def initialize(self):
|
class MotionLights(hass.Hass):
|
||||||
self.listen_state(self.motion, "binary_sensor.drive", new="on")
|
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():
|
def motion(self, entity, attribute, old, new, kwargs):
|
||||||
self.turn_on("light.drive")
|
if self.self.sun_down():
|
||||||
self.run_in(self.light_off, 60)
|
self.turn_on("light.drive")
|
||||||
self.flashcount = 0
|
self.run_in(self.light_off, 60)
|
||||||
self.run_in(self.flash_warning, 1)
|
self.flashcount = 0
|
||||||
|
self.run_in(self.flash_warning, 1)
|
||||||
def light_off(self, kwargs):
|
|
||||||
self.turn_off("light.drive")
|
def light_off(self, kwargs):
|
||||||
|
self.turn_off("light.drive")
|
||||||
def flash_warning(self, kwargs):
|
|
||||||
self.toggle("light.living_room")
|
def flash_warning(self, kwargs):
|
||||||
self.flashcount += 1
|
self.toggle("light.living_room")
|
||||||
if self.flashcount < 10:
|
self.flashcount += 1
|
||||||
self.run_in(self.flash_warning, 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.
|
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.
|
||||||
|
@ -13,13 +13,16 @@ The first step is to create a unique file within the apps directory (as defined
|
|||||||
```python
|
```python
|
||||||
import homeassistant.appapi as appapi
|
import homeassistant.appapi as appapi
|
||||||
|
|
||||||
|
|
||||||
class MotionLights(appapi.AppDaemon):
|
class MotionLights(appapi.AppDaemon):
|
||||||
|
"""Motion lights implementation."""
|
||||||
```
|
```
|
||||||
|
|
||||||
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 its `initialize()` function - this function must appear as part of every app:
|
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 its `initialize()` function - this function must appear as part of every app:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
|
"""Perform initialization."""
|
||||||
```
|
```
|
||||||
|
|
||||||
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 its callbacks are activated.
|
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 its callbacks are activated.
|
||||||
@ -50,17 +53,17 @@ import datetime
|
|||||||
|
|
||||||
# Declare Class
|
# Declare Class
|
||||||
class NightLight(appapi.AppDaemon):
|
class NightLight(appapi.AppDaemon):
|
||||||
#initialize() function which will be called at startup and reload
|
# initialize() function which will be called at startup and reload
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
# Create a time object for 7pm
|
# Create a time object for 7pm
|
||||||
time = datetime.time(19, 00, 0)
|
time = datetime.time(19, 00, 0)
|
||||||
# Schedule a daily callback that will call run_daily() at 7pm every night
|
# Schedule a daily callback that will call run_daily() at 7pm every night
|
||||||
self.run_daily(self.run_daily_callback, time)
|
self.run_daily(self.run_daily_callback, time)
|
||||||
|
|
||||||
# Our callback function will be called by the scheduler every day at 7pm
|
# Our callback function will be called by the scheduler every day at 7pm
|
||||||
def run_daily_callback(self, kwargs):
|
def run_daily_callback(self, kwargs):
|
||||||
# Call to Home Assistant to turn the porch light on
|
# Call to Home Assistant to turn the porch light on
|
||||||
self.turn_on("light.porch")
|
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!
|
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!
|
||||||
@ -273,7 +276,7 @@ In most cases, the attribute `state` has the most important value in it, e.g., f
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
get_state(entity = None, attribute = None)
|
get_state(entity=None, attribute=None)
|
||||||
```
|
```
|
||||||
|
|
||||||
`get_state()` is used to query the state of any integration 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.
|
`get_state()` is used to query the state of any integration 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.
|
||||||
@ -311,10 +314,10 @@ state = self.get_state("switch")
|
|||||||
state = self.get_state("light.office_1")
|
state = self.get_state("light.office_1")
|
||||||
|
|
||||||
# Return the brightness attribute for light.office_1
|
# Return the brightness attribute for light.office_1
|
||||||
state = self.get_state("light.office_1", attribute = "brightness")
|
state = self.get_state("light.office_1", attribute="brightness")
|
||||||
|
|
||||||
# Return the entire state for light.office_1
|
# Return the entire state for light.office_1
|
||||||
state = self.get_state("light.office_1", attribute = "all")
|
state = self.get_state("light.office_1", attribute="all")
|
||||||
```
|
```
|
||||||
|
|
||||||
### set_state()
|
### set_state()
|
||||||
@ -348,7 +351,7 @@ A list of keyword values to be changed or added to the entities state. e.g., `st
|
|||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
```python
|
```python
|
||||||
status = self.set_state("light.office_1", state = "on", attributes = {"color_name": "red"})
|
status = self.set_state("light.office_1", state="on", attributes={"color_name": "red"})
|
||||||
```
|
```
|
||||||
|
|
||||||
### About Callbacks
|
### About Callbacks
|
||||||
@ -409,8 +412,9 @@ AppDaemons's state callbacks allow an App to listen to a wide variety of events,
|
|||||||
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:
|
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
|
```python
|
||||||
def my_callback(self, entity, attribute, old, new, **kwargs):
|
def my_callback(self, entity, attribute, old, new, **kwargs):
|
||||||
<do some useful work here>
|
"""Handle state callback."""
|
||||||
|
# do some useful work here
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
@ -450,7 +454,7 @@ A dictionary containing any constraints and/or additional user specific keyword
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
handle = listen_state(callback, entity = None, **kwargs)
|
handle = listen_state(callback, entity=None, **kwargs)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
@ -492,8 +496,9 @@ Note: `old` and `new` can be used singly or together.
|
|||||||
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 occurred, since the assumption is that none of them have changed in the intervening period.
|
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 occurred, since the assumption is that none of them have changed in the intervening period.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def my_callback(self, **kwargs):
|
def my_callback(self, **kwargs):
|
||||||
<do some useful work here>
|
"""Handle state change."""
|
||||||
|
# do some useful work here
|
||||||
```
|
```
|
||||||
|
|
||||||
(Scheduler callbacks are documented in detail later in this document)
|
(Scheduler callbacks are documented in detail later in this document)
|
||||||
@ -515,20 +520,25 @@ self.handle = self.listen_state(self.my_callback, "light")
|
|||||||
self.handle = self.listen_state(self.my_callback, "light.office_1")
|
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
|
# 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")
|
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
|
# 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")
|
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
|
# 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")
|
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
|
# 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")
|
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
|
# 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)
|
self.handle = self.listen_state(
|
||||||
|
self.my_callback, "light.office_1", new="on", duration=60
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### cancel_listen_state()
|
### cancel_listen_state()
|
||||||
@ -592,8 +602,9 @@ AppDaemon contains a powerful scheduler that is able to run with 1 second resolu
|
|||||||
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:
|
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
|
```python
|
||||||
def my_callback(self, **kwargs):
|
def my_callback(self, **kwargs):
|
||||||
<do some useful work here>
|
"""Handle scheduler callback."""
|
||||||
|
# do some useful work here
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
@ -643,7 +654,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
self.handle = self.run_in(self.run_in_c)
|
self.handle = self.run_in(self.run_in_c)
|
||||||
self.handle = self.run_in(self.run_in_c, title = "run_in5")
|
self.handle = self.run_in(self.run_in_c, title="run_in5")
|
||||||
```
|
```
|
||||||
#### run_once()
|
#### run_once()
|
||||||
|
|
||||||
@ -678,6 +689,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run at 4pm today, or 4pm tomorrow if it is already after 4pm
|
# Run at 4pm today, or 4pm tomorrow if it is already after 4pm
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
runtime = datetime.time(16, 0, 0)
|
runtime = datetime.time(16, 0, 0)
|
||||||
handle = self.run_once(self.run_once_c, runtime)
|
handle = self.run_once(self.run_once_c, runtime)
|
||||||
@ -716,6 +728,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run at 4pm today
|
# Run at 4pm today
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
runtime = datetime.time(16, 0, 0)
|
runtime = datetime.time(16, 0, 0)
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
@ -755,6 +768,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run daily at 7pm
|
# Run daily at 7pm
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
time = datetime.time(19, 0, 0)
|
time = datetime.time(19, 0, 0)
|
||||||
self.run_daily(self.run_daily_c, runtime)
|
self.run_daily(self.run_daily_c, runtime)
|
||||||
@ -767,7 +781,7 @@ Execute a callback at the same time every hour. If the time has already passed,
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
self.handle = self.run_hourly(callback, time = None, **kwargs)
|
self.handle = self.run_hourly(callback, time=None, **kwargs)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
@ -793,6 +807,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run every hour, on the hour
|
# Run every hour, on the hour
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
time = datetime.time(0, 0, 0)
|
time = datetime.time(0, 0, 0)
|
||||||
self.run_daily(self.run_daily_c, runtime)
|
self.run_daily(self.run_daily_c, runtime)
|
||||||
@ -804,7 +819,7 @@ Execute a callback at the same time every minute. If the time has already passed
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
self.handle = self.run_minutely(callback, time = None, **kwargs)
|
self.handle = self.run_minutely(callback, time=None, **kwargs)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
@ -830,6 +845,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run Every Minute on the minute
|
# Run Every Minute on the minute
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
time = datetime.time(0, 0, 0)
|
time = datetime.time(0, 0, 0)
|
||||||
self.run_minutely(self.run_minutely_c, time)
|
self.run_minutely(self.run_minutely_c, time)
|
||||||
@ -872,6 +888,7 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Run every 17 minutes starting in 2 hours time
|
# Run every 17 minutes starting in 2 hours time
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
...
|
...
|
||||||
self.run_every(self.run_every_c, time, 17 * 60)
|
self.run_every(self.run_every_c, time, 17 * 60)
|
||||||
```
|
```
|
||||||
@ -944,11 +961,11 @@ For example:
|
|||||||
|
|
||||||
```python
|
```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
|
# 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)
|
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
|
# 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)
|
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
|
# 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)
|
self.handle = self.run_in(callback, 120, random_start=-60, random_end=60, **kwargs)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sunrise and Sunset
|
## Sunrise and Sunset
|
||||||
@ -987,15 +1004,17 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
import datetime
|
import datetime
|
||||||
...
|
|
||||||
|
# ...
|
||||||
|
|
||||||
# Run 45 minutes before sunset
|
# Run 45 minutes before sunset
|
||||||
self.run_at_sunrise(self.sun, offset = datetime.timedelta(minutes = -45).total_seconds(), "Sunrise -45 mins")
|
self.run_at_sunrise(self.sun, offset=datetime.timedelta(minutes=-45).total_seconds())
|
||||||
# or you can just do the math yourself
|
# or you can just do the math yourself
|
||||||
self.run_at_sunrise(self.sun, offset = 30 * 60, "Sunrise +30 mins")
|
self.run_at_sunrise(self.sun, offset=30 * 60) # Sunrise +30 mins
|
||||||
# Run at a random time +/- 60 minutes from sunrise
|
# 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")
|
self.run_at_sunrise(self.sun, random_start=-60 * 60, random_end=60 * 60)
|
||||||
# Run at a random time between 30 and 60 minutes before sunrise
|
# 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")
|
self.run_at_sunrise(self.sun, random_start=-60 * 60, random_end=30 * 60)
|
||||||
```
|
```
|
||||||
|
|
||||||
### run_at_sunset()
|
### run_at_sunset()
|
||||||
@ -1031,14 +1050,18 @@ Arbitrary keyword parameters to be provided to the callback function when it is
|
|||||||
```python
|
```python
|
||||||
# Example using timedelta
|
# Example using timedelta
|
||||||
import datetime
|
import datetime
|
||||||
...
|
|
||||||
self.run_at_sunset(self.sun, datetime.timedelta(minutes = -45).total_seconds(), "Sunset -45 mins")
|
# ...
|
||||||
|
|
||||||
|
self.run_at_sunset(
|
||||||
|
self.sun, datetime.timedelta(minutes=-45).total_seconds()
|
||||||
|
) # Sunset -45 mins
|
||||||
# or you can just do the math yourself
|
# or you can just do the math yourself
|
||||||
self.run_at_sunset(self.sun, 30 * 60, "Sunset +30 mins")
|
self.run_at_sunset(self.sun, 30 * 60) # Sunset +30 mins
|
||||||
# Run at a random time +/- 60 minutes from sunset
|
# 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")
|
self.run_at_sunset(self.sun, random_start=-60 * 60, random_end=60 * 60)
|
||||||
# Run at a random time between 30 and 60 minutes before 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")
|
self.run_at_sunset(self.sun, random_start=-60 * 60, random_end=30 * 60)
|
||||||
```
|
```
|
||||||
### sunrise()
|
### sunrise()
|
||||||
|
|
||||||
@ -1096,7 +1119,7 @@ result = self.sun_up()
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.sun_up():
|
if self.sun_up():
|
||||||
do something
|
do_something()
|
||||||
```
|
```
|
||||||
|
|
||||||
### sun_down()
|
### sun_down()
|
||||||
@ -1117,7 +1140,7 @@ result = self.sun_down()
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.sun_down():
|
if self.sun_down():
|
||||||
do something
|
do_something()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Calling Services
|
## Calling Services
|
||||||
@ -1153,8 +1176,8 @@ Each service has different parameter requirements. This argument allows you to s
|
|||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
```python
|
```python
|
||||||
self.call_service("light/turn_on", entity_id = "light.office_lamp", color_name = "red")
|
self.call_service("light/turn_on", entity_id="light.office_lamp", color_name="red")
|
||||||
self.call_service("notify/notify", title = "Hello", message = "Hello World")
|
self.call_service("notify/notify", title="Hello", message="Hello World")
|
||||||
```
|
```
|
||||||
### turn_on()
|
### turn_on()
|
||||||
|
|
||||||
@ -1192,7 +1215,7 @@ A comma separated list of key value pairs to allow specification of parameters o
|
|||||||
```python
|
```python
|
||||||
self.turn_on("switch.patio_lights")
|
self.turn_on("switch.patio_lights")
|
||||||
self.turn_on("scene.bedroom_on")
|
self.turn_on("scene.bedroom_on")
|
||||||
self.turn_on("light.office_1", color_name = "green")
|
self.turn_on("light.office_1", color_name="green")
|
||||||
```
|
```
|
||||||
|
|
||||||
### turn_off()
|
### turn_off()
|
||||||
@ -1246,7 +1269,7 @@ Fully qualified entity_id of the thing to be toggled, e.g., `light.office_lamp`
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
self.toggle("switch.patio_lights")
|
self.toggle("switch.patio_lights")
|
||||||
self.toggle("light.office_1", color_name = "green")
|
self.toggle("light.office_1", color_name="green")
|
||||||
```
|
```
|
||||||
|
|
||||||
### select_value()
|
### select_value()
|
||||||
@ -1365,8 +1388,9 @@ In addition to the Home Assistant supplied events, AppDaemon adds 2 more events.
|
|||||||
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:
|
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
|
```python
|
||||||
def my_callback(self, event_name, data, kwargs):
|
def my_callback(self, event_name, data, kwargs):
|
||||||
<do some useful work here>
|
"""Handle event callback."""
|
||||||
|
# do some useful work here
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
@ -1396,7 +1420,7 @@ Listen event sets up a callback for a specific event, or any event.
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
handle = listen_event(function, event = None, **kwargs):
|
handle = listen_event(function, event=None, **kwargs)
|
||||||
```
|
```
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
@ -1425,9 +1449,11 @@ Filtering will work with any event type, but it will be necessary to figure out
|
|||||||
```python
|
```python
|
||||||
self.listen_event(self.mode_event, "MODE_CHANGE")
|
self.listen_event(self.mode_event, "MODE_CHANGE")
|
||||||
# Listen for a minimote event activating scene 3:
|
# Listen for a minimote event activating scene 3:
|
||||||
self.listen_event(self.generic_event, "zwave.scene_activated", scene_id = 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:
|
# 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)
|
self.listen_event(
|
||||||
|
self.generic_event, "zwave.scene_activated", entity_id="minimote_31", scene_id=3
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### cancel_listen_event()
|
### cancel_listen_event()
|
||||||
@ -1518,6 +1544,7 @@ Functions called as an event callback will be supplied with 2 arguments:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
def service(self, event_name, data):
|
def service(self, event_name, data):
|
||||||
|
"""Handle event."""
|
||||||
```
|
```
|
||||||
|
|
||||||
#### event_name
|
#### event_name
|
||||||
@ -1560,7 +1587,7 @@ automation:
|
|||||||
This can be triggered with a call to AppDaemon's fire_event() as follows:
|
This can be triggered with a call to AppDaemon's fire_event() as follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
self.fire_event("MODE_CHANGE", mode = "Day")
|
self.fire_event("MODE_CHANGE", mode="Day")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Presence
|
## Presence
|
||||||
@ -1585,7 +1612,7 @@ An iterable list of all device trackers.
|
|||||||
```python
|
```python
|
||||||
trackers = self.get_trackers()
|
trackers = self.get_trackers()
|
||||||
for tracker in trackers:
|
for tracker in trackers:
|
||||||
do something
|
do_something(tracker)
|
||||||
```
|
```
|
||||||
|
|
||||||
### get_tracker_state()
|
### get_tracker_state()
|
||||||
@ -1618,7 +1645,7 @@ Fully qualified entity_id of the device tracker to query, e.g., `device_tracker.
|
|||||||
```python
|
```python
|
||||||
trackers = self.get_trackers()
|
trackers = self.get_trackers()
|
||||||
for tracker in trackers:
|
for tracker in trackers:
|
||||||
self.log("{} is {}".format(tracker, self.get_tracker_state(tracker)))
|
self.log("{} is {}".format(tracker, self.get_tracker_state(tracker)))
|
||||||
```
|
```
|
||||||
|
|
||||||
### everyone_home()
|
### everyone_home()
|
||||||
@ -1638,7 +1665,7 @@ Returns `True` if everyone is at home, `False` otherwise.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.everyone_home():
|
if self.everyone_home():
|
||||||
do something
|
do_something()
|
||||||
```
|
```
|
||||||
### anyone_home()
|
### anyone_home()
|
||||||
|
|
||||||
@ -1658,7 +1685,7 @@ Returns `True` if anyone is at home, `False` otherwise.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.anyone_home():
|
if self.anyone_home():
|
||||||
do something
|
do_something()
|
||||||
```
|
```
|
||||||
### noone_home()
|
### noone_home()
|
||||||
|
|
||||||
@ -1678,7 +1705,7 @@ Returns `True` if no one is home, `False` otherwise.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.noone_home():
|
if self.noone_home():
|
||||||
do something
|
do_something()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Miscellaneous Helper Functions
|
## Miscellaneous Helper Functions
|
||||||
@ -1837,9 +1864,9 @@ A representation of the start and end time respectively in a string format with
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if self.now_is_between("17:30:00", "08:00:00"):
|
if self.now_is_between("17:30:00", "08:00:00"):
|
||||||
do something
|
do_something()
|
||||||
if self.now_is_between("sunset - 00:45:00", "sunrise + 00:45:00"):
|
if self.now_is_between("sunset - 00:45:00", "sunrise + 00:45:00"):
|
||||||
do something
|
do_something_else()
|
||||||
```
|
```
|
||||||
|
|
||||||
### friendly_name()
|
### friendly_name()
|
||||||
@ -1860,7 +1887,11 @@ The friendly name of the entity if it exists or the entity id if not.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
tracker = "device_tracker.andrew"
|
tracker = "device_tracker.andrew"
|
||||||
self.log("{} ({}) is {}".format(tracker, self.friendly_name(tracker), self.get_tracker_state(tracker)))
|
self.log(
|
||||||
|
"{} ({}) is {}".format(
|
||||||
|
tracker, self.friendly_name(tracker), self.get_tracker_state(tracker)
|
||||||
|
)
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### split_entity()
|
### split_entity()
|
||||||
@ -1888,7 +1919,7 @@ A list with 2 entries, the device and entity respectively.
|
|||||||
```python
|
```python
|
||||||
device, entity = self.split_entity(entity_id)
|
device, entity = self.split_entity(entity_id)
|
||||||
if device == "scene":
|
if device == "scene":
|
||||||
do something specific to scenes
|
do_something_specific_to_scenes()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -1935,7 +1966,7 @@ A list of split devices with 1 or more entries.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
for sensor in self.split_device_list(self.args["sensors"]):
|
for sensor in self.split_device_list(self.args["sensors"]):
|
||||||
do something for each sensor, e.g., make a state subscription
|
do_something(sensor) # e.g. make a state subscription
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -1948,7 +1979,7 @@ AppDaemon uses 2 separate logs - the general log and the error log. An AppDaemon
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
log(message, level = "INFO")
|
log(message, level="INFO")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
@ -1969,7 +2000,7 @@ The log level of the message - takes a string representing the standard logger l
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
self.log("Log Test: Parameter is {}".format(some_variable))
|
self.log("Log Test: Parameter is {}".format(some_variable))
|
||||||
self.log("Log Test: Parameter is {}".format(some_variable), level = "ERROR")
|
self.log("Log Test: Parameter is {}".format(some_variable), level="ERROR")
|
||||||
```
|
```
|
||||||
|
|
||||||
### error()
|
### error()
|
||||||
@ -1977,7 +2008,7 @@ self.log("Log Test: Parameter is {}".format(some_variable), level = "ERROR")
|
|||||||
#### Synopsis
|
#### Synopsis
|
||||||
|
|
||||||
```python
|
```python
|
||||||
error(message, level = "WARNING")
|
error(message, level="WARNING")
|
||||||
```
|
```
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
@ -1997,7 +2028,7 @@ The log level of the message - takes a string representing the standard logger l
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
self.error("Some Warning string")
|
self.error("Some Warning string")
|
||||||
self.error("Some Critical string", level = "CRITICAL")
|
self.error("Some Critical string", level="CRITICAL")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sharing information between Apps
|
## Sharing information between Apps
|
||||||
@ -2007,7 +2038,7 @@ Sharing information between different Apps is very simple if required. Each app
|
|||||||
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:
|
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
|
```python
|
||||||
other_apps_arg = self.config["some_app"]["some_parameter"].
|
other_apps_arg = self.config["some_app"]["some_parameter"]
|
||||||
```
|
```
|
||||||
|
|
||||||
To get AppDaemon's config parameters, use the key "AppDaemon", e.g.:
|
To get AppDaemon's config parameters, use the key "AppDaemon", e.g.:
|
||||||
|
@ -58,18 +58,17 @@ different scenes in a different version of the App.
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
|
|
||||||
class OutsideLights(hass.Hass):
|
class OutsideLights(hass.Hass):
|
||||||
|
def initialize(self):
|
||||||
|
self.run_at_sunrise(self.sunrise_cb)
|
||||||
|
self.run_at_sunset(self.before_sunset_cb, offset=-900)
|
||||||
|
|
||||||
def initialize(self):
|
def sunrise_cb(self, kwargs):
|
||||||
self.run_at_sunrise(self.sunrise_cb)
|
self.turn_on(self.args["off_scene"])
|
||||||
self.run_at_sunset(self.before_sunset_cb, offset=-900)
|
|
||||||
|
|
||||||
def sunrise_cb(self, kwargs):
|
|
||||||
self.turn_on(self.args["off_scene"])
|
|
||||||
|
|
||||||
def before_sunset_cb(self, kwargs):
|
|
||||||
self.turn_on(self.args["on_scene"])
|
|
||||||
|
|
||||||
|
def before_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.
|
This is also fairly easy to achieve with Home Assistant automations, but we are just getting started.
|
||||||
@ -81,18 +80,18 @@ Our next example is to turn on a light when motion is detected and it is dark, a
|
|||||||
```python
|
```python
|
||||||
import appdaemon.appapi as appapi
|
import appdaemon.appapi as appapi
|
||||||
|
|
||||||
|
|
||||||
class FlashyMotionLights(appapi.AppDaemon):
|
class FlashyMotionLights(appapi.AppDaemon):
|
||||||
|
def initialize(self):
|
||||||
|
self.listen_state(self.motion, "binary_sensor.drive", new="on")
|
||||||
|
|
||||||
def initialize(self):
|
def motion(self, entity, attribute, old, new, kwargs):
|
||||||
self.listen_state(self.motion, "binary_sensor.drive", new = "on")
|
if self.sun_down():
|
||||||
|
self.turn_on("light.drive")
|
||||||
|
self.run_in(self.light_off, 60)
|
||||||
|
|
||||||
def motion(self, entity, attribute, old, new, kwargs):
|
def light_off(self, kwargs):
|
||||||
if self.sun_down():
|
self.turn_off("light.drive")
|
||||||
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.
|
This is starting to get a little more complex in Home Assistant automations requiring an Automation rule and two separate scripts.
|
||||||
@ -102,26 +101,26 @@ Now lets extend this with a somewhat artificial example to show something that i
|
|||||||
```python
|
```python
|
||||||
import homeassistant.appapi as appapi
|
import homeassistant.appapi as appapi
|
||||||
|
|
||||||
|
|
||||||
class MotionLights(appapi.AppDaemon):
|
class MotionLights(appapi.AppDaemon):
|
||||||
|
def initialize(self):
|
||||||
|
self.listen_state(self.motion, "binary_sensor.drive", new="on")
|
||||||
|
|
||||||
def initialize(self):
|
def motion(self, entity, attribute, old, new, kwargs):
|
||||||
self.listen_state(self.motion, "binary_sensor.drive", new = "on")
|
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 motion(self, entity, attribute, old, new, kwargs):
|
def light_off(self, kwargs):
|
||||||
if self.self.sun_down():
|
self.turn_off("light.drive")
|
||||||
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):
|
def flash_warning(self, kwargs):
|
||||||
self.turn_off("light.drive")
|
self.toggle("light.living_room")
|
||||||
|
self.flashcount += 1
|
||||||
def flash_warning(self, kwargs):
|
if self.flashcount < 10:
|
||||||
self.toggle("light.living_room")
|
self.run_in(self.flash_warning, 1)
|
||||||
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.
|
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.
|
||||||
|
@ -154,7 +154,7 @@ for x in range(0, 10):
|
|||||||
translations["%s" % x] = "\\x3%s" % x
|
translations["%s" % x] = "\\x3%s" % x
|
||||||
|
|
||||||
for c in sys.argv[1]:
|
for c in sys.argv[1]:
|
||||||
print(translations[c], end='')
|
print(translations[c], end="")
|
||||||
```
|
```
|
||||||
|
|
||||||
## OZW Log
|
## OZW Log
|
||||||
|
@ -51,7 +51,7 @@ entity_id:
|
|||||||
title:
|
title:
|
||||||
description: >
|
description: >
|
||||||
A title to be used for the notification if the notifier supports it
|
A title to be used for the notification if the notifier supports it
|
||||||
with [template][template] support.
|
with [template](/docs/configuration/templating/) support.
|
||||||
required: false
|
required: false
|
||||||
type: template
|
type: template
|
||||||
state:
|
state:
|
||||||
@ -80,13 +80,13 @@ skip_first:
|
|||||||
message:
|
message:
|
||||||
description: >
|
description: >
|
||||||
A message to be sent after an alert transitions from `off` to `on`
|
A message to be sent after an alert transitions from `off` to `on`
|
||||||
with [template][template] support.
|
with [template](/docs/configuration/templating/) support.
|
||||||
required: false
|
required: false
|
||||||
type: template
|
type: template
|
||||||
done_message:
|
done_message:
|
||||||
description: >
|
description: >
|
||||||
A message sent after an alert transitions from `on` to `off` with
|
A message sent after an alert transitions from `on` to `off` with
|
||||||
[template][template] support. Is only sent if an alert notification
|
[template](/docs/configuration/templating/) support. Is only sent if an alert notification
|
||||||
was sent for transitioning from `off` to `on`.
|
was sent for transitioning from `off` to `on`.
|
||||||
required: false
|
required: false
|
||||||
type: template
|
type: template
|
||||||
@ -205,7 +205,7 @@ sent at 2:15, 2:45, 3:45, 4:45, etc., continuing every 60 minutes.
|
|||||||
### Message Templates
|
### Message Templates
|
||||||
|
|
||||||
It may be desirable to have the alert notifications include information
|
It may be desirable to have the alert notifications include information
|
||||||
about the state of the entity. [Templates](/docs/configuration/templating/)
|
about the state of the entity. [Templates][template]
|
||||||
can be used in the message or name of the alert to make it more relevant.
|
can be used in the message or name of the alert to make it more relevant.
|
||||||
The following will show for a plant how to include the problem `attribute`
|
The following will show for a plant how to include the problem `attribute`
|
||||||
of the entity.
|
of the entity.
|
||||||
|
@ -223,6 +223,7 @@ import appdaemon.plugins.hass.hassapi as hass
|
|||||||
import datetime
|
import datetime
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class DeconzHelper(hass.Hass):
|
class DeconzHelper(hass.Hass):
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
self.listen_event(self.event_received, "deconz_event")
|
self.listen_event(self.event_received, "deconz_event")
|
||||||
@ -232,8 +233,15 @@ class DeconzHelper(hass.Hass):
|
|||||||
event_id = data["id"]
|
event_id = data["id"]
|
||||||
event_received = datetime.now()
|
event_received = datetime.now()
|
||||||
|
|
||||||
self.log("Deconz event received from {}. Event was: {}".format(event_id, event_data))
|
self.log(f"Deconz event received from {event_id}. Event was: {event_data}")
|
||||||
self.set_state("sensor.deconz_event", state = event_id, attributes = {"event_data": event_data, "event_received": str(event_received)})
|
self.set_state(
|
||||||
|
"sensor.deconz_event",
|
||||||
|
state=event_id,
|
||||||
|
attributes={
|
||||||
|
"event_data": event_data,
|
||||||
|
"event_received": str(event_received),
|
||||||
|
},
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
@ -255,23 +263,23 @@ remote_control:
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
class RemoteControl(hass.Hass):
|
|
||||||
|
|
||||||
|
class RemoteControl(hass.Hass):
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if 'event' in self.args:
|
if "event" in self.args:
|
||||||
self.listen_event(self.handle_event, self.args['event'])
|
self.listen_event(self.handle_event, self.args["event"])
|
||||||
|
|
||||||
def handle_event(self, event_name, data, kwargs):
|
def handle_event(self, event_name, data, kwargs):
|
||||||
if data['id'] == self.args['id']:
|
if data["id"] == self.args["id"]:
|
||||||
self.log(data['event'])
|
self.log(data["event"])
|
||||||
if data['event'] == 1002:
|
if data["event"] == 1002:
|
||||||
self.log('Button on')
|
self.log("Button on")
|
||||||
elif data['event'] == 2002:
|
elif data["event"] == 2002:
|
||||||
self.log('Button dim up')
|
self.log("Button dim up")
|
||||||
elif data['event'] == 3002:
|
elif data["event"] == 3002:
|
||||||
self.log('Button dim down')
|
self.log("Button dim down")
|
||||||
elif data['event'] == 4002:
|
elif data["event"] == 4002:
|
||||||
self.log('Button off')
|
self.log("Button off")
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
@ -298,34 +306,36 @@ sonos_remote_control:
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
class SonosRemote(hass.Hass):
|
|
||||||
|
|
||||||
|
class SonosRemote(hass.Hass):
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.sonos = self.args['sonos']
|
self.sonos = self.args["sonos"]
|
||||||
if 'event' in self.args:
|
if "event" in self.args:
|
||||||
self.listen_event(self.handle_event, self.args['event'])
|
self.listen_event(self.handle_event, self.args["event"])
|
||||||
|
|
||||||
def handle_event(self, event_name, data, kwargs):
|
def handle_event(self, event_name, data, kwargs):
|
||||||
if data['id'] == self.args['id']:
|
if data["id"] == self.args["id"]:
|
||||||
if data['event'] == 1002:
|
if data["event"] == 1002:
|
||||||
self.log('Button toggle')
|
self.log("Button toggle")
|
||||||
self.call_service("media_player/media_play_pause", entity_id = self.sonos)
|
self.call_service("media_player/media_play_pause", entity_id=self.sonos)
|
||||||
|
|
||||||
elif data['event'] == 2002:
|
elif data["event"] == 2002:
|
||||||
self.log('Button volume up')
|
self.log("Button volume up")
|
||||||
self.call_service("media_player/volume_up", entity_id = self.sonos)
|
self.call_service("media_player/volume_up", entity_id=self.sonos)
|
||||||
|
|
||||||
elif data['event'] == 3002:
|
elif data["event"] == 3002:
|
||||||
self.log('Button volume down')
|
self.log("Button volume down")
|
||||||
self.call_service("media_player/volume_down", entity_id = self.sonos)
|
self.call_service("media_player/volume_down", entity_id=self.sonos)
|
||||||
|
|
||||||
elif data['event'] == 4002:
|
elif data["event"] == 4002:
|
||||||
self.log('Button previous')
|
self.log("Button previous")
|
||||||
self.call_service("media_player/media_previous_track", entity_id = self.sonos)
|
self.call_service(
|
||||||
|
"media_player/media_previous_track", entity_id=self.sonos
|
||||||
|
)
|
||||||
|
|
||||||
elif data['event'] == 5002:
|
elif data["event"] == 5002:
|
||||||
self.log('Button next')
|
self.log("Button next")
|
||||||
self.call_service("media_player/media_next_track", entity_id = self.sonos)
|
self.call_service("media_player/media_next_track", entity_id=self.sonos)
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
@ -30,7 +30,7 @@ As it suggests the region name is not the city or nearest city you want to get t
|
|||||||
|
|
||||||
Be aware, to get the region name you need to use the following link by replacing `Hamburg` with your city:
|
Be aware, to get the region name you need to use the following link by replacing `Hamburg` with your city:
|
||||||
- Find your region here: `https://www.dwd.de/DE/wetter/warnungen_landkreise/warnWetter_node.html?ort=Hamburg`
|
- Find your region here: `https://www.dwd.de/DE/wetter/warnungen_landkreise/warnWetter_node.html?ort=Hamburg`
|
||||||
- On the page that is loaded in your browser you will find the correct region ("Kreis") below the map as a headding.
|
- On the page that is loaded in your browser you will find the correct region ("Kreis") below the map as a heading.
|
||||||
- Verify if you find any warning for your region here. Your region ("Kreis") will appear only if warnings exist: `https://www.dwd.de/DWD/warnungen/warnapp_landkreise/json/warnings.json?jsonp=loadWarnings`
|
- Verify if you find any warning for your region here. Your region ("Kreis") will appear only if warnings exist: `https://www.dwd.de/DWD/warnungen/warnapp_landkreise/json/warnings.json?jsonp=loadWarnings`
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -161,6 +161,7 @@ in seconds since the UNIX epoch. Convert them manually using
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
datetime.fromtimestamp(1422830502)
|
datetime.fromtimestamp(1422830502)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -222,9 +222,13 @@ As already shown on the [API](/developers/rest_api/) page, it's very simple to u
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
'http://localhost:8123/api/states/binary_sensor.radio',
|
"http://localhost:8123/api/states/binary_sensor.radio",
|
||||||
headers={'Authorization': 'Bearer LONG_LIVED_ACCESS_TOKEN', 'content-type': 'application/json'},
|
headers={
|
||||||
data=json.dumps({'state': 'on', 'attributes': {'friendly_name': 'Radio'}}))
|
"Authorization": "Bearer LONG_LIVED_ACCESS_TOKEN",
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
data=json.dumps({"state": "on", "attributes": {"friendly_name": "Radio"}}),
|
||||||
|
)
|
||||||
print(response.text)
|
print(response.text)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ It is not possible to use Python imports with this integration. If you want to d
|
|||||||
- Create a file `hello_world.py` in the folder and give it this content:
|
- Create a file `hello_world.py` in the folder and give it this content:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
name = data.get('name', 'world')
|
name = data.get("name", "world")
|
||||||
logger.info("Hello {}".format(name))
|
logger.info("Hello %s", name)
|
||||||
hass.bus.fire(name, { "wow": "from a Python script!" })
|
hass.bus.fire(name, {"wow": "from a Python script!"})
|
||||||
```
|
```
|
||||||
|
|
||||||
- Start Home Assistant
|
- Start Home Assistant
|
||||||
@ -50,11 +50,11 @@ The following example shows how to call a service from `python_script`. This scr
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# turn_on_light.py
|
# turn_on_light.py
|
||||||
entity_id = data.get('entity_id')
|
entity_id = data.get("entity_id")
|
||||||
rgb_color = data.get('rgb_color', [255, 255, 255])
|
rgb_color = data.get("rgb_color", [255, 255, 255])
|
||||||
if entity_id is not None:
|
if entity_id is not None:
|
||||||
service_data = {'entity_id': entity_id, 'rgb_color': rgb_color, 'brightness': 255 }
|
service_data = {"entity_id": entity_id, "rgb_color": rgb_color, "brightness": 255}
|
||||||
hass.services.call('light', 'turn_on', service_data, False)
|
hass.services.call("light", "turn_on", service_data, False)
|
||||||
```
|
```
|
||||||
The above `python_script` can be called using the following JSON as an input.
|
The above `python_script` can be called using the following JSON as an input.
|
||||||
|
|
||||||
|
@ -137,19 +137,19 @@ You can then use the following `python_script` to save the video file:
|
|||||||
```python
|
```python
|
||||||
# obtain ring doorbell camera object
|
# obtain ring doorbell camera object
|
||||||
# replace the camera.front_door by your camera entity
|
# replace the camera.front_door by your camera entity
|
||||||
ring_cam = hass.states.get('camera.front_door')
|
ring_cam = hass.states.get("camera.front_door")
|
||||||
|
|
||||||
subdir_name = 'ring_{}'.format(ring_cam.attributes.get('friendly_name'))
|
subdir_name = f"ring_{ring_cam.attributes.get('friendly_name')}"
|
||||||
|
|
||||||
# get video URL
|
# get video URL
|
||||||
data = {
|
data = {
|
||||||
'url': ring_cam.attributes.get('video_url'),
|
"url": ring_cam.attributes.get("video_url"),
|
||||||
'subdir': subdir_name,
|
"subdir": subdir_name,
|
||||||
'filename': ring_cam.attributes.get('friendly_name')
|
"filename": ring_cam.attributes.get("friendly_name"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# call downloader integration to save the video
|
# call downloader integration to save the video
|
||||||
hass.services.call('downloader', 'download_file', data)
|
hass.services.call("downloader", "download_file", data)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sensor
|
## Sensor
|
||||||
|
@ -155,8 +155,9 @@ The script (saved as `arest-value.py`) that is used looks like the example below
|
|||||||
```python
|
```python
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
from requests import get
|
from requests import get
|
||||||
response = get('http://10.0.0.48/analog/2')
|
|
||||||
print(response.json()['return_value'])
|
response = get("http://10.0.0.48/analog/2")
|
||||||
|
print(response.json()["return_value"])
|
||||||
```
|
```
|
||||||
|
|
||||||
To use the script you need to add something like the following to your `configuration.yaml` file.
|
To use the script you need to add something like the following to your `configuration.yaml` file.
|
||||||
|
@ -53,7 +53,7 @@ The result set will include your chat ID as `id` in the `chat` section:
|
|||||||
|
|
||||||
**Method 3:** Another way to get your chat ID directly is described below. Start your Python interpreter from the command-line:
|
**Method 3:** Another way to get your chat ID directly is described below. Start your Python interpreter from the command-line:
|
||||||
|
|
||||||
```python
|
```shell
|
||||||
$ python3
|
$ python3
|
||||||
>>> import telegram
|
>>> import telegram
|
||||||
>>> bot = telegram.Bot(token='YOUR_API_TOKEN')
|
>>> bot = telegram.Bot(token='YOUR_API_TOKEN')
|
||||||
|
@ -424,76 +424,90 @@ This is how the previous 4 automations would be through a simple AppDaemon app:
|
|||||||
```python
|
```python
|
||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
|
|
||||||
|
|
||||||
class TelegramBotEventListener(hass.Hass):
|
class TelegramBotEventListener(hass.Hass):
|
||||||
"""Event listener for Telegram bot events."""
|
"""Event listener for Telegram bot events."""
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""Listen to Telegram Bot events of interest."""
|
"""Listen to Telegram Bot events of interest."""
|
||||||
self.listen_event(self.receive_telegram_text, 'telegram_text')
|
self.listen_event(self.receive_telegram_text, "telegram_text")
|
||||||
self.listen_event(self.receive_telegram_callback, 'telegram_callback')
|
self.listen_event(self.receive_telegram_callback, "telegram_callback")
|
||||||
|
|
||||||
def receive_telegram_text(self, event_id, payload_event, *args):
|
def receive_telegram_text(self, event_id, payload_event, *args):
|
||||||
"""Text repeater."""
|
"""Text repeater."""
|
||||||
assert event_id == 'telegram_text'
|
assert event_id == "telegram_text"
|
||||||
user_id = payload_event['user_id']
|
user_id = payload_event["user_id"]
|
||||||
msg = 'You said: ``` %s ```' % payload_event['text']
|
msg = "You said: ``` %s ```" % payload_event["text"]
|
||||||
keyboard = [[("Edit message", "/edit_msg"),
|
keyboard = [
|
||||||
("Don't", "/do_nothing")],
|
[("Edit message", "/edit_msg"), ("Don't", "/do_nothing")],
|
||||||
[("Remove this button", "/remove button")]]
|
[("Remove this button", "/remove button")],
|
||||||
self.call_service('telegram_bot/send_message',
|
]
|
||||||
title='*Dumb automation*',
|
self.call_service(
|
||||||
target=user_id,
|
"telegram_bot/send_message",
|
||||||
message=msg,
|
title="*Dumb automation*",
|
||||||
disable_notification=True,
|
target=user_id,
|
||||||
inline_keyboard=keyboard)
|
message=msg,
|
||||||
|
disable_notification=True,
|
||||||
|
inline_keyboard=keyboard,
|
||||||
|
)
|
||||||
|
|
||||||
def receive_telegram_callback(self, event_id, payload_event, *args):
|
def receive_telegram_callback(self, event_id, payload_event, *args):
|
||||||
"""Event listener for Telegram callback queries."""
|
"""Event listener for Telegram callback queries."""
|
||||||
assert event_id == 'telegram_callback'
|
assert event_id == "telegram_callback"
|
||||||
data_callback = payload_event['data']
|
data_callback = payload_event["data"]
|
||||||
callback_id = payload_event['id']
|
callback_id = payload_event["id"]
|
||||||
chat_id = payload_event['chat_id']
|
chat_id = payload_event["chat_id"]
|
||||||
# keyboard = ["Edit message:/edit_msg, Don't:/do_nothing",
|
# keyboard = ["Edit message:/edit_msg, Don't:/do_nothing",
|
||||||
# "Remove this button:/remove button"]
|
# "Remove this button:/remove button"]
|
||||||
keyboard = [[("Edit message", "/edit_msg"),
|
keyboard = [
|
||||||
("Don't", "/do_nothing")],
|
[("Edit message", "/edit_msg"), ("Don't", "/do_nothing")],
|
||||||
[("Remove this button", "/remove button")]]
|
[("Remove this button", "/remove button")],
|
||||||
|
]
|
||||||
|
|
||||||
if data_callback == '/edit_msg': # Message editor:
|
if data_callback == "/edit_msg": # Message editor:
|
||||||
# Answer callback query
|
# Answer callback query
|
||||||
self.call_service('telegram_bot/answer_callback_query',
|
self.call_service(
|
||||||
message='Editing the message!',
|
"telegram_bot/answer_callback_query",
|
||||||
callback_query_id=callback_id,
|
message="Editing the message!",
|
||||||
show_alert=True)
|
callback_query_id=callback_id,
|
||||||
|
show_alert=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Edit the message origin of the callback query
|
# Edit the message origin of the callback query
|
||||||
msg_id = payload_event['message']['message_id']
|
msg_id = payload_event["message"]["message_id"]
|
||||||
user = payload_event['from_first']
|
user = payload_event["from_first"]
|
||||||
title = '*Message edit*'
|
title = "*Message edit*"
|
||||||
msg = 'Callback received from %s. Message id: %s. Data: ``` %s ```'
|
msg = "Callback received from %s. Message id: %s. Data: ``` %s ```"
|
||||||
self.call_service('telegram_bot/edit_message',
|
self.call_service(
|
||||||
chat_id=chat_id,
|
"telegram_bot/edit_message",
|
||||||
message_id=msg_id,
|
chat_id=chat_id,
|
||||||
title=title,
|
message_id=msg_id,
|
||||||
message=msg % (user, msg_id, data_callback),
|
title=title,
|
||||||
inline_keyboard=keyboard)
|
message=msg % (user, msg_id, data_callback),
|
||||||
|
inline_keyboard=keyboard,
|
||||||
|
)
|
||||||
|
|
||||||
elif data_callback == '/remove button': # Keyboard editor:
|
elif data_callback == "/remove button": # Keyboard editor:
|
||||||
# Answer callback query
|
# Answer callback query
|
||||||
self.call_service('telegram_bot/answer_callback_query',
|
self.call_service(
|
||||||
message='Callback received for editing the '
|
"telegram_bot/answer_callback_query",
|
||||||
'inline keyboard!',
|
message="Callback received for editing the " "inline keyboard!",
|
||||||
callback_query_id=callback_id)
|
callback_query_id=callback_id,
|
||||||
|
)
|
||||||
|
|
||||||
# Edit the keyboard
|
# Edit the keyboard
|
||||||
new_keyboard = keyboard[:1]
|
new_keyboard = keyboard[:1]
|
||||||
self.call_service('telegram_bot/edit_replymarkup',
|
self.call_service(
|
||||||
chat_id=chat_id,
|
"telegram_bot/edit_replymarkup",
|
||||||
message_id='last',
|
chat_id=chat_id,
|
||||||
inline_keyboard=new_keyboard)
|
message_id="last",
|
||||||
|
inline_keyboard=new_keyboard,
|
||||||
|
)
|
||||||
|
|
||||||
elif data_callback == '/do_nothing': # Only Answer to callback query
|
elif data_callback == "/do_nothing": # Only Answer to callback query
|
||||||
self.call_service('telegram_bot/answer_callback_query',
|
self.call_service(
|
||||||
message='OK, you said no!',
|
"telegram_bot/answer_callback_query",
|
||||||
callback_query_id=callback_id)
|
message="OK, you said no!",
|
||||||
|
callback_query_id=callback_id,
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
@ -36,6 +36,6 @@ name:
|
|||||||
|
|
||||||
For valid time zones check the **TZ** column in the [Wikipedia overview](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Or get the full list from the [pytz](https://pypi.python.org/pypi/pytz) module.
|
For valid time zones check the **TZ** column in the [Wikipedia overview](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Or get the full list from the [pytz](https://pypi.python.org/pypi/pytz) module.
|
||||||
|
|
||||||
```python
|
```shell
|
||||||
python3 -c "import pytz;print(pytz.all_timezones)"
|
python3 -c "import pytz;print(pytz.all_timezones)"
|
||||||
```
|
```
|
||||||
|
@ -315,7 +315,7 @@ All services for use in Home Assistant are moved to their integration naming spa
|
|||||||
## Release 0.103.4 - December 22
|
## Release 0.103.4 - December 22
|
||||||
|
|
||||||
- Remove requirement from entity integration ([@balloob] - [#30113]) ([doods docs]) ([image_processing docs]) ([seven_segments docs]) ([tensorflow docs])
|
- Remove requirement from entity integration ([@balloob] - [#30113]) ([doods docs]) ([image_processing docs]) ([seven_segments docs]) ([tensorflow docs])
|
||||||
- Move imports into setup function in homekit __init__.py ([@springstan] - [#30137]) ([homekit docs])
|
- Move imports into setup function in homekit **init**.py ([@springstan] - [#30137]) ([homekit docs])
|
||||||
- Fix deconz SSDP updating Hassio discovery ([@frenck] - [#30153]) ([deconz docs])
|
- Fix deconz SSDP updating Hassio discovery ([@frenck] - [#30153]) ([deconz docs])
|
||||||
- Allow battery value of 0 as well as make sure to not create a battery tracker if one already exist ([@Kane610] - [#30155]) ([deconz docs])
|
- Allow battery value of 0 as well as make sure to not create a battery tracker if one already exist ([@Kane610] - [#30155]) ([deconz docs])
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ All services for use in Home Assistant are moved to their integration naming spa
|
|||||||
[#30137]: https://github.com/home-assistant/home-assistant/pull/30137
|
[#30137]: https://github.com/home-assistant/home-assistant/pull/30137
|
||||||
[#30153]: https://github.com/home-assistant/home-assistant/pull/30153
|
[#30153]: https://github.com/home-assistant/home-assistant/pull/30153
|
||||||
[#30155]: https://github.com/home-assistant/home-assistant/pull/30155
|
[#30155]: https://github.com/home-assistant/home-assistant/pull/30155
|
||||||
[@Kane610]: https://github.com/Kane610
|
[@kane610]: https://github.com/Kane610
|
||||||
[@balloob]: https://github.com/balloob
|
[@balloob]: https://github.com/balloob
|
||||||
[@frenck]: https://github.com/frenck
|
[@frenck]: https://github.com/frenck
|
||||||
[@springstan]: https://github.com/springstan
|
[@springstan]: https://github.com/springstan
|
||||||
@ -342,6 +342,27 @@ All services for use in Home Assistant are moved to their integration naming spa
|
|||||||
[@tchellomello]: https://github.com/tchellomello
|
[@tchellomello]: https://github.com/tchellomello
|
||||||
[ring docs]: /components/ring/
|
[ring docs]: /components/ring/
|
||||||
|
|
||||||
|
## Release 0.103.6 - January 6
|
||||||
|
|
||||||
|
- Handle wired bug on restart ([@Kane610] - [#30276]) ([unifi docs])
|
||||||
|
- Bump pysmartthings 0.7.0 ([@andrewsayre] - [#30302]) ([smartthings docs])
|
||||||
|
- Bump env_canada to 0.0.31 ([@michaeldavie] - [#30409]) ([environment_canada docs])
|
||||||
|
- Fix google sync ([@balloob] - [#30524]) ([cloud docs]) ([google_assistant docs])
|
||||||
|
|
||||||
|
[#30276]: https://github.com/home-assistant/home-assistant/pull/30276
|
||||||
|
[#30302]: https://github.com/home-assistant/home-assistant/pull/30302
|
||||||
|
[#30409]: https://github.com/home-assistant/home-assistant/pull/30409
|
||||||
|
[#30524]: https://github.com/home-assistant/home-assistant/pull/30524
|
||||||
|
[@kane610]: https://github.com/Kane610
|
||||||
|
[@andrewsayre]: https://github.com/andrewsayre
|
||||||
|
[@balloob]: https://github.com/balloob
|
||||||
|
[@michaeldavie]: https://github.com/michaeldavie
|
||||||
|
[cloud docs]: /integrations/cloud/
|
||||||
|
[environment_canada docs]: /integrations/environment_canada/
|
||||||
|
[google_assistant docs]: /integrations/google_assistant/
|
||||||
|
[smartthings docs]: /integrations/smartthings/
|
||||||
|
[unifi docs]: /integrations/unifi/
|
||||||
|
|
||||||
## If you need help...
|
## If you need help...
|
||||||
|
|
||||||
...don't hesitate to use our very active [forums](https://community.home-assistant.io/) or join us for a little [chat](https://discord.gg/c5DvZ4e).
|
...don't hesitate to use our very active [forums](https://community.home-assistant.io/) or join us for a little [chat](https://discord.gg/c5DvZ4e).
|
||||||
|
@ -16,7 +16,6 @@ The following will take you through the steps required to install Hass.io.
|
|||||||
- [Tinkerboard][tinker]
|
- [Tinkerboard][tinker]
|
||||||
- [Odroid-C2][odroid-c2]
|
- [Odroid-C2][odroid-c2]
|
||||||
- [Odroid-XU4][odroid-xu4]
|
- [Odroid-XU4][odroid-xu4]
|
||||||
- [OrangePi-Prime][opi-prime]
|
|
||||||
- [Intel-Nuc][intel-nuc]
|
- [Intel-Nuc][intel-nuc]
|
||||||
|
|
||||||
- As a virtual appliance:
|
- As a virtual appliance:
|
||||||
@ -218,7 +217,6 @@ A detailed guide about running Hass.io as a virtual machine is available in the
|
|||||||
[tinker]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_tinker-3.7.img.gz
|
[tinker]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_tinker-3.7.img.gz
|
||||||
[odroid-c2]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_odroid-c2-3.7.img.gz
|
[odroid-c2]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_odroid-c2-3.7.img.gz
|
||||||
[odroid-xu4]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_odroid-xu4-3.7.img.gz
|
[odroid-xu4]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_odroid-xu4-3.7.img.gz
|
||||||
[opi-prime]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_opi-prime-3.7.img.gz
|
|
||||||
[intel-nuc]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_intel-nuc-3.7.img.gz
|
[intel-nuc]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_intel-nuc-3.7.img.gz
|
||||||
[vmdk]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_ova-3.7.vmdk.gz
|
[vmdk]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_ova-3.7.vmdk.gz
|
||||||
[vhdx]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_ova-3.7.vhdx.gz
|
[vhdx]: https://github.com/home-assistant/hassos/releases/download/3.7/hassos_ova-3.7.vhdx.gz
|
||||||
|
Loading…
x
Reference in New Issue
Block a user