コード例 #1
0
class SleepTimer(BaseSwitch):
    """Define a feature to turn a switch off after an amount of time."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_SWITCH): cv.entity_id,
                vol.Required(CONF_TIMER_SLIDER): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA,
        )
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self._on_switch_turned_off,
            self.entity_ids[CONF_SWITCH],
            new="off",
            constrain_enabled=True,
        )
        self.listen_state(
            self._on_timer_change,
            self.entity_ids[CONF_TIMER_SLIDER],
            constrain_enabled=True,
        )

    def _on_timer_change(self, entity: Union[str, dict], attribute: str,
                         old: str, new: str, kwargs: dict) -> None:
        """Start/stop a sleep timer for this switch."""
        minutes = int(float(new))

        if minutes == 0:
            self.log("Deactivating sleep timer")
            self.toggle(state="off")

            if HANDLE_TIMER in self.handles:
                cancel = self.handles.pop(HANDLE_TIMER)
                self.cancel_timer(cancel)
        else:
            self.log("Activating sleep timer: {0} minutes".format(minutes))
            self.toggle(state="on")
            self.handles[HANDLE_TIMER] = self.run_in(self._on_timer_complete,
                                                     minutes * 60)

    def _on_switch_turned_off(self, entity: Union[str, dict], attribute: str,
                              old: str, new: str, kwargs: dict) -> None:
        """Reset the sleep timer when the switch turns off."""
        self.set_value(self.entity_ids[CONF_TIMER_SLIDER], 0)

    def _on_timer_complete(self, kwargs: dict) -> None:
        """Turn off a switch at the end of sleep timer."""
        self.log("Sleep timer over; turning switch off")

        self.set_value(self.entity_ids[CONF_TIMER_SLIDER], 0)
コード例 #2
0
class BadLoginNotification(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify me of unauthorized login attempts."""

    APP_SCHEMA = APP_SCHEMA.extend(
        {
            CONF_ENTITY_IDS: vol.Schema(
                {
                    vol.Required(CONF_BAD_LOGIN): cv.entity_id,
                    vol.Required(CONF_IP_BAN): cv.entity_id,
                },
                extra=vol.ALLOW_EXTRA,
            )
        }
    )

    def configure(self) -> None:
        """Configure."""
        self._send_notification_func = None  # type: Optional[Callable]

        for notification_type in self.entity_ids.values():
            self.listen_state(
                self._on_bad_login,
                notification_type,
                attribute="all",
                constrain_enabled=True,
            )

    def _on_bad_login(
        self, entity: str, attribute: str, old: str, new: dict, kwargs: dict
    ) -> None:
        """Send a notification when there's a bad login attempt."""
        if not new:
            return

        if entity == self.entity_ids[CONF_BAD_LOGIN]:
            title = "Unauthorized Access Attempt"
        else:
            title = "IP Ban"

        def _send_notification() -> None:
            """Send a notification about the attempt."""
            send_notification(
                self, "person:Aaron", new["attributes"]["message"], title=title
            )

        if self.enabled:
            _send_notification()
        else:
            self._send_notification_func = _send_notification

    def on_enable(self) -> None:
        """Send the notification once the automation is enabled."""
        if self._send_notification_func:
            self._send_notification_func()
            self._send_notification_func = None
コード例 #3
0
ファイル: robot_vacuum.py プロジェクト: vipk31/smart-home
class NotifyWhenRunComplete(Base):
    """Define a feature to notify when the vacuum cycle is complete."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_PROPERTIES:
        vol.Schema({vol.Required(CONF_NOTIFICATION_INTERVAL): int},
                   extra=vol.ALLOW_EXTRA)
    })

    def configure(self) -> None:
        """Configure."""
        if self.enabled and self.app.bin_state == self.app.BinStates.full:
            self._start_notification_cycle()

        self.listen_state(self._on_vacuum_bin_change,
                          self.app.entity_ids[CONF_BIN_STATE])

    def _cancel_notification_cycle(self) -> None:
        """Cancel any active notification."""
        if HANDLE_BIN_FULL in self.handles:
            cancel = self.handles.pop(HANDLE_BIN_FULL)
            cancel()

    def _on_vacuum_bin_change(self, entity: Union[str, dict], attribute: str,
                              old: str, new: str, kwargs: dict) -> None:
        """Deal with changes to the bin."""
        if self.enabled and new == self.app.BinStates.full.value:
            self._start_notification_cycle()
        elif old == self.app.BinStates.full.value:
            self._cancel_notification_cycle()

    def _start_notification_cycle(self) -> None:
        """Start a repeating notification sequence."""
        self._cancel_notification_cycle()

        self.handles[HANDLE_BIN_FULL] = send_notification(
            self,
            "presence:home",
            "Empty him now and you won't have to do it later!",
            title="Wolfie Full 🤖",
            when=self.datetime(),
            interval=self.properties[CONF_NOTIFICATION_INTERVAL],
            data={"push": {
                "category": "dishwasher"
            }},
        )

    def on_disable(self) -> None:
        """Stop notifying when the automation is disabled."""
        self._cancel_notification_cycle()

    def on_enable(self) -> None:
        """Start notifying when the automation is enabled (if appropriate)."""
        if self.app.bin_state == self.app.BinStates.full:
            self._start_notification_cycle()
コード例 #4
0
ファイル: robot_vacuum.py プロジェクト: vipk31/smart-home
class NotifyWhenStuck(Base):
    """Define a feature to notify when the vacuum is stuck."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_PROPERTIES:
        vol.Schema({vol.Required(CONF_NOTIFICATION_INTERVAL): int},
                   extra=vol.ALLOW_EXTRA)
    })

    def configure(self) -> None:
        """Configure."""
        if self.enabled and self.app.state == self.app.States.error:
            self._start_notification_cycle()

        self.listen_state(self._on_error_change,
                          self.app.entity_ids[CONF_STATUS])

    def _cancel_notification_cycle(self) -> None:
        """Cancel any active notification."""
        if HANDLE_STUCK in self.handles:
            cancel = self.handles.pop(HANDLE_STUCK)
            cancel()

    def _on_error_change(self, entity: Union[str, dict], attribute: str,
                         old: str, new: str, kwargs: dict) -> None:
        """Notify when the vacuum is an error state."""
        if self.enabled and new == self.app.States.error.value:
            self._start_notification_cycle()
        elif old == self.app.States.error.value:
            self._cancel_notification_cycle()

    def _start_notification_cycle(self) -> None:
        """Start a repeating notification sequence."""
        self._cancel_notification_cycle()

        self.handles[HANDLE_STUCK] = send_notification(
            self,
            "presence:home",
            "Help him get back on track or home.",
            title="Wolfie Stuck 😢",
            when=self.datetime(),
            interval=self.properties[CONF_NOTIFICATION_INTERVAL],
            data={"push": {
                "category": "dishwasher"
            }},
        )

    def on_disable(self) -> None:
        """Stop notifying when the automation is disabled."""
        self._cancel_notification_cycle()

    def on_enable(self) -> None:
        """Start notifying when the automation is enabled (if appropriate)."""
        if self.app.state == self.app.States.error:
            self._start_notification_cycle()
