Skip to content

Instantly share code, notes, and snippets.

@crhan
Last active September 19, 2018 14:40
Show Gist options
  • Select an option

  • Save crhan/a75c289d6dab373f3204cc51a9fa5c75 to your computer and use it in GitHub Desktop.

Select an option

Save crhan/a75c289d6dab373f3204cc51a9fa5c75 to your computer and use it in GitHub Desktop.

Revisions

  1. crhan revised this gist Jul 5, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@

    1. 支持光照度条件, 环境光足够亮就不开灯, 例如阳台灯
    2. 支持多个 sensor 共同触发, 例如卫生间可以放两个人体传感器, 玄关可以放一个门窗传感器+人体传感器
    3. 支持只关不开(厕所排气扇场景)或者只开不关(不知道为啥要这样, 你想到了告诉我)4. 支持延时关闭, 根据自身情况设定, 规避人体传感器不灵敏的问题
    3. 支持只关不开(厕所排气扇场景)或者只开不关(不知道为啥要这样, 你想到了告诉我)
    4. 支持延时关闭, 根据自身情况设定, 规避人体传感器不灵敏的问题

    这个功能不适合经常有人呆着的地方, 比如书房, 卧室, 客厅.
  2. crhan revised this gist Jul 5, 2018. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    支持的功能:

    1. 支持光照度条件, 环境光足够亮就不开灯, 例如阳台灯
    2. 支持多个 sensor 共同触发, 例如卫生间可以放两个人体传感器, 玄关可以放一个门窗传感器+人体传感器
    3. 支持只关不开(厕所排气扇场景)或者只开不关(不知道为啥要这样, 你想到了告诉我)4. 支持延时关闭, 根据自身情况设定, 规避人体传感器不灵敏的问题

    这个功能不适合经常有人呆着的地方, 比如书房, 卧室, 客厅.
  3. crhan revised this gist Jul 5, 2018. No changes.
  4. crhan created this gist Jul 5, 2018.
    47 changes: 47 additions & 0 deletions apps.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    hallway_motion_lights:
    module: motion_lights
    class: MotionLights
    sensor_list:
    - binary_sensor.door_window_sensor_158d0001191793
    - binary_sensor.motion_sensor_158d0001296340
    entity_on: group.hallway_lights
    entity_off: group.hallway_lights
    delay: 5

    inside_restroom_motion_lights:
    module: motion_lights
    class: MotionLights
    sensor_list:
    - binary_sensor.motion_sensor_158d0002231dad
    - binary_sensor.motion_sensor_158d000224f8df
    entity_on: switch.wall_switch_ln_left_158d00022a8a9d
    entity_off: switch.wall_switch_ln_left_158d00022a8a9d
    illumination_sensor: sensor.illumination_158d000224f8df
    illumination_throttle: 300
    delay: 300

    outside_restroom_motion_lights:
    module: motion_lights
    class: MotionLights
    sensor_list:
    - binary_sensor.motion_sensor_158d000211a0c9
    entity_on: switch.wall_switch_ln_right_158d00022a83d4
    entity_off: switch.wall_switch_ln_right_158d00022a83d4
    delay: 180

    inside_restroom_fan:
    module: motion_lights
    class: MotionLights
    delay: 120
    sensor_list:
    - binary_sensor.motion_sensor_158d0002231dad
    - binary_sensor.motion_sensor_158d000224f8df
    entity_off: switch.wall_switch_ln_right_158d00022a8a9d

    outside_restroom_fan:
    module: motion_lights
    class: MotionLights
    delay: 120
    sensor_list:
    - binary_sensor.motion_sensor_158d000211a0c9
    entity_off: switch.wall_switch_ln_left_158d00022a83d4
    156 changes: 156 additions & 0 deletions motion_lights.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,156 @@
    import appdaemon.plugins.hass.hassapi as hass

    #
    # App to turn lights on when motion detected then off again after a delay
    #
    # Use with constrints to activate only for the hours of darkness
    #
    # Args:
    #
    # sensor_list: binary_sensors to listen on, each sensor turn "on" will action on the switch
    # entity_on : entity to turn on when any sensor of sensor_list turned on, can be a light, script, scene or anything else that can be turned on
    # entity_off : entity to turn off when all sensor of sensor_list turned off , can be a light, script, scene or anything else that can be turned off
    # delay: amount of time after turning on to turn off again. If not specified defaults to 60 seconds.
    # illumination_sensor: illumination sensor to depends on
    # illumination_throttle: turn on switch if illumination_sensor state is below this config value, default is 500
    #
    # Release Notes
    #
    # Version 1.1:
    # Add ability for other apps to cancel the timer
    #
    # Version 1.0:
    # Initial Version


    class MotionLights(hass.Hass):
    def initialize(self):
    self.handle = None
    # Subscribe to sensors
    for _sensor in self.args['sensor_list']:
    self.listen_state(self.state_cb_sensor, _sensor)

    if self.entity_off:
    self.listen_state(self.state_cb_count_down_when_on,
    self.entity_off)

    self._start_turn_off_timer()

    def state_cb_sensor(self, entity, attribute, old, new, kwargs):
    if new == 'on':
    if not self.can_turn_entity_on:
    return
    self.log("turn on entity %s because of sensor %s state %s" %
    (self.entity_on, entity, new))
    self.turn_on(self.entity_on)
    self._start_turn_off_timer()

    if new == "off":
    if not self.can_turn_entity_off:
    return

    self.log("%s turn %s" % (entity, new))
    self._start_turn_off_timer()

    @property
    def illumination_switch(self):
    """光照条件开关
    配置 illumination_sensor 后启用
    光照临界值 illumination_throttle 默认 500
    Returns:
    Boolean -- True 时可以开灯
    """

    if self.illumination_sensor_state is None:
    return True
    self.log("illumination state %s, throttle %s" %
    (self.illumination_sensor_state, self.illumination_throttle))

    return self.illumination_sensor_state < self.illumination_throttle

    @property
    def illumination_throttle(self):
    """光照阈值, 超过时不满足开灯条件."""
    return float(self.args.get('illumination_throttle', 500))

    @property
    def illumination_sensor_state(self):
    if 'illumination_sensor' in self.args:
    illum_state = self.get_state(self.args['illumination_sensor'])
    try:
    return float(illum_state)
    except ValueError:
    return 0
    return None

    @property
    def delay(self):
    if "delay" in self.args:
    return self.args['delay']
    elif "delay_entity" in self.args:
    return int(self.get_state(self.args["delay_entity"]))
    return 30

    def state_cb_count_down_when_on(self, entity, attribute, old, new, kwargs):
    if new == "on":
    self.log("%s turn %s, start timer" % (entity, new))
    self._start_turn_off_timer()

    def _start_turn_off_timer(self):
    if not self.entity_off:
    return
    self.log("start count down %s seconds to turn off %s" %
    (self.delay, self.entity_off))
    self.cancel()
    self.handle = self.run_in(self._turn_off_entity, self.delay)
    self.log("start timer %s" % self.handle)

    def _turn_off_entity(self, kwargs):
    if self.can_turn_entity_off:
    self.log("Turning {} off".format(self.entity_off))
    self.turn_off(self.entity_off)

    def _turn_on_entity(self):
    if not self.entity_on:
    return
    if self.can_turn_entity_on:
    self.turn_on(self.entity_on)

    def cancel(self):
    if self.handle is not None:
    self.log("cancel timer %s" % self.handle)
    self.cancel_timer(self.handle)
    self.handle = None

    @property
    def entity_off(self):
    return self.args.get("entity_off")

    @property
    def entity_on(self):
    return self.args.get("entity_on")

    @property
    def can_turn_entity_off(self):
    if not self.entity_off:
    return False
    if self.get_state(self.entity_off) == "off":
    return False

    all_sensor_is_off = all([
    self.get_state(entity_id) == "off"
    for entity_id in self.args['sensor_list']
    ])
    return all_sensor_is_off

    @property
    def can_turn_entity_on(self):
    if not self.entity_on:
    return False
    if self.get_state(self.entity_on) == "on":
    return False

    return self.illumination_switch