Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Ongoing

- Code improvements
- Remove unused dependencies from pyproject.toml

## V0.37.1
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_elga_2/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "d34dfe6ab90b410c98068e75de3eb631",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_elga_2_cooling/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "d34dfe6ab90b410c98068e75de3eb631",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_elga_2_schedule_off/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "d34dfe6ab90b410c98068e75de3eb631",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_elga_no_cooling/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": true,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_heatpump_cooling/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/anna_heatpump_heating/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": true,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/m_anna_heatpump_cooling/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/m_anna_heatpump_idle/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dhw_state": false,
"flame_state": false,
"heating_state": false,
"slave_boiler_state": false
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
Expand Down
91 changes: 47 additions & 44 deletions plugwise/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,20 @@
# radiator_valve: 'uncorrected_temperature', 'temperature_offset'

DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
# HA Core thermostat current_temperature
"temperature": UOM(TEMP_CELSIUS),
# HA Core thermostat setpoint
"thermostat": DATA("setpoint", TEMP_CELSIUS),
# Specific for an Anna
"illuminance": UOM(UNIT_LUMEN),
"humidity": UOM(PERCENTAGE), # Specific for a 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 a Lisa a Tom/Floor
##################################
# Specific for a Lisa or Tom/Floor
"battery": UOM(PERCENTAGE),
"temperature_difference": UOM(DEGREE),
"valve_position": UOM(PERCENTAGE),
# Specific for a Jip
"humidity": UOM(PERCENTAGE),
#####################
# Specific for a Plug
"electricity_consumed": UOM(POWER_WATT),
"electricity_produced": UOM(POWER_WATT),
Expand All @@ -144,39 +143,43 @@

# 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
"central_heating_state": DATA(
"c_heating_state", NONE
), # For Elga (heatpump) use this instead of intended_central_heating_state
"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),
# Used with the Elga heatpump - marcelveldt
"compressor_state": UOM(NONE),
"cooling_state": UOM(NONE),
"thermostat_supports_cooling": UOM(NONE),
# Available with the Loria and Elga (newer Anna firmware) heatpumps
"cooling_enabled": UOM(NONE),
# Next 2 keys are used to show the state of the gas-heater used next to the Elga heatpump - marcelveldt
"slave_boiler_state": UOM(NONE),
"flame_state": UOM(NONE), # Also present when there is a single gas-heater
"central_heater_water_pressure": DATA("water_pressure", PRESSURE_BAR),
# Legacy Anna: similar to flame-state on Anna/Adam
"boiler_state": DATA("flame_state", NONE),
# Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably
"intended_boiler_state": DATA("heating_state", NONE),
# Outdoor temperature from APPLIANCES - present for a heatpump
"outdoor_temperature": DATA("outdoor_air_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, ...]] = (
Expand Down Expand Up @@ -253,22 +256,22 @@
]

BinarySensorType = Literal[
"cooling_enabled",
"compressor_state",
"cooling_enabled",
"cooling_state",
"dhw_state",
"flame_state",
"heating_state",
"plugwise_notification",
"slave_boiler_state",
"secondary_boiler_state",
]
BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType)

LIMITS: Final[tuple[str, ...]] = (
"lower_bound",
"offset",
"setpoint",
"resolution",
"lower_bound",
"setpoint",
"upper_bound",
)

Expand Down Expand Up @@ -329,8 +332,8 @@

SPECIAL_PLUG_TYPES: Final[tuple[str, ...]] = (
"central_heating_pump",
"valve_actuator",
"heater_electric",
"valve_actuator",
)

SpecialType = Literal[
Expand All @@ -350,14 +353,14 @@
]
SWITCHES: Final[tuple[str, ...]] = get_args(SwitchType)

SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("switching", "report")
SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("report", "switching")

THERMOSTAT_CLASSES: Final[tuple[str, ...]] = (
"thermostat",
"thermostatic_radiator_valve",
"thermo_sensor",
"zone_thermometer",
"zone_thermostat",
"thermostatic_radiator_valve",
)