コード例 #5
0
ファイル: version.py プロジェクト: buyfuturetoday/smart-home
class NewPortainerVersionNotification(DynamicSensor):
    """Detect new versions of Portainer-defined images."""

    APP_SCHEMA = DYNAMIC_APP_SCHEMA.extend({
        vol.Required(CONF_PROPERTIES):
        PROPERTIES_SCHEMA.extend({
            vol.Required(CONF_ENDPOINT_ID): int,
            vol.Required(CONF_IMAGE_NAME): str,
        })
    })

    API_URL = 'http://portainer:9000/api'

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_AVAILABLE): cv.entity_id,
                vol.Required(CONF_INSTALLED): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema({
            vol.Required(CONF_APP_NAME): str,
        }, extra=vol.ALLOW_EXTRA),
    })

    @property
    def sensor_value(self) -> Optional[str]:
        """Get the version from Portainer."""
        auth_resp = requests.post('{0}/auth'.format(self.API_URL),
                                  json={
                                      'Username':
                                      self.config['portainer_username'],
                                      'Password':
                                      self.config['portainer_password']
                                  }).json()
        token = auth_resp['jwt']

        images_resp = requests.get(
            '{0}/endpoints/{1}/docker/images/json'.format(
                self.API_URL, self.properties[CONF_ENDPOINT_ID]),
            headers={
                'Authorization': 'Bearer {0}'.format(token)
            }).json()

        try:
            tagged_image = next((i for image in images_resp
                                 for i in image['RepoTags']
                                 if self.properties[CONF_IMAGE_NAME] in i))
        except StopIteration:
            self.error('No match for image: {0}'.format(
                self.properties[CONF_IMAGE_NAME]))

        return tagged_image.split(':')[1].replace('v', '').split('-')[0]
