def __init__( self, xknx: XKNX, name: str, group_address_state: GroupAddressesType | None = None, sync_state: bool | int | float | str = True, always_callback: bool = False, value_type: int | str | None = None, device_updated_cb: DeviceCallbackType[Sensor] | None = None, ): """Initialize Sensor class.""" super().__init__(xknx, name, device_updated_cb) self.sensor_value: RemoteValueControl | RemoteValueSensor if isinstance(value_type, str) and value_type in [ "stepwise_dimming", "stepwise_blinds", "startstop_dimming", "startstop_blinds", ]: self.sensor_value = RemoteValueControl( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) else: self.sensor_value = RemoteValueSensor( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) self.always_callback = always_callback
def __init__( self, xknx: "XKNX", name: str, group_address_temperature: Optional["GroupAddressableType"] = None, group_address_brightness_south: Optional[ "GroupAddressableType"] = None, group_address_brightness_north: Optional[ "GroupAddressableType"] = None, group_address_brightness_west: Optional["GroupAddressableType"] = None, group_address_brightness_east: Optional["GroupAddressableType"] = None, group_address_wind_speed: Optional["GroupAddressableType"] = None, group_address_wind_bearing: Optional["GroupAddressableType"] = None, group_address_rain_alarm: Optional["GroupAddressableType"] = None, group_address_frost_alarm: Optional["GroupAddressableType"] = None, group_address_wind_alarm: Optional["GroupAddressableType"] = None, group_address_day_night: Optional["GroupAddressableType"] = None, group_address_air_pressure: Optional["GroupAddressableType"] = None, group_address_humidity: Optional["GroupAddressableType"] = None, create_sensors: bool = False, sync_state: bool = True, device_updated_cb: Optional[DeviceCallbackType] = None, ) -> None: """Initialize Weather class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self._temperature = RemoteValueSensor( xknx, group_address_state=group_address_temperature, sync_state=sync_state, value_type="temperature", device_name=self.name, feature_name="Temperature", after_update_cb=self.after_update, ) self._brightness_south = RemoteValueSensor( xknx, group_address_state=group_address_brightness_south, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness south", after_update_cb=self.after_update, ) self._brightness_north = RemoteValueSensor( xknx, group_address_state=group_address_brightness_north, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness north", after_update_cb=self.after_update, ) self._brightness_west = RemoteValueSensor( xknx, group_address_state=group_address_brightness_west, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness west", after_update_cb=self.after_update, ) self._brightness_east = RemoteValueSensor( xknx, group_address_state=group_address_brightness_east, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness east", after_update_cb=self.after_update, ) self._wind_speed = RemoteValueSensor( xknx, group_address_state=group_address_wind_speed, sync_state=sync_state, value_type="wind_speed_ms", device_name=self.name, feature_name="Wind speed", after_update_cb=self.after_update, ) self._wind_bearing = RemoteValueSensor( xknx, group_address_state=group_address_wind_bearing, sync_state=sync_state, value_type="angle", device_name=self.name, feature_name="Wind bearing", after_update_cb=self.after_update, ) self._rain_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_rain_alarm, device_name=self.name, feature_name="Rain alarm", after_update_cb=self.after_update, ) self._frost_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_frost_alarm, device_name=self.name, feature_name="Frost alarm", after_update_cb=self.after_update, ) self._wind_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_wind_alarm, device_name=self.name, feature_name="Wind alarm", after_update_cb=self.after_update, ) self._day_night = RemoteValueSwitch( xknx, group_address_state=group_address_day_night, device_name=self.name, feature_name="Day/Night", after_update_cb=self.after_update, ) self._air_pressure = RemoteValueSensor( xknx, group_address_state=group_address_air_pressure, sync_state=sync_state, value_type="pressure_2byte", device_name=self.name, feature_name="Air pressure", after_update_cb=self.after_update, ) self._humidity = RemoteValueSensor( xknx, group_address_state=group_address_humidity, sync_state=sync_state, value_type="humidity", device_name=self.name, feature_name="Humidity", after_update_cb=self.after_update, ) if create_sensors: self.create_sensors()
class Weather(Device): """Class for managing a weather device.""" # pylint: disable=too-many-locals def __init__( self, xknx: "XKNX", name: str, group_address_temperature: Optional["GroupAddressableType"] = None, group_address_brightness_south: Optional[ "GroupAddressableType"] = None, group_address_brightness_north: Optional[ "GroupAddressableType"] = None, group_address_brightness_west: Optional["GroupAddressableType"] = None, group_address_brightness_east: Optional["GroupAddressableType"] = None, group_address_wind_speed: Optional["GroupAddressableType"] = None, group_address_wind_bearing: Optional["GroupAddressableType"] = None, group_address_rain_alarm: Optional["GroupAddressableType"] = None, group_address_frost_alarm: Optional["GroupAddressableType"] = None, group_address_wind_alarm: Optional["GroupAddressableType"] = None, group_address_day_night: Optional["GroupAddressableType"] = None, group_address_air_pressure: Optional["GroupAddressableType"] = None, group_address_humidity: Optional["GroupAddressableType"] = None, create_sensors: bool = False, sync_state: bool = True, device_updated_cb: Optional[DeviceCallbackType] = None, ) -> None: """Initialize Weather class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self._temperature = RemoteValueSensor( xknx, group_address_state=group_address_temperature, sync_state=sync_state, value_type="temperature", device_name=self.name, feature_name="Temperature", after_update_cb=self.after_update, ) self._brightness_south = RemoteValueSensor( xknx, group_address_state=group_address_brightness_south, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness south", after_update_cb=self.after_update, ) self._brightness_north = RemoteValueSensor( xknx, group_address_state=group_address_brightness_north, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness north", after_update_cb=self.after_update, ) self._brightness_west = RemoteValueSensor( xknx, group_address_state=group_address_brightness_west, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness west", after_update_cb=self.after_update, ) self._brightness_east = RemoteValueSensor( xknx, group_address_state=group_address_brightness_east, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness east", after_update_cb=self.after_update, ) self._wind_speed = RemoteValueSensor( xknx, group_address_state=group_address_wind_speed, sync_state=sync_state, value_type="wind_speed_ms", device_name=self.name, feature_name="Wind speed", after_update_cb=self.after_update, ) self._wind_bearing = RemoteValueSensor( xknx, group_address_state=group_address_wind_bearing, sync_state=sync_state, value_type="angle", device_name=self.name, feature_name="Wind bearing", after_update_cb=self.after_update, ) self._rain_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_rain_alarm, device_name=self.name, feature_name="Rain alarm", after_update_cb=self.after_update, ) self._frost_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_frost_alarm, device_name=self.name, feature_name="Frost alarm", after_update_cb=self.after_update, ) self._wind_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_wind_alarm, device_name=self.name, feature_name="Wind alarm", after_update_cb=self.after_update, ) self._day_night = RemoteValueSwitch( xknx, group_address_state=group_address_day_night, device_name=self.name, feature_name="Day/Night", after_update_cb=self.after_update, ) self._air_pressure = RemoteValueSensor( xknx, group_address_state=group_address_air_pressure, sync_state=sync_state, value_type="pressure_2byte", device_name=self.name, feature_name="Air pressure", after_update_cb=self.after_update, ) self._humidity = RemoteValueSensor( xknx, group_address_state=group_address_humidity, sync_state=sync_state, value_type="humidity", device_name=self.name, feature_name="Humidity", after_update_cb=self.after_update, ) if create_sensors: self.create_sensors() def _iter_remote_values(self) -> Iterator[RemoteValue[Any]]: """Iterate the devices remote values.""" yield self._temperature yield self._brightness_south yield self._brightness_north yield self._brightness_east yield self._brightness_west yield self._wind_speed yield self._wind_bearing yield self._rain_alarm yield self._wind_alarm yield self._frost_alarm yield self._day_night yield self._air_pressure yield self._humidity async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" for remote_value in self._iter_remote_values(): await remote_value.process(telegram) @property def temperature(self) -> Optional[float]: """Return current temperature.""" return self._temperature.value # type: ignore @property def brightness_south(self) -> float: """Return brightness south.""" return ( # type: ignore 0.0 if self._brightness_south.value is None else self._brightness_south.value) @property def brightness_north(self) -> float: """Return brightness north.""" return ( # type: ignore 0.0 if self._brightness_north.value is None else self._brightness_north.value) @property def brightness_east(self) -> float: """Return brightness east.""" return ( # type: ignore 0.0 if self._brightness_east.value is None else self._brightness_east.value) @property def brightness_west(self) -> float: """Return brightness west.""" return ( # type: ignore 0.0 if self._brightness_west.value is None else self._brightness_west.value) @property def wind_speed(self) -> Optional[float]: """Return wind speed in m/s.""" return self._wind_speed.value # type: ignore @property def wind_bearing(self) -> Optional[int]: """Return wind bearing in °.""" return self._wind_bearing.value # type: ignore @property def rain_alarm(self) -> Optional[bool]: """Return True if rain alarm False if not.""" return self._rain_alarm.value # type: ignore @property def wind_alarm(self) -> Optional[bool]: """Return True if wind alarm False if not.""" return self._wind_alarm.value # type: ignore @property def frost_alarm(self) -> Optional[bool]: """Return True if frost alarm False if not.""" return self._frost_alarm.value # type: ignore @property def day_night(self) -> Optional[bool]: """Return day or night.""" return self._day_night.value # type: ignore @property def air_pressure(self) -> Optional[float]: """Return pressure in Pa.""" return self._air_pressure.value # type: ignore @property def humidity(self) -> Optional[float]: """Return humidity in %.""" return self._humidity.value # type: ignore @property def max_brightness(self) -> float: """Return highest illuminance from all sensors.""" return max( self.brightness_west, self.brightness_south, self.brightness_north, self.brightness_east, ) def create_sensors(self) -> None: """Expose sensors to xknx.""" for suffix, group_address in ( ("_rain_alarm", self._rain_alarm.group_address_state), ("_wind_alarm", self._wind_alarm.group_address_state), ("_frost_alarm", self._frost_alarm.group_address_state), ("_day_night", self._day_night.group_address_state), ): if group_address is not None: BinarySensor( self.xknx, name=self.name + suffix, group_address_state=group_address, ) for suffix, group_address, value_type in ( ( "_temperature", self._temperature.group_address_state, "temperature", ), ( "_brightness_south", self._brightness_south.group_address_state, "illuminance", ), ( "_brightness_north", self._brightness_north.group_address_state, "illuminance", ), ( "_brightness_west", self._brightness_west.group_address_state, "illuminance", ), ( "_brightness_east", self._brightness_east.group_address_state, "illuminance", ), ( "_wind_speed", self._wind_speed.group_address_state, "wind_speed_ms", ), ( "_wind_bearing", self._wind_bearing.group_address_state, "angle", ), ( "_air_pressure", self._air_pressure.group_address_state, "pressure", ), ( "_humidity", self._humidity.group_address_state, "humidity", ), ): if group_address is not None: Sensor( self.xknx, name=self.name + suffix, group_address_state=group_address, value_type=value_type, ) # pylint: disable=too-many-return-statements def ha_current_state( self, current_date: date = date.today()) -> WeatherCondition: """Return the current state for home assistant.""" def _get_season(now: date) -> Season: """Return winter or summer.""" if isinstance(now, datetime): now = now.date() now = now.replace(year=YEAR) return next(season for season, (start, end) in SEASONS if start <= now <= end) if self.wind_alarm and self.rain_alarm: return WeatherCondition.lightning_rainy if self.frost_alarm and self.rain_alarm: return WeatherCondition.snowy_rainy if self.rain_alarm: return WeatherCondition.rainy if self.wind_alarm: return WeatherCondition.windy current_season: Season = _get_season(current_date) _season: Season function: Callable[[float], bool] result: WeatherCondition for _season, function, result in ILLUMINANCE_MAPPING: if _season == current_season and function(self.max_brightness): return result if self.day_night is False: return WeatherCondition.clear_night return WeatherCondition.exceptional @classmethod def from_config(cls, xknx: "XKNX", name: str, config: Any) -> "Weather": """Initialize object from configuration structure.""" group_address_temperature = config.get("group_address_temperature") group_address_brightness_south = config.get( "group_address_brightness_south") group_address_brightness_north = config.get( "group_address_brightness_north") group_address_brightness_west = config.get( "group_address_brightness_west") group_address_brightness_east = config.get( "group_address_brightness_east") group_address_wind_speed = config.get("group_address_wind_speed") group_address_wind_bearing = config.get("group_address_wind_bearing") group_address_rain_alarm = config.get("group_address_rain_alarm") group_address_frost_alarm = config.get("group_address_frost_alarm") group_address_wind_alarm = config.get("group_address_wind_alarm") group_address_day_night = config.get("group_address_day_night") group_address_air_pressure = config.get("group_address_air_pressure") group_address_humidity = config.get("group_address_humidity") create_sensors = config.get("create_sensors", False) sync_state = config.get("sync_state", True) return cls( xknx, name, group_address_temperature=group_address_temperature, group_address_brightness_south=group_address_brightness_south, group_address_brightness_north=group_address_brightness_north, group_address_brightness_west=group_address_brightness_west, group_address_brightness_east=group_address_brightness_east, group_address_wind_speed=group_address_wind_speed, group_address_wind_bearing=group_address_wind_bearing, group_address_rain_alarm=group_address_rain_alarm, group_address_frost_alarm=group_address_frost_alarm, group_address_wind_alarm=group_address_wind_alarm, group_address_day_night=group_address_day_night, group_address_air_pressure=group_address_air_pressure, group_address_humidity=group_address_humidity, create_sensors=create_sensors, sync_state=sync_state, ) def __str__(self) -> str: """Return object as readable string.""" return ( '<Weather name="{}" ' 'temperature="{}" brightness_south="{}" brightness_north="{}" brightness_west="{}" ' 'brightness_east="{}" wind_speed="{}" wind_bearing="{}" rain_alarm="{}" ' 'wind_alarm="{}" frost_alarm="{}" day_night="{}" ' 'air_pressure="{}" humidity="{}" />'.format( self.name, self._temperature.group_addr_str(), self._brightness_south.group_addr_str(), self._brightness_north.group_addr_str(), self._brightness_west.group_addr_str(), self._brightness_east.group_addr_str(), self._wind_speed.group_addr_str(), self._wind_bearing.group_addr_str(), self._rain_alarm.group_addr_str(), self._wind_alarm.group_addr_str(), self._frost_alarm.group_addr_str(), self._day_night.group_addr_str(), self._air_pressure.group_addr_str(), self._humidity.group_addr_str(), ))
def test_wrong_value_type(self): """Test initializing with wrong value_type.""" xknx = XKNX() with pytest.raises(ConversionError): RemoteValueSensor(xknx=xknx, value_type="wrong_value_type")
class Sensor(Device): """Class for managing a sensor.""" def __init__(self, xknx, name, group_address_state=None, sync_state=True, value_type=None, device_updated_cb=None): """Initialize Sensor class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self.sensor_value = RemoteValueSensor( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update) def _iter_remote_values(self): """Iterate the devices RemoteValue classes.""" yield self.sensor_value @classmethod def from_config(cls, xknx, name, config): """Initialize object from configuration structure.""" group_address_state = config.get('group_address_state') sync_state = config.get('sync_state', True) value_type = config.get('value_type') return cls(xknx, name, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type) async def process_group_write(self, telegram): """Process incoming GROUP WRITE telegram.""" await self.sensor_value.process(telegram) def unit_of_measurement(self): """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def ha_device_class(self): """Return the home assistant device class as string.""" return self.sensor_value.ha_device_class def resolve_state(self): """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self): """Return object as readable string.""" return '<Sensor name="{0}" ' \ 'sensor="{1}" value="{2}" unit="{3}"/>' \ .format(self.name, self.sensor_value.group_addr_str(), self.resolve_state(), self.unit_of_measurement()) def __eq__(self, other): """Equal operator.""" return self.__dict__ == other.__dict__
class Weather(Device): """Class for managing a weather device.""" def __init__( self, xknx: XKNX, name: str, group_address_temperature: GroupAddressesType | None = None, group_address_brightness_south: GroupAddressesType | None = None, group_address_brightness_north: GroupAddressesType | None = None, group_address_brightness_west: GroupAddressesType | None = None, group_address_brightness_east: GroupAddressesType | None = None, group_address_wind_speed: GroupAddressesType | None = None, group_address_wind_bearing: GroupAddressesType | None = None, group_address_rain_alarm: GroupAddressesType | None = None, group_address_frost_alarm: GroupAddressesType | None = None, group_address_wind_alarm: GroupAddressesType | None = None, group_address_day_night: GroupAddressesType | None = None, group_address_air_pressure: GroupAddressesType | None = None, group_address_humidity: GroupAddressesType | None = None, create_sensors: bool = False, sync_state: bool = True, device_updated_cb: DeviceCallbackType | None = None, ) -> None: """Initialize Weather class.""" super().__init__(xknx, name, device_updated_cb) self._temperature = RemoteValueSensor( xknx, group_address_state=group_address_temperature, sync_state=sync_state, value_type="temperature", device_name=self.name, feature_name="Temperature", after_update_cb=self.after_update, ) self._brightness_south = RemoteValueSensor( xknx, group_address_state=group_address_brightness_south, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness south", after_update_cb=self.after_update, ) self._brightness_north = RemoteValueSensor( xknx, group_address_state=group_address_brightness_north, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness north", after_update_cb=self.after_update, ) self._brightness_west = RemoteValueSensor( xknx, group_address_state=group_address_brightness_west, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness west", after_update_cb=self.after_update, ) self._brightness_east = RemoteValueSensor( xknx, group_address_state=group_address_brightness_east, sync_state=sync_state, value_type="illuminance", device_name=self.name, feature_name="Brightness east", after_update_cb=self.after_update, ) self._wind_speed = RemoteValueSensor( xknx, group_address_state=group_address_wind_speed, sync_state=sync_state, value_type="wind_speed_ms", device_name=self.name, feature_name="Wind speed", after_update_cb=self.after_update, ) self._wind_bearing = RemoteValueSensor( xknx, group_address_state=group_address_wind_bearing, sync_state=sync_state, value_type="angle", device_name=self.name, feature_name="Wind bearing", after_update_cb=self.after_update, ) self._rain_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_rain_alarm, device_name=self.name, feature_name="Rain alarm", after_update_cb=self.after_update, ) self._frost_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_frost_alarm, device_name=self.name, feature_name="Frost alarm", after_update_cb=self.after_update, ) self._wind_alarm = RemoteValueSwitch( xknx, group_address_state=group_address_wind_alarm, device_name=self.name, feature_name="Wind alarm", after_update_cb=self.after_update, ) self._day_night = RemoteValueSwitch( xknx, group_address_state=group_address_day_night, device_name=self.name, feature_name="Day/Night", after_update_cb=self.after_update, ) self._air_pressure = RemoteValueSensor( xknx, group_address_state=group_address_air_pressure, sync_state=sync_state, value_type="pressure_2byte", device_name=self.name, feature_name="Air pressure", after_update_cb=self.after_update, ) self._humidity = RemoteValueSensor( xknx, group_address_state=group_address_humidity, sync_state=sync_state, value_type="humidity", device_name=self.name, feature_name="Humidity", after_update_cb=self.after_update, ) if create_sensors: self.create_sensors() def _iter_remote_values(self) -> Iterator[RemoteValue[Any, Any]]: """Iterate the devices remote values.""" yield self._temperature yield self._brightness_south yield self._brightness_north yield self._brightness_east yield self._brightness_west yield self._wind_speed yield self._wind_bearing yield self._rain_alarm yield self._wind_alarm yield self._frost_alarm yield self._day_night yield self._air_pressure yield self._humidity @property def unique_id(self) -> str | None: """Return unique id for this device.""" return f"{self._temperature.group_address_state}" async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" for remote_value in self._iter_remote_values(): await remote_value.process(telegram) @property def temperature(self) -> float | None: """Return current temperature.""" return self._temperature.value # type: ignore @property def brightness_south(self) -> float: """Return brightness south.""" if self._brightness_south.value is not None: return self._brightness_south.value # type: ignore return 0.0 @property def brightness_north(self) -> float: """Return brightness north.""" if self._brightness_north.value is not None: return self._brightness_north.value # type: ignore return 0.0 @property def brightness_east(self) -> float: """Return brightness east.""" if self._brightness_east.value is not None: return self._brightness_east.value # type: ignore return 0.0 @property def brightness_west(self) -> float: """Return brightness west.""" if self._brightness_west.value is not None: return self._brightness_west.value # type: ignore return 0.0 @property def wind_speed(self) -> float | None: """Return wind speed in m/s.""" return self._wind_speed.value # type: ignore @property def wind_bearing(self) -> int | None: """Return wind bearing in °.""" return self._wind_bearing.value # type: ignore @property def rain_alarm(self) -> bool | None: """Return True if rain alarm False if not.""" return self._rain_alarm.value @property def wind_alarm(self) -> bool | None: """Return True if wind alarm False if not.""" return self._wind_alarm.value @property def frost_alarm(self) -> bool | None: """Return True if frost alarm False if not.""" return self._frost_alarm.value @property def day_night(self) -> bool | None: """Return day or night.""" return self._day_night.value @property def air_pressure(self) -> float | None: """Return pressure in Pa.""" return self._air_pressure.value # type: ignore @property def humidity(self) -> float | None: """Return humidity in %.""" return self._humidity.value # type: ignore @property def max_brightness(self) -> float: """Return highest illuminance from all sensors.""" return max( self.brightness_west, self.brightness_south, self.brightness_north, self.brightness_east, ) def create_sensors(self) -> None: """Expose sensors to xknx.""" for suffix, group_address in ( ("_rain_alarm", self._rain_alarm.group_address_state), ("_wind_alarm", self._wind_alarm.group_address_state), ("_frost_alarm", self._frost_alarm.group_address_state), ("_day_night", self._day_night.group_address_state), ): if group_address is not None: BinarySensor( self.xknx, name=self.name + suffix, group_address_state=group_address, ) for suffix, group_address, value_type in ( ( "_temperature", self._temperature.group_address_state, "temperature", ), ( "_brightness_south", self._brightness_south.group_address_state, "illuminance", ), ( "_brightness_north", self._brightness_north.group_address_state, "illuminance", ), ( "_brightness_west", self._brightness_west.group_address_state, "illuminance", ), ( "_brightness_east", self._brightness_east.group_address_state, "illuminance", ), ( "_wind_speed", self._wind_speed.group_address_state, "wind_speed_ms", ), ( "_wind_bearing", self._wind_bearing.group_address_state, "angle", ), ( "_air_pressure", self._air_pressure.group_address_state, "pressure", ), ( "_humidity", self._humidity.group_address_state, "humidity", ), ): if group_address is not None: Sensor( self.xknx, name=self.name + suffix, group_address_state=group_address, value_type=value_type, ) def ha_current_state(self, current_date: date = date.today()) -> WeatherCondition: """Return the current state for home assistant.""" def _get_season(now: date) -> Season: """Return winter or summer.""" if isinstance(now, datetime): now = now.date() now = now.replace(year=YEAR) return next( season for season, (start, end) in SEASONS if start <= now <= end ) if self.wind_alarm and self.rain_alarm: return WeatherCondition.LIGHTNING_RAINY if self.frost_alarm and self.rain_alarm: return WeatherCondition.SNOWY_RAINY if self.rain_alarm: return WeatherCondition.RAINY if self.wind_alarm: return WeatherCondition.WINDY current_season: Season = _get_season(current_date) _season: Season function: Callable[[float], bool] result: WeatherCondition for _season, function, result in ILLUMINANCE_MAPPING: if _season == current_season and function(self.max_brightness): return result if self.day_night is False: return WeatherCondition.CLEAR_NIGHT return WeatherCondition.EXCEPTIONAL def __str__(self) -> str: """Return object as readable string.""" return ( '<Weather name="{}" ' "temperature={} brightness_south={} brightness_north={} brightness_west={} " "brightness_east={} wind_speed={} wind_bearing={} rain_alarm={} " "wind_alarm={} frost_alarm={} day_night={} " "air_pressure={} humidity={} />".format( self.name, self._temperature.group_addr_str(), self._brightness_south.group_addr_str(), self._brightness_north.group_addr_str(), self._brightness_west.group_addr_str(), self._brightness_east.group_addr_str(), self._wind_speed.group_addr_str(), self._wind_bearing.group_addr_str(), self._rain_alarm.group_addr_str(), self._wind_alarm.group_addr_str(), self._frost_alarm.group_addr_str(), self._day_night.group_addr_str(), self._air_pressure.group_addr_str(), self._humidity.group_addr_str(), ) )
class Sensor(Device): """Class for managing a sensor.""" def __init__( self, xknx: "XKNX", name: str, group_address_state: Optional["GroupAddressableType"] = None, sync_state: bool = True, always_callback: bool = False, value_type: Optional[str] = None, device_updated_cb: Optional[DeviceCallbackType] = None, ): """Initialize Sensor class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self.sensor_value: Union[RemoteValueControl, RemoteValueSensor] if value_type in [ "stepwise_dimming", "stepwise_blinds", "startstop_dimming", "startstop_blinds", ]: self.sensor_value = RemoteValueControl( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) else: self.sensor_value = RemoteValueSensor( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) self.always_callback = always_callback def _iter_remote_values(self) -> Iterator["RemoteValue"]: """Iterate the devices RemoteValue classes.""" yield self.sensor_value @classmethod def from_config(cls, xknx: "XKNX", name: str, config: Any) -> "Sensor": """Initialize object from configuration structure.""" group_address_state = config.get("group_address_state") sync_state = config.get("sync_state", True) always_callback = config.get("always_callback", False) value_type = config.get("value_type") return cls( xknx, name, group_address_state=group_address_state, sync_state=sync_state, always_callback=always_callback, value_type=value_type, ) async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" await self.sensor_value.process(telegram, always_callback=self.always_callback) async def process_group_response(self, telegram: "Telegram") -> None: """Process incoming GroupValueResponse telegrams.""" await self.sensor_value.process(telegram) def unit_of_measurement(self) -> Optional[str]: """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def ha_device_class(self) -> Optional[str]: """Return the home assistant device class as string.""" return self.sensor_value.ha_device_class def resolve_state(self) -> Optional[Any]: """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self) -> str: """Return object as readable string.""" return '<Sensor name="{}" ' 'sensor="{}" value="{}" unit="{}"/>'.format( self.name, self.sensor_value.group_addr_str(), self.resolve_state(), self.unit_of_measurement(), )
class ExposeSensor(Device): """Class for managing a sensor.""" def __init__( self, xknx: "XKNX", name: str, group_address: Optional["GroupAddressableType"] = None, value_type: Optional[str] = None, device_updated_cb: Optional[DeviceCallbackType] = None, ): """Initialize Sensor class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self.sensor_value: "RemoteValue" if value_type == "binary": self.sensor_value = RemoteValueSwitch( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update, ) else: self.sensor_value = RemoteValueSensor( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update, value_type=value_type, ) def _iter_remote_values(self) -> Iterator["RemoteValue"]: """Iterate the devices RemoteValue classes.""" yield self.sensor_value @classmethod def from_config(cls, xknx: "XKNX", name: str, config: Any) -> "ExposeSensor": """Initialize object from configuration structure.""" group_address = config.get("group_address") value_type = config.get("value_type") return cls(xknx, name, group_address=group_address, value_type=value_type) async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" await self.sensor_value.process(telegram) async def process_group_read(self, telegram: "Telegram") -> None: """Process incoming GROUP READ telegram.""" await self.sensor_value.respond() async def set(self, value: Any) -> None: """Set new value.""" await self.sensor_value.set(value) def unit_of_measurement(self) -> Optional[str]: """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def resolve_state(self) -> Any: """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self) -> str: """Return object as readable string.""" return '<ExposeSensor name="{}" ' 'sensor="{}" value="{}" unit="{}"/>'.format( self.name, self.sensor_value.group_addr_str(), self.resolve_state(), self.unit_of_measurement(), )
def test_wrong_value_type(self): """Test initializing with wrong value_type.""" xknx = XKNX(loop=self.loop) with self.assertRaises(ConversionError): RemoteValueSensor(xknx=xknx, value_type="wrong_value_type")
class ExposeSensor(Device): """Class for managing a sensor.""" def __init__(self, xknx, name, group_address=None, value_type=None, device_updated_cb=None): """Initialize Sensor class.""" # pylint: disable=too-many-arguments super().__init__(xknx, name, device_updated_cb) self.sensor_value = None if value_type == "binary": self.sensor_value = RemoteValueSwitch( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update) else: self.sensor_value = RemoteValueSensor( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update, value_type=value_type) def _iter_remote_values(self): """Iterate the devices RemoteValue classes.""" yield self.sensor_value @classmethod def from_config(cls, xknx, name, config): """Initialize object from configuration structure.""" group_address = \ config.get('group_address') value_type = \ config.get('value_type') return cls(xknx, name, group_address=group_address, value_type=value_type) async def process_group_read(self, telegram): """Process incoming GROUP READ telegram.""" await self.sensor_value.send(response=True) async def set(self, value): """Set new value.""" await self.sensor_value.set(value) def unit_of_measurement(self): """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def resolve_state(self): """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self): """Return object as readable string.""" return '<ExposeSensor name="{0}" ' \ 'sensor="{1}" value="{2}" unit="{3}"/>' \ .format(self.name, self.sensor_value.group_addr_str(), self.resolve_state(), self.unit_of_measurement()) def __eq__(self, other): """Equal operator.""" return self.__dict__ == other.__dict__
class Sensor(Device): """Class for managing a sensor.""" def __init__( self, xknx: XKNX, name: str, group_address_state: GroupAddressesType | None = None, sync_state: bool = True, always_callback: bool = False, value_type: int | str | None = None, ha_value_template: Any = None, device_updated_cb: DeviceCallbackType | None = None, ): """Initialize Sensor class.""" super().__init__(xknx, name, device_updated_cb) self.sensor_value: RemoteValueControl | RemoteValueSensor if isinstance(value_type, str) and value_type in [ "stepwise_dimming", "stepwise_blinds", "startstop_dimming", "startstop_blinds", ]: self.sensor_value = RemoteValueControl( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) else: self.sensor_value = RemoteValueSensor( xknx, group_address_state=group_address_state, sync_state=sync_state, value_type=value_type, device_name=self.name, after_update_cb=self.after_update, ) self.always_callback = always_callback self.ha_value_template = ha_value_template def _iter_remote_values(self) -> Iterator[RemoteValue[Any, Any]]: """Iterate the devices RemoteValue classes.""" yield self.sensor_value @property def unique_id(self) -> str | None: """Return unique id for this device.""" return f"{self.sensor_value.group_address_state}" @property def last_telegram(self) -> Telegram | None: """Return the last telegram received from the RemoteValue.""" return self.sensor_value.telegram async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" await self.sensor_value.process(telegram, always_callback=self.always_callback) async def process_group_response(self, telegram: "Telegram") -> None: """Process incoming GroupValueResponse telegrams.""" await self.sensor_value.process(telegram) def unit_of_measurement(self) -> str | None: """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def ha_device_class(self) -> str | None: """Return the home assistant device class as string.""" return self.sensor_value.ha_device_class def resolve_state(self) -> Any | None: """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self) -> str: """Return object as readable string.""" return '<Sensor name="{}" sensor={} value={} unit="{}"/>'.format( self.name, self.sensor_value.group_addr_str(), self.resolve_state().__repr__(), self.unit_of_measurement(), )
class ExposeSensor(Device): """Class for managing a sensor.""" def __init__( self, xknx: XKNX, name: str, group_address: GroupAddressesType | None = None, value_type: int | str | None = None, device_updated_cb: DeviceCallbackType | None = None, ): """Initialize Sensor class.""" super().__init__(xknx, name, device_updated_cb) self.sensor_value: RemoteValueSensor | RemoteValueSwitch if value_type == "binary": self.sensor_value = RemoteValueSwitch( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update, ) else: self.sensor_value = RemoteValueSensor( xknx, group_address=group_address, sync_state=False, device_name=self.name, after_update_cb=self.after_update, value_type=value_type, ) def _iter_remote_values(self) -> Iterator[RemoteValue[Any, Any]]: """Iterate the devices RemoteValue classes.""" yield self.sensor_value async def process_group_write(self, telegram: "Telegram") -> None: """Process incoming and outgoing GROUP WRITE telegram.""" await self.sensor_value.process(telegram) async def process_group_read(self, telegram: "Telegram") -> None: """Process incoming GROUP READ telegram.""" await self.sensor_value.respond() async def set(self, value: Any) -> None: """Set new value.""" await self.sensor_value.set(value) def unit_of_measurement(self) -> str | None: """Return the unit of measurement.""" return self.sensor_value.unit_of_measurement def resolve_state(self) -> Any: """Return the current state of the sensor as a human readable string.""" return self.sensor_value.value def __str__(self) -> str: """Return object as readable string.""" return '<ExposeSensor name="{}" sensor={} value={} unit="{}"/>'.format( self.name, self.sensor_value.group_addr_str(), self.resolve_state().__repr__(), self.unit_of_measurement(), )