示例#1
0
    def __call__(self, data: Any) -> str | list[str]:
        """Validate the passed selection."""

        include_entities = self.config.get("include_entities")
        exclude_entities = self.config.get("exclude_entities")

        def validate(e_or_u: str) -> str:
            e_or_u = cv.entity_id_or_uuid(e_or_u)
            if not valid_entity_id(e_or_u):
                return e_or_u
            if allowed_domains := cv.ensure_list(self.config.get("domain")):
                domain = split_entity_id(e_or_u)[0]
                if domain not in allowed_domains:
                    raise vol.Invalid(
                        f"Entity {e_or_u} belongs to domain {domain}, "
                        f"expected {allowed_domains}")
            if include_entities:
                vol.In(include_entities)(e_or_u)
            if exclude_entities:
                vol.NotIn(exclude_entities)(e_or_u)
            return e_or_u
示例#2
0
    cv.temperature_unit,
    CONF_UNIT_SYSTEM:
    cv.unit_system,
    CONF_TIME_ZONE:
    cv.time_zone,
    vol.Optional(CONF_WHITELIST_EXTERNAL_DIRS):
    # pylint: disable=no-value-for-parameter
    vol.All(cv.ensure_list, [vol.IsDir()]),
    vol.Optional(CONF_PACKAGES, default={}):
    PACKAGES_CONFIG_SCHEMA,
    vol.Optional(CONF_AUTH_PROVIDERS):
    vol.All(cv.ensure_list, [
        auth_providers.AUTH_PROVIDER_SCHEMA.extend({
            CONF_TYPE:
            vol.NotIn(['insecure_example'],
                      'The insecure_example auth provider'
                      ' is for testing only.')
        })
    ], _no_duplicate_auth_provider),
    vol.Optional(CONF_AUTH_MFA_MODULES):
    vol.All(cv.ensure_list, [
        auth_mfa_modules.MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({
            CONF_TYPE:
            vol.NotIn(['insecure_example'], 'The insecure_example mfa module'
                      ' is for testing only.')
        })
    ], _no_duplicate_auth_mfa_module),
})


def get_default_config_dir() -> str:
示例#3
0
PY_XIAOMI_GATEWAY = "xiaomi_gw"

TIME_TILL_UNAVAILABLE = timedelta(minutes=150)

SERVICE_PLAY_RINGTONE = 'play_ringtone'
SERVICE_STOP_RINGTONE = 'stop_ringtone'
SERVICE_ADD_DEVICE = 'add_device'
SERVICE_REMOVE_DEVICE = 'remove_device'

GW_MAC = vol.All(cv.string, lambda value: value.replace(':', '').lower(),
                 vol.Length(min=12, max=12))

SERVICE_SCHEMA_PLAY_RINGTONE = vol.Schema({
    vol.Required(ATTR_RINGTONE_ID):
    vol.All(vol.Coerce(int), vol.NotIn([9, 14, 15, 16, 17, 18, 19])),
    vol.Optional(ATTR_RINGTONE_VOL):
    vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100))
})

SERVICE_SCHEMA_REMOVE_DEVICE = vol.Schema({
    vol.Required(ATTR_DEVICE_ID):
    vol.All(cv.string, vol.Length(min=14, max=14))
})

GATEWAY_CONFIG = vol.Schema({
    vol.Optional(CONF_MAC, default=None):
    vol.Any(GW_MAC, None),
    vol.Optional(CONF_KEY):
    vol.All(cv.string, vol.Length(min=16, max=16)),
    vol.Optional(CONF_HOST):
示例#4
0
 CONF_ELEVATION: vol.Coerce(int),
 vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
 CONF_UNIT_SYSTEM: cv.unit_system,
 CONF_TIME_ZONE: cv.time_zone,
 vol.Optional(CONF_WHITELIST_EXTERNAL_DIRS):
 # pylint: disable=no-value-for-parameter
 vol.All(cv.ensure_list, [vol.IsDir()]),
 vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA,
 vol.Optional(CONF_AUTH_PROVIDERS): vol.All(
     cv.ensure_list,
     [
         auth_providers.AUTH_PROVIDER_SCHEMA.extend(
             {
                 CONF_TYPE: vol.NotIn(
                     ["insecure_example"],
                     "The insecure_example auth provider"
                     " is for testing only.",
                 )
             }
         )
     ],
     _no_duplicate_auth_provider,
 ),
 vol.Optional(CONF_AUTH_MFA_MODULES): vol.All(
     cv.ensure_list,
     [
         auth_mfa_modules.MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend(
             {
                 CONF_TYPE: vol.NotIn(
                     ["insecure_example"],
                     "The insecure_example mfa module" " is for testing only.",
示例#5
0
        if self.is_off:
            return "OFF"
        return str(self.value)


TEMP_SCHEMA = vol.Schema(
    vol.All(
        vol.Any(float, int, Off, vol.All(str, lambda v: v.upper(), "OFF")),
        lambda v: Temp(v),  # pylint: disable=unnecessary-lambda
    ))

CONFIG_SCHEMA = vol.Schema(
    {
        vol.Optional("delta", default=0):
        vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])),
        vol.Optional("min_temp", default=None):
        vol.Any(
            vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])),
            None,
        ),
        vol.Optional("max_temp", default=None):
        vol.Any(
            vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])),
            None,
        ),
        vol.Optional("off_temp", default=OFF):
        TEMP_SCHEMA,
        vol.Optional("supports_opmodes", default=True):
        bool,
        vol.Optional("opmode_heat", default="heat"):