コード例 #6
0
class MonitorConsumables(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify when a consumable gets low."""

    APP_SCHEMA = APP_SCHEMA.extend({
        vol.Required(CONF_CONSUMABLE_THRESHOLD):
        cv.positive_int,
        vol.Required(CONF_CONSUMABLES):
        vol.All(cv.ensure_list, [cv.string]),
    })

    def configure(self) -> None:
        """Configure."""
        self._consumables_met: List[str] = []
        self._send_notification_func: Optional[Callable] = None

        for consumable in self.args[CONF_CONSUMABLES]:
            self.listen_state(
                self._on_consumable_change,
                self.app.args[CONF_VACUUM],
                attribute=consumable,
            )

    def _on_consumable_change(self, entity: Union[str, dict], attribute: str,
                              old: str, new: str, kwargs: dict) -> None:
        """Create a task when a consumable is getting low."""
        def _send_notification() -> None:
            """Send the notification."""
            send_notification(self, "slack:@aaron",
                              f"Order a new Wolfie consumable: {attribute}")

        if int(new) < self.args[CONF_CONSUMABLE_THRESHOLD]:
            if attribute in self._consumables_met:
                return

            self._consumables_met.append(attribute)

            self.log("Consumable is low: %s", attribute)

            if self.enabled:
                _send_notification()
            else:
                self._send_notification_func = _send_notification
        else:
            if attribute not in self._consumables_met:
                return

            self._consumables_met.remove(attribute)

            self.log("Consumable is restored: %s", attribute)

    def on_enable(self) -> None:
        """Send the notification once the automation is enabled (if appropriate)."""
        if self._send_notification_func:
            self._send_notification_func()
            self._send_notification_func = None
コード例 #7
0
class AbsentInsecure(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify us when we've left home insecure."""

    APP_SCHEMA = APP_SCHEMA.extend(
        {
            CONF_ENTITY_IDS: vol.Schema(
                {vol.Required(CONF_STATE): cv.entity_id}, extra=vol.ALLOW_EXTRA
            )
        }
    )

    def configure(self) -> None:
        """Configure."""
        self._send_notification_func = None  # type: Optional[Callable]

        self.listen_state(
            self._on_house_insecure,
            self.entity_ids[CONF_STATE],
            new="Open",
            duration=60 * 5,
            constrain_enabled=True,
            constrain_noone="just_arrived,home",
        )

    def _on_house_insecure(
        self, entity: Union[str, dict], attribute: str, old: str, new: str, kwargs: dict
    ) -> None:
        """Send notifications when the house has been left insecure."""

        def _send_notification() -> None:
            """Send the notification."""
            send_notification(
                self,
                ["person:Aaron", "person:Britt"],
                "No one is home and the house isn't locked up.",
                title="Security Issue 🔐",
                data={"push": {"category": "dishwasher"}},
            )

        self.log("No one home and house is insecure; notifying")

        # If the automation is enabled when the house is insecure, send a notification;
        # if not, remember that we should send the notification when the automation
        # becomes enabled:
        if self.enabled:
            _send_notification()
        else:
            self._send_notification_func = _send_notification

    def on_enable(self) -> None:
        """Send the notification once the automation is enabled (if appropriate)."""
        if self._send_notification_func:
            self._send_notification_func()
            self._send_notification_func = None
コード例 #8
0
class Vacuum(Base):
    """Define an app to represent a vacuum-type appliance."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS: vol.Schema({
            vol.Required(CONF_BIN_STATE): cv.entity_id,
            vol.Required(CONF_STATUS): cv.entity_id,
            vol.Required(CONF_VACUUM): cv.entity_id,
        }, extra=vol.ALLOW_EXTRA),
    })

    @property
    def bin_state(self) -> Enum:
        """Define a property to get the bin state."""
        return self.BinStates(self.get_state(self.entity_ids['bin_state']))

    @bin_state.setter
    def bin_state(self, value: Enum) -> None:
        """Set the bin state."""
        self.select_option(self.entity_ids['bin_state'], value.value)

    class BinStates(Enum):
        """Define an enum for vacuum bin states."""

        empty = 'Empty'
        full = 'Full'

    class States(Enum):
        """Define an enum for vacuum states."""

        cleaning = 'Cleaning'
        docked = 'Docked'
        error = 'Error'
        idle = 'Idle'
        paused = 'Paused'
        remote_control = 'Remote Control'
        returning = 'Returning'

    def start(self) -> None:
        """Start a cleaning cycle."""
        self.log('Starting vacuuming cycle')

        if (self.security_manager.alarm_state ==
                self.security_manager.AlarmStates.away):
            self.log('Changing alarm state to "Home"')

            self.security_manager.set_alarm(
                self.security_manager.AlarmStates.home)
        else:
            self.log('Activating vacuum')

            self.call_service(
                'vacuum/start', entity_id=self.entity_ids['vacuum'])
コード例 #9
0
class NotifyBadAqi(Base):
    """Define a feature to notify us of bad air quality."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_AQI): cv.entity_id,
                vol.Required(CONF_HVAC_STATE): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema({
            vol.Required(CONF_AQI_THRESHOLD): int,
        },
                   extra=vol.ALLOW_EXTRA),
    })

    @property
    def current_aqi(self) -> int:
        """Define a property to get the current AQI."""
        return int(self.get_state(self.entity_ids[CONF_AQI]))

    def configure(self) -> None:
        """Configure."""
        self.notification_sent = False

        self.listen_state(self.bad_aqi_detected,
                          self.entity_ids[CONF_HVAC_STATE],
                          new='cooling',
                          constrain_input_boolean=self.enabled_entity_id)

    def bad_aqi_detected(self, entity: Union[str, dict], attribute: str,
                         old: str, new: str, kwargs: dict) -> None:
        """Send select notifications when cooling and poor AQI."""
        if (not self.notification_sent
                and self.current_aqi > self.properties[CONF_AQI_THRESHOLD]):
            self.log('Poor AQI; notifying anyone at home')

            self.notification_manager.send(
                'AQI is at {0}; consider closing the humidifier vent.'.format(
                    self.current_aqi),
                title='Poor AQI 😤',
                target='home')
            self.notification_sent = True
        elif (self.notification_sent
              and self.current_aqi <= self.properties[CONF_AQI_THRESHOLD]):
            self.notification_manager.send(
                'AQI is at {0}; open the humidifer vent again.'.format(
                    self.current_aqi),
                title='Better AQI 😅',
                target='home')
            self.notification_sent = True
コード例 #10
0
ファイル: car.py プロジェクト: vipk31/smart-home
class NotifyLowFuel(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify of the vehicle's ETA to home."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({vol.Required(CONF_CAR): cv.entity_id},
                   extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema(
            {
                vol.Required(CONF_FRIENDLY_NAME): str,
                vol.Required(CONF_FUEL_THRESHOLD): int,
                vol.Required(CONF_NOTIFICATION_TARGET): str,
            },
            extra=vol.ALLOW_EXTRA,
        ),
    })

    def configure(self):
        """Configure."""
        self.registered = False

        self.listen_state(
            self._on_low_fuel,
            self.entity_ids[CONF_CAR],
            attribute="fuel_level",
            constrain_enabled=True,
        )

    def _on_low_fuel(self, entity: Union[str, dict], attribute: str, old: str,
                     new: str, kwargs: dict) -> None:
        """Send a notification when my car is low on gas."""
        try:
            if int(new) < self.properties["fuel_threshold"]:
                if self.registered:
                    return

                self.log("Low fuel detected detected: {0}".format(
                    self.entity_ids[CONF_CAR]))

                self.registered = True
                send_notification(
                    self,
                    self.properties[CONF_NOTIFICATION_TARGET],
                    "{0} needs gas; fill 'er up!.".format(
                        self.properties[CONF_FRIENDLY_NAME]),
                    title="{0} is Low ⛽".format(
                        self.properties[CONF_FRIENDLY_NAME]),
                )
            else:
                self.registered = False
        except ValueError:
            return