ToggleNameType = Literal[
Expand Down Expand Up @@ -392,25 +395,25 @@ class ModelData(TypedDict):
"""The ModelData class."""

contents: bool
vendor_name: str | None
vendor_model: str | None
hardware_version: str | None
firmware_version: str | None
zigbee_mac_address: 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."""

cooling_enabled: bool
compressor_state: bool
cooling_enabled: bool
cooling_state: bool
dhw_state: bool
flame_state: bool
heating_state: bool
plugwise_notification: bool
slave_boiler_state: bool
secondary_boiler_state: bool


class SmileSensors(TypedDict, total=False):
Expand Down Expand Up @@ -482,9 +485,9 @@ class ThermoLoc(TypedDict, total=False):
"""Thermo Location class."""

name: str
master: str | None
master_prio: int
slaves: set[str]
primary: str | None
primary_prio: int
secondary: set[str]


class ActuatorData(TypedDict, total=False):
Expand Down
6 changes: 3 additions & 3 deletions plugwise/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def _get_device_data(self, dev_id: str) -> DeviceData:
self._device_data_switching_group(device, data)
# Adam data
self._device_data_adam(device, data)
# Skip obtaining data for non master-thermostats
# Skip obtaining data for (Adam) secondary thermostats
if device["dev_class"] not in ZONE_THERMOSTATS:
return data

Expand Down Expand Up @@ -164,7 +164,7 @@ def _device_data_adam(self, device: DeviceData, data: DeviceData) -> None:
):
data["binary_sensors"]["heating_state"] = self._heating_valves() != 0

# Show the allowed regulation modes and gateway_modes
# Show the allowed regulation_modes and gateway_modes
if device["dev_class"] == "gateway":
if self._reg_allowed_modes:
data["regulation_modes"] = self._reg_allowed_modes
Expand All @@ -173,7 +173,7 @@ def _device_data_adam(self, device: DeviceData, data: DeviceData) -> None:
data["gateway_modes"] = self._gw_allowed_modes
self._count += 1

# Control_state, only for Adam master thermostats
# Control_state, only available for Adam primary thermostats
if device["dev_class"] in ZONE_THERMOSTATS:
loc_id = device["location"]
if ctrl_state := self._control_state(loc_id):
Expand Down
30 changes: 15 additions & 15 deletions plugwise/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ def _scan_thermostats(self) -> None:
"""Helper-function for smile.py: get_all_devices().

Update locations with thermostat ranking results and use
the result to update the device_class of slave thermostats.
the result to update the device_class of secondary thermostats.
"""
self._thermo_locs = self._match_locations()

Expand All @@ -828,11 +828,11 @@ def _scan_thermostats(self) -> None:
for dev_id, device in self.gw_devices.items():
self._rank_thermostat(thermo_matching, loc_id, dev_id, device)

# Update slave thermostat class where needed
# Update secondary thermostat class where needed
for dev_id, device in self.gw_devices.items():
if (loc_id := device["location"]) in self._thermo_locs:
tl_loc_id = self._thermo_locs[loc_id]
if "slaves" in tl_loc_id and dev_id in tl_loc_id["slaves"]:
if "secondary" in tl_loc_id and dev_id in tl_loc_id["secondary"]:
device["dev_class"] = "thermo_sensor"

def _match_locations(self) -> dict[str, ThermoLoc]:
Expand All @@ -845,7 +845,7 @@ def _match_locations(self) -> dict[str, ThermoLoc]:
for appliance_details in self.gw_devices.values():
if appliance_details["location"] == location_id:
location_details.update(
{"master": None, "master_prio": 0, "slaves": set()}
{"primary": None, "primary_prio": 0, "secondary": set()}
)
matched_locations[location_id] = location_details

Expand All @@ -860,29 +860,29 @@ def _rank_thermostat(
) -> None:
"""Helper-function for _scan_thermostats().

Rank the thermostat based on appliance_details: master or slave.
Rank the thermostat based on appliance_details: primary or secondary.
"""
appl_class = appliance_details["dev_class"]
appl_d_loc = appliance_details["location"]
if loc_id == appl_d_loc and appl_class in thermo_matching:
# Pre-elect new master
if thermo_matching[appl_class] > self._thermo_locs[loc_id]["master_prio"]:
# Demote former master
if (tl_master := self._thermo_locs[loc_id]["master"]) is not None:
self._thermo_locs[loc_id]["slaves"].add(tl_master)
# Pre-elect new primary
if thermo_matching[appl_class] > self._thermo_locs[loc_id]["primary_prio"]:
# Demote former primary
if (tl_primary:= self._thermo_locs[loc_id]["primary"]) is not None:
self._thermo_locs[loc_id]["secondary"].add(tl_primary)

# Crown master
self._thermo_locs[loc_id]["master_prio"] = thermo_matching[appl_class]
self._thermo_locs[loc_id]["master"] = appliance_id
# Crown primary
self._thermo_locs[loc_id]["primary_prio"] = thermo_matching[appl_class]
self._thermo_locs[loc_id]["primary"] = appliance_id

else:
self._thermo_locs[loc_id]["slaves"].add(appliance_id)
self._thermo_locs[loc_id]["secondary"].add(appliance_id)

def _control_state(self, loc_id: str) -> str | bool:
"""Helper-function for _device_data_adam().

Adam: find the thermostat control_state of a location, from DOMAIN_OBJECTS.
Represents the heating/cooling demand-state of the local master thermostat.
Represents the heating/cooling demand-state of the local primary thermostat.
Note: heating or cooling can still be active when the setpoint has been reached.
"""
locator = f'location[@id="{loc_id}"]'
Expand Down
Loading