Skip to content

Commit

Permalink
feat: Initial services buildout (#142)
Browse files Browse the repository at this point in the history
* Inital services buildout.

* Inital services buildout.

* Update README.md

Add some documentation for new service features.
  • Loading branch information
xannor authored Mar 2, 2020
1 parent 00478c8 commit e5e18f5
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 11 deletions.
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,83 @@ mtn_lounge:

**Note:** Using state entities can have unexpected consequences. For example, if you state entities do not overlap with control entities then your control entities will never turn off. This is the culprit of _advanced configurations_, use at your own risk. If you have problems, make your state entities the same as your control entities, and stick to state entities with a clear state (such as lights, media players etc.)

## Automation Support and Services

### Entity Services

The entity controller support a few services that can be used to extend the customization of the entity.

#### Stay

```yaml
service: entity_controller.set_stay_on
entity_id: entity_controller.motion
```

This service takes an entity id and will set the stay flag to on

```yaml
service: entity_controller.set_stay_off
entity_id: entity_controller.motion
```

This service takes an entity id and will clear the stay flag.

**Note:** There is no attribute that exposes the stay flag state at this time.

#### Clearing Blocking State

```yaml
service: entity_controller.clear_block
entity_id: entity_controller.motion
```

This service will clear the blocking state, if it is set, the same as if the block timer was run down.
This allows for automations to react to the entity being blocked by a light on and clear the state is needed.

**Example**
```yaml
automations:
- id: example
trigger:
- platform: state
entity_id: entity_controller.motion
to: blocking
for: 00:01:00
action:
- service: entity_controller.clear_block
entity_id: entity_controller.motion
```
**Note:** The above example is functionally equivalent to setting a block timeout in the configuration.

#### Set Night Mode

```yaml
service: entity_controller.set_night_mode
entity_id: entity_controller.motion
data:
start_time: now
end_time: constraint
```

This service is for customizing the night mode settings for more dynamic scripts. It will set the night mode start
and stop times to the times specified. If only one or both times are provided, only those times are changed. If
no time is provided night mode is effectivly disabled by setting both the start and end to midnight. This service takes
the same time rules as the configuration, plus supports two additional options that make sense with automations.

```yaml
start_time: now
```

This will set the start (or end) night mode time to the current time. This is usefull for turning night mode on or off instantly.

```yaml
end_time: constraint
```

This will set the end (or start) night mode time to the appropriate constraint time (start or end.) This is handy for starting or making
night mode last the same as the configured constraints. **Note:** This has no meaning if constraints are not set, so it would be equivilent to not providing a value.

### Custom State Strings
The following code extract shows the default state strings that were made to represent the `on` and `off` states. These defaults can be overwritten for all entity types using the configuration keys `state_strings_on` and `state_strings_off`. For more granular control, use the entity specific configuration keys shown in the code extract below.

Expand All @@ -352,7 +429,6 @@ self.OVERRIDE_OFF_STATE = config.get("override_states_off", DEFAULT_OFF)
self.STATE_ON_STATE = config.get("state_states_on", DEFAULT_ON)
self.STATE_OFF_STATE = config.get("state_states_off", DEFAULT_OFF)
```

### Drawing State Machine Diagrams (not supported yet in `v2`)

You can generate state machine diagrams that update based on the state of the motion light. These produce a file in the file system that can be targeted by `file` based cameras.
Expand Down
30 changes: 20 additions & 10 deletions custom_components/entity_controller/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@
DEPENDENCIES = ["light", "sensor", "binary_sensor", "cover", "fan", "media_player"]
# REQUIREMENTS = ['transitions==0.6.9']

DOMAIN = "entity_controller"
from .const import (
DOMAIN,
STATES,
CONF_START_TIME,
CONF_END_TIME,
)

from .entity_services import (
async_setup_entity_services,
)

CONSTRAIN_START = 1
CONSTRAIN_END = 2

Expand Down Expand Up @@ -55,17 +65,8 @@
CONF_SENSOR_TYPE_DURATION = "sensor_type_duration"
CONF_SENSOR_TYPE = "sensor_type"
CONF_SENSOR_RESETS_TIMER = "sensor_resets_timer"
CONF_START_TIME = "start_time"
CONF_END_TIME = "end_time"
CONF_NIGHT_MODE = "night_mode"
CONF_STATE_ATTRIBUTES_IGNORE = "state_attributes_ignore"
STATES = [
"idle",
"overridden",
"constrained",
"blocked",
{"name": "active", "children": ["timer", "stay_on"], "initial": False},
]

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -131,6 +132,8 @@ async def async_setup(hass, config):
)
_LOGGER.info("Domain Configuration: " + str(myconfig))

async_setup_entity_services(component)

machine = Machine(
states=STATES,
initial="idle",
Expand Down Expand Up @@ -301,6 +304,13 @@ async def async_setup(hass, config):


class EntityController(entity.Entity):
from .entity_services import (
async_entity_service_clear_block as async_clear_block,
async_entitiy_service_set_stay_on as async_set_stay_on,
async_entitiy_service_set_stay_off as async_set_stay_off,
async_entity_service_set_night_mode as async_set_night_mode,
)

def __init__(self, hass, config, machine):
self.attributes = {}
self.may_update = False
Expand Down
20 changes: 20 additions & 0 deletions custom_components/entity_controller/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
""" Constants used by other files """

DOMAIN = "entity_controller"

# services
SERVICE_CLEAR_BLOCK = "clear_block"
SERVICE_SET_STAY_ON = "set_stay_on"
SERVICE_SET_STAY_OFF = "set_stay_off"
SERVICE_SET_NIGHT_MODE = "set_night_mode"

#configuration
CONF_START_TIME = 'start_time'
CONF_END_TIME = 'end_time'

MODE_DAY = 'day'
MODE_NIGHT = 'night'

STATES = ['idle', 'overridden', 'constrained', 'blocked',
{'name': 'active', 'children': ['timer', 'stay_on'],
'initial': False}]
99 changes: 99 additions & 0 deletions custom_components/entity_controller/entity_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
""" Entity Service Definitions """

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
make_entity_service_schema,
)

from homeassistant.helpers.entity_component import EntityComponent

from homeassistant.const import (
STATE_OFF,
STATE_ON,
)

import homeassistant.util.dt as dt_util

from .const import (
SERVICE_CLEAR_BLOCK,
SERVICE_SET_STAY_ON,
SERVICE_SET_STAY_OFF,
SERVICE_SET_NIGHT_MODE,
CONF_START_TIME,
CONF_END_TIME,
)

TIME_STR_FORMAT = "%H:%M:%S"

def async_setup_entity_services(component: EntityComponent):
""" Setup Entity services."""

component.logger.debug("Setting up entity services")
component.async_register_entity_service(SERVICE_CLEAR_BLOCK, {}, "async_clear_block")
component.async_register_entity_service(SERVICE_SET_STAY_ON, {}, "async_set_stay_on")
component.async_register_entity_service(SERVICE_SET_STAY_OFF, {}, "async_set_stay_off")
component.async_register_entity_service(
SERVICE_SET_NIGHT_MODE,
{ vol.Optional(CONF_START_TIME): cv.string, vol.Optional(CONF_END_TIME): cv.string },
"async_set_night_mode")

return True

def async_entity_service_clear_block(self):
""" Clear the block property, if set"""

if(self.model is None or self.model.state != 'blocked'):
return

self.model.log.debug("Clearing Blocked state")
self.model.block_timer_expires()

def async_entitiy_service_set_stay_on(self):
""" Changes the stay attribute with a custom value """

self.model.log.debug("Changing stay to on")
self.model.stay = True

def async_entitiy_service_set_stay_off(self):
""" Changes the stay attribute with a custom value """

self.model.log.debug("Changing stay to off")
self.model.stay = False

def async_entity_service_set_night_mode(self, start_time=None, end_time=None):
""" Changes the night mode start and/or end times """

if(self.model is None or self.model.night_mode is None):
return

now = None
if(start_time == 'now'):
if now is None:
now = dt_util.utcnow()
start_time = dt_util.as_local(now).strftime(TIME_STR_FORMAT)
elif(start_time == 'constraint'):
start_time = self.model._start_time_private

if(end_time == 'now'):
if now is None:
now = dt_util.utcnow()
end_time = dt_util.as_local(now).strftime(TIME_STR_FORMAT)
elif(end_time == 'constraint'):
end_time = self.model._end_time_private

if(start_time is None and end_time is None):
start_time = end_time = '00:00:00'

if(not start_time is None):
self.model.log.debug("Setting Night Mode Start to: %s", start_time)
self.model.night_mode[CONF_START_TIME] = start_time

if(not end_time is None):
self.model.log.debug("Setting Night Mode End to: %s", end_time)
self.model.night_mode[CONF_END_TIME] = end_time

self.model.prepare_service_data()


35 changes: 35 additions & 0 deletions custom_components/entity_controller/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
clear_block:
description: Clears the blocked state of an Entity Controller, if set
fields:
entity_id:
description: Name(s) of entities to change.
example: 'entity_controller.motion_light'

set_stay_on:
description: Change the stay to on
fields:
entity_id:
description: Name(s) of entities to change.
example: 'entity_controller.motion_light'

set_stay_off:
description: Change the stay to off
fields:
entity_id:
description: Name(s) of entities to change.
example: 'entity_controller.motion_light'

set_night_mode:
description: Change the night mode start and end times, does nothing
if night mode is not defined in configuration. If both start_time and end_time
are not provided, it will set both to midnight, effectivly disabling night mode.
fields:
entity_id:
description: Name(s) of entities to change.
example: 'entity_controller.motion_light'
start_time:
description: new start time to set night mode to.
example: sunset - 00:30:00 or '18:30:00' or now or constraint (set same as day start)
end_time:
description: new end time to set night mode to.
example: sunset + 03:00:00 or '21:30:00' or now or constraint (set same as day end)

0 comments on commit e5e18f5

Please sign in to comment.