コード例 #11
0
class LeftInState(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to monitor whether an entity is left in a state."""

    APP_SCHEMA = APP_SCHEMA.extend({
        vol.Required(CONF_ENTITY_ID):
        cv.entity_id,
        vol.Required(CONF_DURATION):
        vol.All(cv.time_period, lambda value: value.seconds),
        vol.Required(CONF_STATE):
        cv.string,
        vol.Required(CONF_NOTIFICATION_TARGET):
        vol.All(cv.ensure_list, [cv.notification_target]),
    })

    def configure(self) -> None:
        """Configure."""
        self._send_notification_func = None  # type: Optional[Callable]

        self.listen_state(
            self._on_limit,
            self.args[CONF_ENTITY_ID],
            new=self.args[CONF_STATE],
            duration=self.args[CONF_DURATION],
        )

    def _on_limit(self, entity: Union[str, dict], attribute: str, old: str,
                  new: str, kwargs: dict) -> None:
        """Notify when the threshold is reached."""
        def _send_notification() -> None:
            """Send a notification."""
            friendly_name = self.get_state(self.args[CONF_ENTITY_ID],
                                           attribute="friendly_name")
            send_notification(
                self,
                self.args[CONF_NOTIFICATION_TARGET],
                (f"{friendly_name} -> {self.args[CONF_STATE]} "
                 f"({self.args[CONF_DURATION]} seconds)"),
                title="Entity Alert",
            )

        # If the automation is enabled when the limit is reached, send a notification;
        # if not, remember that we should send the notification when the automation
        # becomes enabled:
        if self.enabled:
            _send_notification()
        else:
            self._send_notification_func = _send_notification

    def on_enable(self) -> None:
        """Send the notification once the automation is enabled (if appropriate)."""
        if self._send_notification_func:
            self._send_notification_func()
            self._send_notification_func = None
コード例 #12
0
class LowMoisture(Base):
    """Define a feature to notify us of low moisture."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({
            vol.Required(CONF_CURRENT_MOISTURE): cv.entity_id,
        },
                   extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema(
            {
                vol.Required(CONF_FRIENDLY_NAME): str,
                vol.Required(CONF_MOISTURE_THRESHOLD): int,
                vol.Required(CONF_NOTIFICATION_INTERVAL): int,
            },
            extra=vol.ALLOW_EXTRA),
    })

    def configure(self) -> None:
        """Configure."""
        self._low_moisture = False

        self.listen_state(self.low_moisture_detected,
                          self.entity_ids['current_moisture'],
                          constrain_input_boolean=self.enabled_entity_id)

    @property
    def current_moisture(self) -> int:
        """Define a property to get the current moisture."""
        return int(self.get_state(self.entity_ids['current_moisture']))

    def low_moisture_detected(self, entity: Union[str, dict], attribute: str,
                              old: str, new: str, kwargs: dict) -> None:
        """Notify when the plant's moisture is low."""
        if (not self._low_moisture
                and int(new) < int(self.properties['moisture_threshold'])):
            self.log('Notifying people at home that plant is low on moisture')

            self._low_moisture = True
            self.handles[
                HANDLE_LOW_MOISTURE] = self.notification_manager.repeat(
                    '{0} is at {1}% moisture and needs water.'.format(
                        self.properties['friendly_name'],
                        self.current_moisture),
                    self.properties['notification_interval'],
                    title='{0} is Dry 💧'.format(
                        self.properties['friendly_name']),
                    target='home')
        else:
            self._low_moisture = False
            if HANDLE_LOW_MOISTURE in self.handles:
                self.handles.pop(HANDLE_LOW_MOISTURE)()  # type: ignore