示例#6
0
class ThermostatActor(ActorBase):
    """A thermostat to be controlled by Schedy."""

    name = "thermostat"
    config_schema_dict = {
        **ActorBase.config_schema_dict,
        vol.Optional("delta", default=0):
        vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])),
        vol.Optional("min_temp", default=None):
        vol.Any(vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])), None),
        vol.Optional("max_temp", default=None):
        vol.Any(vol.All(TEMP_SCHEMA, vol.NotIn([Temp(OFF)])), None),
        vol.Optional("off_temp", default=OFF):
        TEMP_SCHEMA,
        vol.Optional("supports_hvac_modes", default=True):
        bool,
        vol.Optional("hvac_mode_on", default="heat"):
        str,
        vol.Optional("hvac_mode_off", default="off"):
        str,
    }

    expression_helpers = ActorBase.expression_helpers + [
        ThermostatExpressionHelper
    ]

    stats_param_types = [TempDeltaParameter]

    def __init__(self, *args: T.Any, **kwargs: T.Any) -> None:
        super().__init__(*args, **kwargs)
        self._current_temp = None  # type: T.Optional[Temp]

    def check_config_plausibility(self, state: dict) -> None:
        """Is called during initialization to warn the user about some
        possible common configuration mistakes."""

        if not state:
            self.log("Thermostat couldn't be found.", level="WARNING")
            return

        required_attrs = ["temperature"]
        if self.cfg["supports_hvac_modes"]:
            required_attrs.append("state")
        for attr in required_attrs:
            if attr not in state:
                self.log(
                    "Thermostat has no attribute named {!r}. Available are {!r}. "
                    "Please check your config!".format(attr,
                                                       list(state.keys())),
                    level="WARNING",
                )

        temp_attrs = ["temperature", "current_temperature"]
        for attr in temp_attrs:
            value = state.get(attr)
            try:
                value = float(value)  # type: ignore
            except (TypeError, ValueError):
                self.log(
                    "The value {!r} of attribute {!r} is no valid temperature."
                    .format(value, attr),
                    level="WARNING",
                )

        allowed_hvac_modes = state.get("hvac_modes")
        if not self.cfg["supports_hvac_modes"]:
            if allowed_hvac_modes:
                self.log(
                    "HVAC mode support has been disabled, but the modes {!r} seem to "
                    "be supported. Maybe disabling it was a mistake?".format(
                        allowed_hvac_modes),
                    level="WARNING",
                )
            return

        if not allowed_hvac_modes:
            self.log(
                "Attributes for thermostat contain no 'hvac_modes', Consider "
                "disabling HVAC mode support.",
                level="WARNING",
            )
            return
        for hvac_mode in (self.cfg["hvac_mode_on"], self.cfg["hvac_mode_off"]):
            if hvac_mode not in allowed_hvac_modes:
                self.log(
                    "Thermostat doesn't seem to support the "
                    "HVAC mode {}, supported modes are: {}. "
                    "Please check your config!".format(hvac_mode,
                                                       allowed_hvac_modes),
                    level="WARNING",
                )

    @property
    def current_temp(self) -> T.Optional[Temp]:
        """Returns the current temperature as measured by the thermostat."""

        return self._current_temp

    @staticmethod
    def deserialize_value(value: str) -> Temp:
        """Deserializes by calling validate_value()."""

        return ThermostatActor.validate_value(value)

    def do_send(self) -> None:
        """Sends self._wanted_value to the thermostat."""

        target_temp = self._wanted_value
        if target_temp.is_off:
            hvac_mode = self.cfg["hvac_mode_off"]
            temp = None
        else:
            hvac_mode = self.cfg["hvac_mode_on"]
            temp = target_temp
        if not self.cfg["supports_hvac_modes"]:
            hvac_mode = None

        self.log(
            "Setting temperature = {!r}, HVAC mode = {!r}.".format(
                "<unset>" if temp is None else temp,
                "<unset>" if hvac_mode is None else hvac_mode,
            ),
            level="DEBUG",
            prefix=common.LOG_PREFIX_OUTGOING,
        )
        if hvac_mode is not None:
            self.app.call_service("climate/set_hvac_mode",
                                  entity_id=self.entity_id,
                                  hvac_mode=hvac_mode)
        if temp is not None:
            self.app.call_service(
                "climate/set_temperature",
                entity_id=self.entity_id,
                temperature=temp.value,
            )

    def filter_set_value(self, value: Temp) -> T.Optional[Temp]:
        """Preprocesses the given target temperature for setting on this
        thermostat. This algorithm will try best to achieve the closest
        possible temperature supported by this particular thermostat.
        The return value is either the temperature to set or None,
        if nothing has to be sent."""

        if value.is_off:
            value = self.cfg["off_temp"]

        if not value.is_off:
            value = value + self.cfg["delta"]
            if isinstance(self.cfg["min_temp"],
                          Temp) and value < self.cfg["min_temp"]:
                value = self.cfg["min_temp"]
            elif (isinstance(self.cfg["max_temp"], Temp)
                  and value > self.cfg["max_temp"]):
                value = self.cfg["max_temp"]
        elif not self.cfg["supports_hvac_modes"]:
            self.log(
                "Not turning off because it doesn't support HVAC modes.",
                level="WARNING",
            )
            self.log(
                "Consider defining an off_temp in the actor "
                "configuration for these cases.",
                level="WARNING",
            )
            return None

        return value

    def notify_state_changed(self, attrs: dict) -> T.Any:
        """Is called when the thermostat's state changes.
        This method fetches both the current and target temperature from
        the thermostat and reacts accordingly."""

        _target_temp = None  # type: T.Optional[TempValueType]
        if self.cfg["supports_hvac_modes"]:
            hvac_mode = attrs.get("state")
            self.log(
                "Attribute 'state' is {}.".format(repr(hvac_mode)),
                level="DEBUG",
                prefix=common.LOG_PREFIX_INCOMING,
            )
            if hvac_mode == self.cfg["hvac_mode_off"]:
                _target_temp = OFF
            elif hvac_mode != self.cfg["hvac_mode_on"]:
                self.log(
                    "Unknown HVAC mode {!r}, ignoring thermostat.".format(
                        hvac_mode),
                    level="ERROR",
                )
                return None
        else:
            hvac_mode = None

        if _target_temp is None:
            _target_temp = attrs.get("temperature")
            self.log(
                "Attribute 'temperature' is {}.".format(repr(_target_temp)),
                level="DEBUG",
                prefix=common.LOG_PREFIX_INCOMING,
            )

        try:
            target_temp = Temp(_target_temp)
        except ValueError:
            self.log("Invalid target temperature, ignoring thermostat.",
                     level="ERROR")
            return None

        _current_temp = attrs.get("current_temperature")
        self.log(
            "Attribute 'current_temperature' is {}.".format(
                repr(_current_temp)),
            level="DEBUG",
            prefix=common.LOG_PREFIX_INCOMING,
        )
        if _current_temp is not None:
            try:
                current_temp = Temp(_current_temp)  # type: T.Optional[Temp]
            except ValueError:
                self.log(
                    "Invalid current temperature {!r}, not updating it.".
                    format(_current_temp),
                    level="ERROR",
                )
            else:
                if current_temp != self._current_temp:
                    self._current_temp = current_temp
                    self.events.trigger("current_temp_changed", self,
                                        current_temp)

        return target_temp

    @staticmethod
    def serialize_value(value: Temp) -> str:
        """Wrapper around Temp.serialize()."""

        if not isinstance(value, Temp):
            raise ValueError("can only serialize Temp objects, not {}".format(
                repr(value)))
        return value.serialize()

    @staticmethod
    def validate_value(value: T.Any) -> Temp:
        """Ensures the given value is a valid temperature."""

        return Temp(value)
示例#7
0
    lambda v: TEMP_EXPRESSION_MODULE_SCHEMA(v or {}),
})

########## THERMOSTATS

THERMOSTAT_SCHEMA = vol.Schema(
    vol.All(
        lambda v: v or {},
        {
            "friendly_name":
            str,
            vol.Optional("delta", default=0):
            vol.Any(float, int),
            vol.Optional("min_temp", default=None):
            vol.Any(
                vol.All(TEMP_SCHEMA, vol.NotIn([expr.Temp(expr.OFF)])),
                None,
            ),
            vol.Optional("max_temp", default=None):
            vol.Any(
                vol.All(TEMP_SCHEMA, vol.NotIn([expr.Temp(expr.OFF)])),
                None,
            ),
            vol.Optional("off_temp", default=expr.OFF):
            TEMP_SCHEMA,
            vol.Optional("set_temp_retries", default=10):
            vol.All(int, vol.Range(min=-1)),
            vol.Optional("set_temp_retry_interval", default=30):
            vol.All(int, vol.Range(min=1)),
            vol.Optional("supports_opmodes", default=True):
            bool,