Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
23327be
Add detection of Anna P1
bouwew Oct 28, 2025
c24c9cb
Improve
bouwew Oct 29, 2025
2ae44f9
Show model_id for legacy gataways
bouwew Oct 30, 2025
cc54249
Ruffed
bouwew Oct 31, 2025
2751642
Change anna_p1 indication, collect smartmeter data when available
bouwew Nov 1, 2025
c7ab898
Add Anna_P1 userdata
bouwew Nov 1, 2025
46ffeae
Add anna_p1 testcase
bouwew Nov 1, 2025
31b0f29
Change OpenTherm device data
bouwew Nov 1, 2025
5a3f0c5
Update anna_p1 test-json
bouwew Nov 1, 2025
33649a9
Update anna_p1 entity_items assert
bouwew Nov 1, 2025
968edd1
Save new and updated files
bouwew Nov 1, 2025
adde1cc
Update testdata
bouwew Nov 1, 2025
811e677
Cover Anna P1 as Anna too
bouwew Nov 1, 2025
a6dc51e
Fix typo
bouwew Nov 1, 2025
0639272
Save updated fixture
bouwew Nov 1, 2025
17ec6ab
Collect P1 measurement data for Anna P1 too
bouwew Nov 1, 2025
787bb8d
Don't stop collecting entity data for Anna P1
bouwew Nov 1, 2025
98e44b3
Increase anna_p1 entity_items assert
bouwew Nov 1, 2025
7b0a1a2
Save updated fixture
bouwew Nov 1, 2025
a8b772d
Save updated anna_p1 test-json
bouwew Nov 1, 2025
f2a4756
Don't replace gateway entity id for anna_p1
bouwew Nov 1, 2025
4a86e11
Don't swap gateway and smartmeter entity id's for Anna P1
bouwew Nov 1, 2025
c2cdcc4
Updated related test-json
bouwew Nov 2, 2025
d37d482
Update anna_p1 entity_items assert
bouwew Nov 2, 2025
a16adc8
Save updated fixture
bouwew Nov 2, 2025
e801308
_get_module_data(): add key argument
bouwew Nov 2, 2025
a55cc1c
Update anna_p1 test-json, entity_items assert
bouwew Nov 2, 2025
38ab2aa
Save updated anna_p1 fixture
bouwew Nov 2, 2025
35cf2fd
Improve typing
bouwew Nov 2, 2025
8ff85ec
Update CHANGELOG
bouwew Nov 2, 2025
e697dda
Remove us of thermostat_power type
bouwew Nov 2, 2025
64f9c17
Fix date typo
bouwew Nov 2, 2025
a49a2d5
_smile_detect(): lower complexity
bouwew Nov 2, 2025
a2c05d7
Add pragma-no-cover
bouwew Nov 2, 2025
b743e1b
Less complex
bouwew Nov 2, 2025
2788efe
CRAI nitpicks
bouwew Nov 2, 2025
370bf12
Bump to v1.9.0a0 for testing
bouwew Nov 2, 2025
0141e2a
Set to v1.9.0 release version
bouwew Nov 2, 2025
f766a29
Update check_name() docstring
bouwew Nov 2, 2025
222e6cd
Update README
bouwew Nov 2, 2025
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.9.0