コード例 #13
0
class ServiceOnZWaveSwitchDoubleTap(Base):  # pylint: disable=too-few-public-methods
    """Define an automation to call a service when a Z-Wave switch double-tap occurs."""

    APP_SCHEMA = APP_SCHEMA.extend({
        vol.Required(CONF_SERVICES):
        vol.All(
            vol.Schema({
                vol.Inclusive(CONF_SERVICE_UP, "up"): cv.string,
                vol.Inclusive(CONF_SERVICE_UP_DATA, "up"): dict,
                vol.Inclusive(CONF_SERVICE_DOWN, "down"): cv.string,
                vol.Inclusive(CONF_SERVICE_DOWN_DATA, "down"): dict,
            }),
            cv.has_at_least_one_key(CONF_SERVICE_UP, CONF_SERVICE_DOWN),
        ),
        vol.Required(CONF_ZWAVE_DEVICE):
        cv.entity_id,
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_event(
            self._on_double_tap_up,
            "zwave.node_event",
            entity_id=self.args[CONF_ZWAVE_DEVICE],
            basic_level=255,
        )

        self.listen_event(
            self._on_double_tap_down,
            "zwave.node_event",
            entity_id=self.args[CONF_ZWAVE_DEVICE],
            basic_level=0,
        )

    def _on_double_tap_down(self, event_name: str, data: dict,
                            kwargs: dict) -> None:
        """Call the "down" service."""
        if CONF_SERVICE_DOWN not in self.args[CONF_SERVICES]:
            self.log("No service defined for double-tap down")
            return

        self.call_service(self.args[CONF_SERVICES][CONF_SERVICE_DOWN],
                          **self.args[CONF_SERVICES][CONF_SERVICE_DOWN_DATA])

    def _on_double_tap_up(self, event_name: str, data: dict,
                          kwargs: dict) -> None:
        """Call the "up" service."""
        if CONF_SERVICE_UP not in self.args[CONF_SERVICES]:
            self.log("No service defined for double-tap up")
            return

        self.call_service(self.args[CONF_SERVICES][CONF_SERVICE_UP],
                          **self.args[CONF_SERVICES][CONF_SERVICE_UP_DATA])
コード例 #14
0
class NotifyLowFuel(Base):
    """Define a feature to notify of the vehicle's ETA to home."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({
            vol.Required(CONF_CAR): cv.entity_id,
        },
                   extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema(
            {
                vol.Required(CONF_FRIENDLY_NAME): str,
                vol.Required(CONF_FUEL_THRESHOLD): int,
                vol.Required(CONF_NOTIFICATION_TARGET): str,
            },
            extra=vol.ALLOW_EXTRA),
    })

    def configure(self):
        """Configure."""
        self.registered = False

        self.listen_state(self.low_fuel_found,
                          self.entity_ids[CONF_CAR],
                          attribute='fuel_level',
                          constrain_input_boolean=self.enabled_entity_id)

    def low_fuel_found(self, entity: Union[str, dict], attribute: str,
                       old: str, new: str, kwargs: dict) -> None:
        """Send a notification when my car is low on gas."""
        try:
            if int(new) < self.properties['fuel_threshold']:
                if self.registered:
                    return

                self.log('Low fuel detected detected: {0}'.format(
                    self.entity_ids[CONF_CAR]))

                self.registered = True
                self.notification_manager.send(
                    "{0} needs gas; fill 'er up!.".format(
                        self.properties[CONF_FRIENDLY_NAME]),
                    title='{0} is Low ⛽'.format(
                        self.properties[CONF_FRIENDLY_NAME]),
                    target=self.properties[CONF_NOTIFICATION_TARGET])
            else:
                self.registered = False
        except ValueError:
            return
コード例 #15
0
class ToggleOnNumericThreshold(BaseSwitch):
    """Define a feature to toggle the switch above/below a threshold."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_SWITCH): cv.entity_id,
                vol.Required(CONF_TARGET): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA,
        ),
        CONF_PROPERTIES:
        vol.All(
            vol.Schema(
                {
                    vol.Required(CONF_STATE): vol.In(TOGGLE_STATES),
                    vol.Optional(CONF_ABOVE): int,
                    vol.Optional(CONF_BELOW): int,
                },
                extra=vol.ALLOW_EXTRA,
            ),
            cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
        ),
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self._on_state_change,
            self.entity_ids[CONF_TARGET],
            auto_constraints=True,
            constrain_enabled=True,
        )

    def _on_state_change(self, entity: Union[str, dict], attribute: str,
                         old: str, new: str, kwargs: dict) -> None:
        """Toggle the switch if outside the threshold."""
        new_value = float(new)

        above = self.properties.get(CONF_ABOVE)
        below = self.properties.get(CONF_BELOW)

        if above and new_value >= above:
            self.toggle(state=self.properties[CONF_STATE])
        elif below and new_value < below:
            self.toggle(state=self.properties[CONF_STATE])
        else:
            self.toggle(opposite_of=self.properties[CONF_STATE])
コード例 #16
0
class ToggleAtTime(BaseSwitch):
    """Define a feature to toggle a switch at a certain time."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({vol.Required(CONF_SWITCH): cv.entity_id},
                   extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema(
            {
                vol.Required(CONF_SCHEDULE_TIME):
                vol.Any(str, vol.In(SOLAR_EVENTS)),
                vol.Required(CONF_STATE):
                vol.In(TOGGLE_STATES),
                vol.Optional(CONF_RUN_ON_DAYS):
                cv.ensure_list,
            },
            extra=vol.ALLOW_EXTRA,
        ),
    })

    def configure(self) -> None:
        """Configure."""
        kwargs = {
            "state": self.properties[CONF_STATE],
            "constrain_enabled": True
        }

        if self.properties[CONF_SCHEDULE_TIME] in SOLAR_EVENTS:
            method = getattr(
                self, "run_at_{0}".format(self.properties[CONF_SCHEDULE_TIME]))
            method(self._on_schedule_toggle, auto_constraints=True, **kwargs)
        else:
            if self.properties.get(CONF_RUN_ON_DAYS):
                run_on_days(
                    self,
                    self._on_schedule_toggle,
                    self.properties[CONF_RUN_ON_DAYS],
                    self.parse_time(self.properties[CONF_SCHEDULE_TIME]),
                    auto_constraints=True,
                    **kwargs,
                )
            else:
                self.run_daily(
                    self._on_schedule_toggle,
                    self.parse_time(self.properties[CONF_SCHEDULE_TIME]),
                    auto_constraints=True,
                    **kwargs,
                )
コード例 #17
0
class ToggleOnState(BaseSwitch):
    """Define a feature to toggle the switch when an entity enters a state."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_SWITCH): cv.entity_id,
                vol.Required(CONF_TARGET): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA,
        ),
        CONF_PROPERTIES:
        vol.Schema(
            {
                vol.Required(CONF_SWITCH_STATE): vol.In(TOGGLE_STATES),
                vol.Required(CONF_TARGET_STATE): vol.In(TOGGLE_STATES),
                vol.Optional(CONF_DELAY): int,
            },
            extra=vol.ALLOW_EXTRA,
        ),
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self._on_state_change,
            self.entity_ids[CONF_TARGET],
            auto_constraints=True,
            constrain_enabled=True,
        )

    def _on_state_change(self, entity: Union[str, dict], attribute: str,
                         old: str, new: str, kwargs: dict) -> None:
        """Toggle the switch depending on the target entity's state."""
        if new == self.properties[CONF_TARGET_STATE]:
            if self.properties.get(CONF_DELAY):
                self.handles[HANDLE_TOGGLE_STATE] = self.run_in(
                    self._on_schedule_toggle,
                    self.properties[CONF_DELAY],
                    state=self.properties[CONF_SWITCH_STATE],
                )
            else:
                self.toggle(state=self.properties[CONF_SWITCH_STATE])
        else:
            if HANDLE_TOGGLE_STATE in self.handles:
                handle = self.handles.pop(HANDLE_TOGGLE_STATE)
                self.cancel_timer(handle)
