"""Plugwise Smile constants.""" from __future__ import annotations from collections import namedtuple import logging from typing import Final, Literal, TypedDict, get_args LOGGER = logging.getLogger(__name__) # Copied homeassistant.consts ATTR_NAME: Final = "name" ATTR_STATE: Final = "state" ATTR_STATE_CLASS: Final = "state_class" ATTR_UNIT_OF_MEASUREMENT: Final = "unit_of_measurement" DEGREE: Final = "°" ELECTRIC_POTENTIAL_VOLT: Final = "V" ENERGY_KILO_WATT_HOUR: Final = "kWh" ENERGY_WATT_HOUR: Final = "Wh" GATEWAY_REBOOT: Final = "/core/gateways;@reboot" PERCENTAGE: Final = "%" POWER_WATT: Final = "W" PRESET_AWAY: Final = "away" PRESSURE_BAR: Final = "bar" SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final = "dBm" STATE_OFF: Final = "off" STATE_ON: Final = "on" TEMP_CELSIUS: Final = "°C" TEMP_KELVIN: Final = "°K" TIME_MILLISECONDS: Final = "ms" UNIT_LUMEN: Final = "lm" VOLUME_CUBIC_METERS: Final = "m³" VOLUME_CUBIC_METERS_PER_HOUR: Final = "m³/h" ADAM: Final = "Adam" ALLOWED_ZONE_PROFILES: Final[list[str]] = ["active", "off", "passive"] ANNA: Final = "Smile Anna" ANNA_P1: Final = "Smile Anna P1" DEFAULT_TIMEOUT: Final = 10 DEFAULT_LEGACY_TIMEOUT: Final = 30 DEFAULT_USERNAME: Final = "smile" DEFAULT_PORT: Final = 80 DEFAULT_PW_MAX: Final = 30.0 DEFAULT_PW_MIN: Final = 4.0 DHW_SETPOINT: Final = "domestic_hot_water_setpoint" FAKE_APPL: Final = "aaaa0000aaaa0000aaaa0000aaaa00aa" FAKE_LOC: Final = "0000aaaa0000aaaa0000aaaa0000aa00" HW_MODELS: Final[dict[str, str]] = { "143.1": "ThermoTouch", "159.2": "Adam", # "?????": "Emma Essential", "170.1": "Emma Pro wired", "106-03": "Tom", "158-01": "Lisa", "160-01": "Plug", "165-01": "Floor", "168-01": "Jip", "168-02": "Jip", "170-01": "Emma Pro", "038500": "Stick", "070085": "Stick", "120002": "Stick Legrand", "120041": "Circle+ Legrand type E", "120000": "Circle+ Legrand type F", "090000": "Circle+ type B", "090007": "Circle+ type B", "090088": "Circle+ type E", "070073": "Circle+ type F", "090048": "Circle+ type G", "120049": "Stealth M+", "090188": "Stealth+", "120040": "Circle Legrand type E", "120001": "Circle Legrand type F", "090079": "Circle type B", "090087": "Circle type E", "070140": "Circle type F", "090093": "Circle type G", "100025": "Circle", "120048": "Stealth M", "120029": "Stealth Legrand", "090011": "Stealth", "001200": "Stealth", "080007": "Scan", "110028": "Scan Legrand", "070030": "Sense", "120006": "Sense Legrand", "070051": "Switch", "080029": "Switch", } MAX_SETPOINT: Final[float] = 30.0 MIN_SETPOINT: Final[float] = 4.0 MODULE_LOCATOR: Final = "./logs/point_log/*[@id]" NONE: Final = "None" OFF: Final = "off" PRIORITY_DEVICE_CLASSES = ("gateway", "heater_central") THERMO_MATCHING: Final[dict[str, int]] = { "thermostat": 2, "zone_thermometer": 2, "zone_thermostat": 2, "thermostatic_radiator_valve": 1, } # XML data paths APPLIANCES: Final = "/core/appliances" DOMAIN_OBJECTS: Final = "/core/domain_objects" LOCATIONS: Final = "/core/locations" MODULES: Final = "/core/modules" NOTIFICATIONS: Final = "/core/notifications" RULES: Final = "/core/rules" SYSTEM: Final = "/system" STATUS: Final = "/system/status.xml" UOM = namedtuple("UOM", "unit_of_measurement") DATA = namedtuple("DATA", "name unit_of_measurement") GROUP_MEASUREMENTS: Final[dict[str, UOM]] = { "electricity_consumed": UOM(POWER_WATT), "electricity_produced": UOM(POWER_WATT), "temperature": UOM(TEMP_CELSIUS), } # P1 related measurements: P1_MEASUREMENTS: Final[dict[str, UOM]] = { "electricity_consumed": UOM(POWER_WATT), "electricity_produced": UOM(POWER_WATT), "electricity_phase_one_consumed": UOM(POWER_WATT), "electricity_phase_two_consumed": UOM(POWER_WATT), "electricity_phase_three_consumed": UOM(POWER_WATT), "electricity_phase_one_produced": UOM(POWER_WATT), "electricity_phase_two_produced": UOM(POWER_WATT), "electricity_phase_three_produced": UOM(POWER_WATT), "gas_consumed": UOM(VOLUME_CUBIC_METERS), "voltage_phase_one": UOM(ELECTRIC_POTENTIAL_VOLT), "voltage_phase_two": UOM(ELECTRIC_POTENTIAL_VOLT), "voltage_phase_three": UOM(ELECTRIC_POTENTIAL_VOLT), } P1_LEGACY_MEASUREMENTS: Final[dict[str, UOM]] = { "electricity_consumed": UOM(POWER_WATT), "electricity_produced": UOM(POWER_WATT), "gas_consumed": UOM(VOLUME_CUBIC_METERS), } # Thermostat and Plug/Stretch related measurements # Excluded: # zone_thermosstat: 'temperature_offset' # radiator_valve: 'uncorrected_temperature', 'temperature_offset' DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = { "humidity": UOM(NONE), # Specific for an Emma or Jip "illuminance": UOM(UNIT_LUMEN), # Specific for an Anna "temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature "thermostat": DATA("setpoint", TEMP_CELSIUS), # HA Core thermostat setpoint ######################################################## # Specific for an Anna with heatpump extension installed "cooling_activation_outdoor_temperature": UOM(TEMP_CELSIUS), "cooling_deactivation_threshold": UOM(TEMP_CELSIUS), ################################## # Specific for an Emma Jip, Lisa, Tom or Floor "battery": UOM(PERCENTAGE), "temperature_difference": UOM(DEGREE), "valve_position": UOM(PERCENTAGE), ##################### # Specific for a Plug "electricity_consumed": UOM(POWER_WATT), "electricity_produced": UOM(POWER_WATT), "relay": UOM(NONE), } # Heater Central related measurements HEATER_CENTRAL_MEASUREMENTS: Final[dict[str, DATA | UOM]] = { "boiler_state": DATA( "flame_state", NONE ), # Legacy Anna: similar to flame-state on Anna/Adam "boiler_temperature": DATA("water_temperature", TEMP_CELSIUS), "central_heating_state": DATA( "c_heating_state", NONE ), # For Elga (heatpump) use this instead of intended_central_heating_state "central_heater_water_pressure": DATA("water_pressure", PRESSURE_BAR), "compressor_state": UOM(NONE), # present with heatpump "cooling_enabled": UOM( NONE ), # Available with the Loria and Elga (newer Anna firmware) heatpumps "cooling_state": UOM(NONE), "domestic_hot_water_mode": DATA("select_dhw_mode", NONE), "domestic_hot_water_setpoint": UOM(TEMP_CELSIUS), "domestic_hot_water_state": DATA("dhw_state", NONE), "domestic_hot_water_temperature": DATA("dhw_temperature", TEMP_CELSIUS), "elga_status_code": UOM(NONE), "intended_boiler_state": DATA( "heating_state", NONE ), # Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably "flame_state": UOM(NONE), # Also present when there is a single gas-heater "intended_boiler_temperature": UOM( TEMP_CELSIUS ), # Non-zero when heating, zero when dhw-heating "intended_central_heating_state": DATA( "heating_state", NONE ), # This key shows in general the heating-behavior better than c-h_state. except when connected to a heatpump "modulation_level": UOM(PERCENTAGE), "return_water_temperature": DATA("return_temperature", TEMP_CELSIUS), "outdoor_temperature": DATA( "outdoor_air_temperature", TEMP_CELSIUS ), # Outdoor temperature from APPLIANCES - present for a heatpump "slave_boiler_state": DATA("secondary_boiler_state", NONE), "thermostat_supports_cooling": UOM(NONE), # present with heatpump } OBSOLETE_MEASUREMENTS: Final[tuple[str, ...]] = ( "central_heater_water_pressure", "outdoor_temperature", ) # Zone/climate related measurements ZONE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = { "electricity_consumed": UOM(POWER_WATT), "electricity_produced": UOM(POWER_WATT), "relay": UOM(NONE), "temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature "thermostat": DATA("setpoint", TEMP_CELSIUS), # HA Core thermostat setpoint } # Literals SMILE_P1 = "Smile P1" POWER = "power" STRETCH = "stretch" THERMOSTAT = "thermostat" # Known types of Smiles and Stretches SMILE = namedtuple("SMILE", "smile_type smile_name") SMILES: Final[dict[str, SMILE]] = { "smile_v2": SMILE(POWER, SMILE_P1), "smile_v3": SMILE(POWER, SMILE_P1), "smile_v4": SMILE(POWER, SMILE_P1), "smile_open_therm_v2": SMILE(THERMOSTAT, ADAM), "smile_open_therm_v3": SMILE(THERMOSTAT, ADAM), "smile_thermo_v1": SMILE(THERMOSTAT, ANNA), "smile_thermo_v3": SMILE(THERMOSTAT, ANNA), "smile_thermo_v4": SMILE(THERMOSTAT, ANNA), "stretch_v2": SMILE(STRETCH, "Stretch"), "stretch_v3": SMILE(STRETCH, "Stretch"), } REQUIRE_APPLIANCES: Final[tuple[str, ...]] = ( "smile_thermo_v1", "smile_thermo_v3", "smile_thermo_v4", "stretch_v2", "stretch_v3", ) # Class, Literal and related tuple-definitions ACTUATOR_CLASSES: Final[tuple[str, ...]] = ( "heater_central", "thermostat", "thermostatic_radiator_valve", "zone_thermometer", "zone_thermostat", ) ActuatorType = Literal[ "domestic_hot_water_setpoint", "max_dhw_temperature", "maximum_boiler_temperature", "temperature_offset", "thermostat", ] ACTIVE_ACTUATORS: Final[tuple[str, ...]] = get_args(ActuatorType) ACTIVE_KEYS: Final[tuple[str, ...]] = ( "control_state", "lower_bound", "offset", "regulation_control", "resolution", "setpoint", "upper_bound", ) ActuatorDataType = Literal[ "control_state", "lower_bound", "regulation_control", "resolution", "setpoint", "setpoint_high", "setpoint_low", "upper_bound", ] ApplianceType = Literal[ "dev_class", "firmware", "hardware", "location", "mac_address", "members", "model", "model_id", "name", "vendor", "zigbee_mac_address", ] BinarySensorType = Literal[ "compressor_state", "cooling_enabled", "cooling_state", "dhw_state", "flame_state", "heating_state", "low_battery", "plugwise_notification", "secondary_boiler_state", ] BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType) GROUP_TYPES: Final[tuple[str, ...]] = ("pumping", "report", "switching") SensorType = Literal[ "battery", "cooling_activation_outdoor_temperature", "cooling_deactivation_threshold", "dhw_temperature", "domestic_hot_water_setpoint", "temperature", "electricity_consumed", "electricity_consumed_interval", "electricity_consumed_off_peak_cumulative", "electricity_consumed_off_peak_interval", "electricity_consumed_off_peak_point", "electricity_consumed_peak_cumulative", "electricity_consumed_peak_interval", "electricity_consumed_peak_point", "electricity_consumed_point", "electricity_phase_one_consumed", "electricity_phase_two_consumed", "electricity_phase_three_consumed", "electricity_phase_one_produced", "electricity_phase_two_produced", "electricity_phase_three_produced", "electricity_produced", "electricity_produced_interval", "electricity_produced_off_peak_cumulative", "electricity_produced_off_peak_interval", "electricity_produced_off_peak_point", "electricity_produced_peak_cumulative", "electricity_produced_peak_interval", "electricity_produced_peak_point", "electricity_produced_point", "gas_consumed_cumulative", "gas_consumed_interval", "humidity", "illuminance", "intended_boiler_temperature", "modulation_level", "net_electricity_cumulative", "net_electricity_point", "outdoor_air_temperature", "outdoor_temperature", "return_temperature", "setpoint", "setpoint_high", "setpoint_low", "temperature_difference", "valve_position", "voltage_phase_one", "voltage_phase_two", "voltage_phase_three", "water_pressure", "water_temperature", ] SENSORS: Final[tuple[str, ...]] = get_args(SensorType) SPECIAL_PLUG_TYPES: Final[tuple[str, ...]] = ( "central_heating_pump", "heater_electric", "valve_actuator", ) SpecialType = Literal[ "c_heating_state", "thermostat_supports_cooling", ] SPECIALS: Final[tuple[str, ...]] = get_args(SpecialType) SPECIAL_FORMAT: Final[tuple[str, ...]] = (ENERGY_KILO_WATT_HOUR, VOLUME_CUBIC_METERS) SwitchType = Literal[ "cooling_ena_switch", "dhw_cm_switch", "lock", "relay", ] SWITCHES: Final[tuple[str, ...]] = get_args(SwitchType) SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("report", "switching") THERMOSTAT_CLASSES: Final[tuple[str, ...]] = ( "thermostat", "thermostatic_radiator_valve", "thermo_sensor", "zone_thermometer", "zone_thermostat", ) ToggleNameType = Literal[ "cooling_ena_switch", "dhw_cm_switch", ] TOGGLES: Final[dict[str, ToggleNameType]] = { "cooling_enabled": "cooling_ena_switch", "domestic_hot_water_comfort_mode": "dhw_cm_switch", } ZONE_THERMOSTATS: Final[tuple[str, ...]] = ( "thermostat", "thermostatic_radiator_valve", "zone_thermometer", "zone_thermostat", ) class ModuleData(TypedDict): """The Module data class.""" contents: bool firmware_version: str | None hardware_version: str | None reachable: bool | None vendor_model: str | None vendor_name: str | None zigbee_mac_address: str | None class SmileBinarySensors(TypedDict, total=False): """Smile Binary Sensors class.""" compressor_state: bool cooling_enabled: bool cooling_state: bool dhw_state: bool flame_state: bool heating_state: bool low_battery: bool plugwise_notification: bool secondary_boiler_state: bool class SmileSensors(TypedDict, total=False): """Smile Sensors class.""" battery: int cooling_activation_outdoor_temperature: float cooling_deactivation_threshold: float dhw_temperature: float domestic_hot_water_setpoint: float temperature: float electricity_consumed: float electricity_consumed_interval: float electricity_consumed_off_peak_cumulative: float electricity_consumed_off_peak_interval: float electricity_consumed_off_peak_point: float electricity_consumed_peak_cumulative: float electricity_consumed_peak_interval: float electricity_consumed_peak_point: float electricity_consumed_point: float electricity_phase_one_consumed: float electricity_phase_two_consumed: float electricity_phase_three_consumed: float electricity_phase_one_produced: float electricity_phase_two_produced: float electricity_phase_three_produced: float electricity_produced: float electricity_produced_interval: float electricity_produced_off_peak_cumulative: float electricity_produced_off_peak_interval: float electricity_produced_off_peak_point: float electricity_produced_peak_cumulative: float electricity_produced_peak_interval: float electricity_produced_peak_point: float electricity_produced_point: float gas_consumed_cumulative: float gas_consumed_interval: float humidity: float illuminance: float intended_boiler_temperature: float modulation_level: int net_electricity_cumulative: float net_electricity_point: float outdoor_air_temperature: float outdoor_temperature: float return_temperature: float setpoint: float setpoint_high: float setpoint_low: float temperature_difference: float valve_position: int voltage_phase_one: float voltage_phase_two: float voltage_phase_three: float water_pressure: float water_temperature: float class SmileSwitches(TypedDict, total=False): """Smile Switches class.""" cooling_ena_switch: bool dhw_cm_switch: bool lock: bool relay: bool class ThermoLoc(TypedDict, total=False): """Thermo Location class.""" name: str primary: list[str] primary_prio: int secondary: list[str] class ActuatorData(TypedDict, total=False): """Actuator data for thermostat types.""" control_state: str lower_bound: float regulation_control: str resolution: float setpoint: float setpoint_high: float setpoint_low: float upper_bound: float class GwEntityData(TypedDict, total=False): """The Gateway Entity data class. Covering the collected output-data per device or location. """ # Appliance base data dev_class: str firmware: str hardware: str location: str mac_address: str members: list[str] model: str model_id: str | None name: str vendor: str zigbee_mac_address: str # For temporary use cooling_enabled: bool domestic_hot_water_setpoint: float elga_status_code: int c_heating_state: bool thermostat_supports_cooling: bool # Device availability available: bool | None # Loria select_dhw_mode: str dhw_modes: list[str] # Gateway gateway_modes: list[str] notifications: dict[str, dict[str, str]] regulation_modes: list[str] select_gateway_mode: str select_regulation_mode: str # Thermostat-related select_zone_profile: str thermostats: dict[str, list[str]] zone_profiles: list[str] # Presets: active_preset: str | None preset_modes: list[str] | None # Schedules: available_schedules: list[str] select_schedule: str | None climate_mode: str # Extra for Adam Master Thermostats control_state: str # Dict-types binary_sensors: SmileBinarySensors max_dhw_temperature: ActuatorData maximum_boiler_temperature: ActuatorData sensors: SmileSensors switches: SmileSwitches temperature_offset: ActuatorData thermostat: ActuatorData