- Add support for Anna P1 via PR [#809](https://github.com/plugwise/python-plugwise/pull/809)

## v1.8.3

- Remove storing the last active schedule(s) via PR [#806](https://github.com/plugwise/python-plugwise/pull/806), to be handled by the HA Integration
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ Module providing interfacing with the Plugwise devices:
### Smile

- [x] Adam
- [x] Emma (only tested as ZigBee device connected to Adam)
- [x] Emma
- [x] Jip
- [x] Lisa
- [x] Tom/Floor
- [x] Koen (a Koen always comes with a Plug, the Plug is the active part)
- [x] Plug
- [x] Aqara Plug
- [x] Anna (v1.8 and later firmware versions)
- [ ] Anna P1
- [x] Anna P1
- [x] Smile P1 (v2.0 and later firmware versions)
- [x] Stretch (only with Circles, please help out with other devices)

Expand Down
104 changes: 104 additions & 0 deletions fixtures/anna_p1/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"1e5e55b958ac445583602f767cb45942": {
"active_preset": "home",
"available_schedules": ["Thermostat schedule", "off"],
"climate_mode": "heat",
"control_state": "idle",
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-500",
"location": "5b13651d79c4454684fd268850b1bff8",
"model": "ThermoTouch",
"name": "Anna",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"select_schedule": "off",
"sensors": {
"illuminance": 2.0,
"setpoint": 19.0,
"temperature": 19.4
},
"temperature_offset": {
"lower_bound": -2.0,
"resolution": 0.1,
"setpoint": 0.0,
"upper_bound": 2.0
},
"thermostat": {
"lower_bound": 4.0,
"resolution": 0.1,
"setpoint": 19.0,
"upper_bound": 30.0
},
"vendor": "Plugwise"
},
"36b937e44ad145bab165fa0fe99d742d": {
"available": true,
"binary_sensors": {
"dhw_state": false,
"flame_state": false,
"heating_state": false
},
"dev_class": "heater_central",
"location": "da7be222ab3b420c927f3e49fade0304",
"model": "Generic heater",
"model_id": "HR24",
"name": "OpenTherm",
"sensors": {
"intended_boiler_temperature": 0.0,
"modulation_level": 0.0,
"water_pressure": 6.0,
"water_temperature": 35.0
},
"switches": {
"dhw_cm_switch": true
},
"vendor": "Intergas"
},
"53130847be2f436cb946b78dedb9053a": {
"binary_sensors": {
"plugwise_notification": false
},
"dev_class": "gateway",
"firmware": "4.4.4",
"hardware": "AME Smile 2.0 board",
"location": "da7be222ab3b420c927f3e49fade0304",
"mac_address": "C493000ABCD",
"model": "Gateway",
"model_id": "smile_thermo",
"name": "Smile Anna P1",
"notifications": {},
"sensors": {
"outdoor_temperature": 11.8
},
"vendor": "Plugwise"
},
"da7be222ab3b420c927f3e49fade0304": {
"available": true,
"dev_class": "smartmeter",
"location": "da7be222ab3b420c927f3e49fade0304",
"model": "2MS212 SMR5.5",
"name": "P1",
"sensors": {
"electricity_consumed_off_peak_cumulative": 618.001,
"electricity_consumed_off_peak_interval": 7,
"electricity_consumed_off_peak_point": 393,
"electricity_consumed_peak_cumulative": 576.014,
"electricity_consumed_peak_interval": 0,
"electricity_consumed_peak_point": 0,
"electricity_phase_one_consumed": 393,
"electricity_phase_one_produced": 0,
"electricity_produced_off_peak_cumulative": 246.504,
"electricity_produced_off_peak_interval": 0,
"electricity_produced_off_peak_point": 0,
"electricity_produced_peak_cumulative": 709.442,
"electricity_produced_peak_interval": 0,
"electricity_produced_peak_point": 0,
"gas_consumed_cumulative": 25.37,
"gas_consumed_interval": 0.01,
"net_electricity_cumulative": 238.069,
"net_electricity_point": 393,
"voltage_phase_one": 234.6
},
"vendor": "SAGEM"
}
}
60 changes: 40 additions & 20 deletions plugwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def __init__(
self._stretch_v2 = False
self._target_smile: str = NONE
self.smile: Munch = Munch()
self.smile.anna_p1 = False
self.smile.hostname = NONE
self.smile.hw_version = None
self.smile.legacy = False
Expand Down Expand Up @@ -189,13 +190,23 @@ async def _smile_detect(
"""
model: str = "Unknown"
if (gateway := result.find("./gateway")) is not None:
if (v_model := gateway.find("vendor_model")) is not None:
model = v_model.text
self.smile.version = parse(gateway.find("firmware_version").text)
self.smile.hw_version = gateway.find("hardware_version").text
self.smile.hostname = gateway.find("hostname").text
self.smile.mac_address = gateway.find("mac_address").text
self.smile.model_id = gateway.find("vendor_model").text
if (vendor_model := gateway.find("vendor_model")) is None:
return # pragma: no cover

model = vendor_model.text
elec_measurement = gateway.find(
"gateway_environment/electricity_consumption_tariff_structure"
)
if (
elec_measurement is not None
and elec_measurement.text
and model == "smile_thermo"
):
self.smile.anna_p1 = True
else:
model = await self._smile_detect_legacy(result, dsmrmain, model)

Expand Down Expand Up @@ -231,29 +242,38 @@ async def _smile_detect(
raise UnsupportedDeviceError # pragma: no cover

self.smile.model = "Gateway"
self.smile.model_id = model
self.smile.name = SMILES[self._target_smile].smile_name
self.smile.type = SMILES[self._target_smile].smile_type
if self.smile.name == "Smile Anna" and self.smile.anna_p1:
self.smile.name = "Smile Anna P1"

if self.smile.type == "stretch":
self._stretch_v2 = int(version_major) == 2

if self.smile.type == "thermostat":
self._is_thermostat = True
# For Adam, Anna, determine the system capabilities:
# Find the connected heating/cooling device (heater_central),
# e.g. heat-pump or gas-fired heater
onoff_boiler = result.find("./module/protocols/onoff_boiler")
open_therm_boiler = result.find("./module/protocols/open_therm_boiler")
self._on_off_device = onoff_boiler is not None
self._opentherm_device = open_therm_boiler is not None

# Determine the presence of special features
locator_1 = "./gateway/features/cooling"
locator_2 = "./gateway/features/elga_support"
if result.find(locator_1) is not None:
self._cooling_present = True
if result.find(locator_2) is not None:
self._elga = True
self._process_for_thermostat(result)

def _process_for_thermostat(self, result: etree.Element) -> None:
"""Extra processing for thermostats."""
if self.smile.type != "thermostat":
return

self._is_thermostat = True
# For Adam, Anna, determine the system capabilities:
# Find the connected heating/cooling device (heater_central),
# e.g. heat-pump or gas-fired heater
onoff_boiler = result.find("./module/protocols/onoff_boiler")
open_therm_boiler = result.find("./module/protocols/open_therm_boiler")
self._on_off_device = onoff_boiler is not None
self._opentherm_device = open_therm_boiler is not None

# Determine the presence of special features
locator_1 = "./gateway/features/cooling"
locator_2 = "./gateway/features/elga_support"
if result.find(locator_1) is not None:
self._cooling_present = True
if result.find(locator_2) is not None:
self._elga = True

async def _smile_detect_legacy(
self, result: etree.Element, dsmrmain: etree.Element, model: str
Expand Down
23 changes: 16 additions & 7 deletions plugwise/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ def heater_id(self) -> str:
return self._heater_id

def check_name(self, name: str) -> bool:
"""Helper-function checking the smile-name."""
return bool(self.smile.name == name)
"""Helper-function checking the smile-name.

20251101: modified for finding name = `Smile Anna` in `Smile Anna P1`.
"""
return bool(name in self.smile.name)

def _appl_heater_central_info(
self,
Expand Down Expand Up @@ -98,9 +101,9 @@ def _appl_heater_central_info(
# xml_1: appliance
# xml_3: self._modules for legacy, self._domain_objects for actual
xml_3 = return_valid(xml_3, self._domain_objects)
module_data = self._get_module_data(xml_1, locator_1, xml_3)
module_data = self._get_module_data(xml_1, locator_1, xml_2=xml_3)
if not module_data["contents"]:
module_data = self._get_module_data(xml_1, locator_2, xml_3)
module_data = self._get_module_data(xml_1, locator_2, xml_2=xml_3)
if not module_data["contents"]:
self._heater_id = NONE
return (
Expand All @@ -121,7 +124,7 @@ def _appl_thermostat_info(
"""Helper-function for _appliance_info_finder()."""
locator = "./logs/point_log[type='thermostat']/thermostat"
xml_2 = return_valid(xml_2, self._domain_objects)
module_data = self._get_module_data(xml_1, locator, xml_2)
module_data = self._get_module_data(xml_1, locator, xml_2=xml_2)
if not module_data["contents"]:
return Munch() # no module-data present means the device has been removed

Expand Down Expand Up @@ -239,7 +242,8 @@ def _get_module_data(
self,
xml_1: etree.Element,
locator: str,
xml_2: etree.Element = None,
key: str | None = None,
xml_2: etree.Element | None = None,
legacy: bool = False,
) -> ModuleData:
"""Helper-function for _energy_device_info_finder() and _appliance_info_finder().
Expand All @@ -256,8 +260,11 @@ def _get_module_data(
"zigbee_mac_address": None,
}

if (appl_search := xml_1.find(locator)) is not None:
for appl_search in xml_1.findall(locator):
link_tag = appl_search.tag
if key is not None and key not in link_tag:
continue

link_id = appl_search.attrib["id"]
loc = f".//services/{link_tag}[@id='{link_id}']...."
# Not possible to walrus for some reason...
Expand All @@ -272,4 +279,6 @@ def _get_module_data(
module_data["firmware_version"] = module.find("firmware_version").text
get_zigbee_data(module, module_data, legacy)

break

return module_data
1 change: 1 addition & 0 deletions plugwise/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

ADAM: Final = "Adam"
ANNA: Final = "Smile Anna"
ANNA_P1: Final = "Smile Anna P1"
DEFAULT_TIMEOUT: Final = 10
DEFAULT_LEGACY_TIMEOUT: Final = 30
DEFAULT_USERNAME: Final = "smile"
Expand Down
Loading