コード例 #18
0
ファイル: version.py プロジェクト: zoharaharoni/smart-home
class NewPortainerVersionNotification(DynamicSensor):
    """Detect new versions of Portainer-defined images."""

    APP_SCHEMA = DYNAMIC_APP_SCHEMA.extend({
        vol.Required(CONF_ENDPOINT_ID):
        cv.positive_int,
        vol.Required(CONF_IMAGE_NAME):
        cv.string,
    })

    API_URL = "http://portainer:9000/api"

    APP_SCHEMA = APP_SCHEMA.extend({
        vol.Required(CONF_AVAILABLE): cv.entity_id,
        vol.Required(CONF_INSTALLED): cv.entity_id,
        vol.Required(CONF_APP_NAME): str,
    })

    @property
    def sensor_value(self) -> Optional[str]:
        """Get the version from Portainer."""
        auth_resp = requests.post(
            f"{self.API_URL}/auth",
            json={
                "Username": self.config["portainer_username"],
                "Password": self.config["portainer_password"],
            },
        ).json()
        token = auth_resp["jwt"]

        images_resp = requests.get(
            (f"{self.API_URL}/endpoints/{self.args[CONF_ENDPOINT_ID]}"
             "/docker/images/json"),
            headers={
                "Authorization": f"Bearer {token}"
            },
        ).json()

        try:
            tagged_image = next((i for image in images_resp
                                 for i in image["RepoTags"]
                                 if self.args[CONF_IMAGE_NAME] in i))
        except StopIteration:
            self.error("No match for image: %s", self.args[CONF_IMAGE_NAME])
            return None

        return tagged_image.split(":")[1].replace("v", "").split("-")[0]
コード例 #19
0
ファイル: button.py プロジェクト: zoharaharoni/smart-home
class AmazonDashButton(Button):
    """Define an Amazon Dash button."""

    APP_SCHEMA = APP_SCHEMA.extend(
        {
            vol.Required(CONF_ACTION_LIST): cv.entity_id,
            vol.Required(CONF_FRIENDLY_NAME): cv.string,
        }
    )

    def configure(self) -> None:
        """Configure."""
        self.listen_event(
            self._on_button_press,
            "AMAZON_DASH_PRESS",
            button_label=self.args[CONF_FRIENDLY_NAME],
        )
コード例 #20
0
ファイル: systems.py プロジェクト: buyfuturetoday/smart-home
class LeftInState(Base):
    """Define a feature to monitor whether an entity is left in a state."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS: vol.Schema({
            vol.Required(CONF_ENTITY): cv.entity_id,
        }, extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES: vol.Schema({
            vol.Required(CONF_DURATION): int,
            vol.Required(CONF_STATE): str,
        }, extra=vol.ALLOW_EXTRA),
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self.limit_reached,
            self.entity_ids[CONF_ENTITY],
            new=self.properties[CONF_STATE],
            duration=self.properties[CONF_DURATION],
            constrain_input_boolean=self.enabled_entity_id)

    def limit_reached(
            self, entity: Union[str, dict], attribute: str, old: str, new: str,
            kwargs: dict) -> None:
        """Notify when the threshold is reached."""

        def turn_off():
            """Turn the entity off."""
            self.turn_off(self.entity_ids[CONF_ENTITY])

        self.slack_app_home_assistant.ask(
            'The {0} has been left {1} for {2} minutes. Turn it off?'.format(
                self.get_state(
                    self.entity_ids[CONF_ENTITY], attribute='friendly_name'),
                self.properties[CONF_STATE],
                int(self.properties[CONF_DURATION]) / 60),
            {
                'Yes': {
                    'callback': turn_off,
                    'response_text': 'You got it; turning it off now.'
                },
                'No': {
                    'response_text': 'Keep devouring electricity, little guy.'
                }
            })
コード例 #21
0
class AaronAccountability(Base):
    """Define features to keep me accountable on my phone."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({
            vol.Required(CONF_AARON_ROUTER_TRACKER): cv.entity_id,
        },
                   extra=vol.ALLOW_EXTRA),
    })

    def configure(self) -> None:
        """Configure."""
        self.run_daily(self.send_notification_when_blackout_start,
                       self.parse_time(BLACKOUT_START))

        self.listen_state(self.send_notification_on_disconnect,
                          self.entity_ids[CONF_AARON_ROUTER_TRACKER],
                          new='not_home',
                          constrain_in_blackout=True,
                          constrain_anyone='home')

    @property
    def router_tracker_state(self) -> str:
        """Return the state of Aaron's Unifi tracker."""
        return self.get_state(self.entity_ids[CONF_AARON_ROUTER_TRACKER])

    def _send_notification(self) -> None:
        """Send notification to my love."""
        self.notification_manager.send(
            "His phone shouldn't be off wifi during the night.",
            title='Check on Aaron',
            target='Britt')

    def send_notification_when_blackout_start(self, kwargs: dict) -> None:
        """Send a notification if offline when blackout starts."""
        if (self.aaron.home_state == self.presence_manager.HomeStates.home
                and self.router_tracker_state == 'not_home'):
            self._send_notification()

    def send_notification_on_disconnect(self, entity: Union[str, dict],
                                        attribute: str, old: str, new: str,
                                        kwargs: dict) -> None:
        """Send a notification when I disconnect during a blackout."""
        self._send_notification()
コード例 #22
0
class NotifyLowFuel(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify of the vehicle's ETA to home."""

    APP_SCHEMA = APP_SCHEMA.extend({
        vol.Required(CONF_CAR):
        cv.entity_id,
        vol.Required(CONF_FRIENDLY_NAME):
        cv.string,
        vol.Required(CONF_FUEL_THRESHOLD):
        cv.positive_int,
        vol.Required(CONF_NOTIFICATION_TARGET):
        cv.notification_target,
    })

    def configure(self):
        """Configure."""
        self.registered = False

        self.listen_state(self._on_low_fuel,
                          self.args[CONF_CAR],
                          attribute="fuel_level")

    def _on_low_fuel(self, entity: Union[str, dict], attribute: str, old: str,
                     new: str, kwargs: dict) -> None:
        """Send a notification when my car is low on gas."""
        try:
            if int(new) < self.args["fuel_threshold"]:
                if self.registered:
                    return

                self.registered = True

                self.log("Low fuel detected detected: %s", self.args[CONF_CAR])

                send_notification(
                    self,
                    self.args[CONF_NOTIFICATION_TARGET],
                    f"{self.args[CONF_FRIENDLY_NAME]} needs gas; fill 'er up!.",
                    title=f"{self.args[CONF_FRIENDLY_NAME]} is low ⛽",
                )
            else:
                self.registered = False
        except ValueError:
            return
コード例 #23
0
ファイル: button.py プロジェクト: vipk31/smart-home
class AmazonDashButton(Button):
    """Define an Amazon Dash button."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema({vol.Required(CONF_ACTION_LIST): cv.entity_id},
                   extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema({vol.Required(CONF_FRIENDLY_NAME): str},
                   extra=vol.ALLOW_EXTRA),
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_event(
            self._on_button_press,
            "AMAZON_DASH_PRESS",
            button_label=self.properties[CONF_FRIENDLY_NAME],
        )
コード例 #24
0
ファイル: switches.py プロジェクト: zoharaharoni/smart-home
class PresenceFailsafe(BaseSwitch):
    """Define a feature to restrict activation when we're not home."""

    APP_SCHEMA = APP_SCHEMA.extend({vol.Required(CONF_SWITCH): cv.entity_id})

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self._on_switch_activate,
            self.args[CONF_SWITCH],
            new="on",
            constrain_noone="just_arrived,home",
        )

    def _on_switch_activate(
        self, entity: Union[str, dict], attribute: str, old: str, new: str, kwargs: dict
    ) -> None:
        """Turn the switch off if no one is home."""
        self.log("No one home; not allowing switch to activate")
        self.toggle(state="off")
コード例 #25
0
ファイル: button.py プロジェクト: zoharaharoni/smart-home
class ZWaveButton(Button):
    """Define a Z-Wave button."""

    APP_SCHEMA = APP_SCHEMA.extend(
        {
            vol.Required(CONF_ENTITY_ID): cv.entity_id,
            vol.Required(CONF_ACTION_LIST): cv.entity_id,
            vol.Required(CONF_SCENE_ID): cv.positive_int,
            vol.Required(CONF_SCENE_DATA): cv.positive_int,
        }
    )

    def configure(self) -> None:
        """Configure."""
        self.listen_event(
            self._on_button_press,
            "zwave.scene_activated",
            scene_id=self.args[CONF_SCENE_ID],
            scene_data=self.args[CONF_SCENE_DATA],
        )
コード例 #26
0
class NotifyOnChange(Base):  # pylint: disable=too-few-public-methods
    """Define a feature to notify us the secure status changes."""

    APP_SCHEMA = APP_SCHEMA.extend(
        {
            CONF_ENTITY_IDS: vol.Schema(
                {vol.Required(CONF_STATE): cv.entity_id}, extra=vol.ALLOW_EXTRA
            )
        }
    )

    def configure(self) -> None:
        """Configure."""
        self._send_notification_func = None  # type: Optional[Callable]

        self.listen_state(self._on_security_system_change, self.entity_ids[CONF_STATE])

    def _on_security_system_change(
        self, entity: Union[str, dict], attribute: str, old: str, new: str, kwargs: dict
    ) -> None:
        """Send a notification when the security state changes."""

        def _send_notification() -> None:
            """Send the notification."""
            send_notification(
                self,
                ["person:Aaron", "person:Britt"],
                'The security status has changed to "{0}"'.format(new),
                title="Security Change 🔐",
            )

        if self.enabled:
            _send_notification()
        else:
            self._send_notification_func = _send_notification

    def on_enable(self) -> None:
        """Send the notification once the automation is enabled (if appropriate)."""
        if self._send_notification_func:
            self._send_notification_func()
            self._send_notification_func = None
コード例 #27
0
ファイル: switches.py プロジェクト: buyfuturetoday/smart-home
class DoubleTapTimerSwitch(BaseZwaveSwitch):
    """Define a feature to double tap a switch on for a time."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {
                vol.Required(CONF_TIMER_SLIDER): cv.entity_id,
                vol.Required(CONF_ZWAVE_DEVICE): cv.entity_id,
            },
            extra=vol.ALLOW_EXTRA),
        CONF_PROPERTIES:
        vol.Schema({
            vol.Required(CONF_DURATION): int,
        }, extra=vol.ALLOW_EXTRA)
    })

    def double_up(self, event_name: str, data: dict, kwargs: dict) -> None:
        """Turn on the target timer slider with a double up tap."""
        self.set_value(self.entity_ids[CONF_TIMER_SLIDER],
                       round(self.properties[CONF_DURATION] / 60))
コード例 #28
0
ファイル: systems.py プロジェクト: vipk31/smart-home
class AaronAccountability(Base):
    """Define features to keep me accountable on my phone."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_ENTITY_IDS:
        vol.Schema(
            {vol.Required(CONF_AARON_ROUTER_TRACKER): cv.entity_id},
            extra=vol.ALLOW_EXTRA,
        )
    })

    def configure(self) -> None:
        """Configure."""
        self.listen_state(
            self._on_disconnect,
            self.entity_ids[CONF_AARON_ROUTER_TRACKER],
            new="not_home",
            constrain_in_blackout=True,
            constrain_anyone="home",
        )

    @property
    def router_tracker_state(self) -> str:
        """Return the state of Aaron's Unifi tracker."""
        return self.get_state(self.entity_ids[CONF_AARON_ROUTER_TRACKER])

    def _on_disconnect(self, entity: Union[str, dict], attribute: str,
                       old: str, new: str, kwargs: dict) -> None:
        """Send a notification when I disconnect during a blackout."""
        self._send_notification()

    def _send_notification(self) -> None:
        """Send notification to my love."""
        send_notification(
            self,
            "ios_brittany_bachs_iphone",
            "His phone shouldn't be off wifi during the night.",
            title="Check on Aaron",
        )
コード例 #29
0
ファイル: pin.py プロジェクト: vipk31/smart-home
class SimpliSafePIN(PIN):
    """Define a PIN manager for a SimpliSafe security system."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_PROPERTIES:
        vol.Schema({vol.Required(CONF_SYSTEM_ID): int}, extra=vol.ALLOW_EXTRA)
    })

    def configure(self) -> None:
        """Configure."""
        super().configure()

        self._system_id = self.properties[CONF_SYSTEM_ID]

    def _listen_for_otp_use(self) -> None:
        """Listen for the use of a one-time PIN."""
        self.handles[HANDLE_ONE_TIME_STUB.format(
            self.name)] = self.listen_state(self._on_otp_used,
                                            SIMPLISAFE_ENTITY_ID,
                                            attribute="changed_by")

    def _pin_is_otp(self, pin: str) -> bool:
        """Return whether a detected PIN is a valid one-time PIN."""
        return pin == self.label

    def remove_pin(self) -> None:
        """Remove the PIN from SimpliSafe."""
        self.call_service("simplisafe/remove_pin",
                          system_id=self._system_id,
                          label_or_pin=self.label)

    def set_pin(self) -> None:
        """Add the pin to SimpliSafe."""
        self.call_service(
            "simplisafe/set_pin",
            system_id=self._system_id,
            label=self.label,
            pin=self.value,
        )
コード例 #30
0
class MonitorConsumables(Base):
    """Define a feature to notify when a consumable gets low."""

    APP_SCHEMA = APP_SCHEMA.extend({
        CONF_PROPERTIES: vol.Schema({
            vol.Required(CONF_CONSUMABLE_THRESHOLD): int,
            vol.Required(CONF_CONSUMABLES): cv.ensure_list,
        }, extra=vol.ALLOW_EXTRA),
    })

    def configure(self) -> None:
        """Configure."""
        self.triggered = False

        for consumable in self.properties['consumables']:
            self.listen_state(
                self.consumable_changed,
                self.app.entity_ids['vacuum'],
                attribute=consumable,
                constrain_input_boolean=self.enabled_entity_id)

    def consumable_changed(
            self, entity: Union[str, dict], attribute: str, old: str, new: str,
            kwargs: dict) -> None:
        """Create a task when a consumable is getting low."""
        if int(new) < self.properties['consumable_threshold']:
            if self.triggered:
                return

            self.log('Consumable is low: {0}'.format(attribute))

            self.notification_manager.create_omnifocus_task(
                'Order a new Wolfie consumable: {0}'.format(attribute))

            self.triggered = True
        elif self.triggered:
            self.triggered = False