Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
224574c
Link to plugwise v0.31.2a0, bump to v0.40.2a0
bouwew Apr 29, 2023
03fa7a6
Link to plugwise v0.31.2a1
bouwew Apr 29, 2023
d4b9cd9
Adapt to using PlugwiseData class,
bouwew Apr 29, 2023
d65b3bf
Link to plugwise v0.31.2
bouwew Apr 29, 2023
e4f32a6
Simplify plugwise imports
bouwew Apr 29, 2023
2283e68
Fix pylint errors (copy from Core)
bouwew Apr 29, 2023
b9a3a81
Add missing typing
bouwew Apr 29, 2023
b91f759
Add handling of strict-typing
bouwew Apr 30, 2023
3af89fe
Manually add mypy-settings
bouwew Apr 30, 2023
454f348
Typing fixes
bouwew Apr 30, 2023
c513155
Try
bouwew Apr 30, 2023
f4e77f3
Add local mypy, fix __init__
CoMPaTech May 1, 2023
691df83
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 1, 2023
ebcbef7
Add missing entity desc.
CoMPaTech May 1, 2023
92f756b
Bump cache to ensure core-testing with mypy strict
CoMPaTech May 1, 2023
ed4e35e
Apply Switch to Sensor (with TODOs)
CoMPaTech May 1, 2023
7d83607
Fix TODO's
bouwew May 1, 2023
b0c0258
Fix the 2 actuator-sensors
bouwew May 1, 2023
8a06771
Binary
CoMPaTech May 1, 2023
bb29f53
Mark sensors as TODO
CoMPaTech May 1, 2023
85a76e2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 1, 2023
8da7c1d
Fix number and select (not working, fixtures next)
CoMPaTech May 1, 2023
b0b1cf2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 1, 2023
adb350c
Fix fixtures (4 TODO left), expected increase of strict again
CoMPaTech May 1, 2023
65d9349
Use TypeVar
CoMPaTech May 2, 2023
7c15808
Correctly name dhw to dhw_setpoint
CoMPaTech May 2, 2023
e2c1881
Work on todos (breaking tests)
CoMPaTech May 2, 2023
917dcb9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 2, 2023
e65e947
Refix dhw_setpoint sensor
bouwew May 2, 2023
827c124
Fix typo
bouwew May 2, 2023
bd8dba4
Add dhw_setpoint actuator dict to m_adam_heating fixture
bouwew May 2, 2023
f06d8b2
Temp revert dhw for mypy; ignoring index
CoMPaTech May 3, 2023
940a380
Fugly fix (but working) with 31.3
CoMPaTech May 3, 2023
119aa7c
Try single core-testing
CoMPaTech May 3, 2023
4ff80e7
Try single core-testing without hassfest
CoMPaTech May 3, 2023
2f97660
Try single core-testing without hassfest
CoMPaTech May 3, 2023
49b6df4
Try single core-testing without hassfest
CoMPaTech May 3, 2023
fd2dd56
Fixture generator issue (true core pr diffing)
CoMPaTech May 3, 2023
2a5d56c
Sorted fixtures except m_adam_heating
CoMPaTech May 4, 2023
23904c3
Restore proper naming
CoMPaTech May 5, 2023
e7c57e8
Rework for max_dhw
CoMPaTech May 5, 2023
4d4e080
Remove fugly ors
CoMPaTech May 5, 2023
db3e677
Fixture newlines
CoMPaTech May 5, 2023
da09458
Optimize
bouwew May 5, 2023
538c18b
Remove (now) redundant TypeVar)
CoMPaTech May 5, 2023
bdcd35e
Re-apply prettier, following plugwise-python PR 312
CoMPaTech May 5, 2023
d5da064
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 5, 2023
ec2f7d9
Clean translation files
bouwew May 6, 2023
c50dcaf
Fix unpractical Plugwise-provided resolutions
bouwew May 6, 2023
9c7214c
Bump to a3
bouwew May 6, 2023
78b6976
Bump to v0.40.2 release-version
bouwew May 15, 2023
7d81678
Update CHANGELOG
bouwew May 15, 2023
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
27 changes: 15 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
name: Test with HA-core

env:
CACHE_VERSION: 1
CACHE_VERSION: 2
DEFAULT_PYTHON: "3.11"

on:
Expand Down Expand Up @@ -44,18 +44,21 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-hacore
${{ env.CACHE_VERSION}}-${{ runner.os }}
${{ env.CACHE_VERSION}}
- name: Create HA-core Python virtual environment
- name: Test through core (full, not split)
run: |
scripts/core-testing.sh core_prep
- name: Prepare HA-core Python virtual environment
run: |
scripts/core-testing.sh pip_prep
- name: Test
run: |
scripts/core-testing.sh testing
- name: Quality
run: |
scripts/core-testing.sh quality
GITHUB_ACTIONS="" scripts/core-testing.sh
# - name: Create HA-core Python virtual environment
# run: |
# scripts/core-testing.sh core_prep
# - name: Prepare HA-core Python virtual environment
# run: |
# scripts/core-testing.sh pip_prep
# - name: Test
# run: |
# scripts/core-testing.sh testing
# - name: Quality
# run: |
# scripts/core-testing.sh quality

shellcheck:
name: Shellcheck
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

## Versions from 0.40 and up

### Ongoing / Unreleased
### 0.40.2

- CI improvements
- Implement strict typing, also via [plugwise v0.31.3](https://github.com/plugwise/python-plugwise/releases/tag/v0.31.3)
- Dynamic generated fixtures re-introduced

### 0.40.1

Expand Down
17 changes: 9 additions & 8 deletions custom_components/plugwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er
from plugwise.exceptions import PlugwiseError

Expand Down Expand Up @@ -60,15 +60,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)

async def delete_notification(
self,
): # pragma: no cover # pw-beta: HA service - delete_notification
call: ServiceCall,
) -> None: # pragma: no cover # pw-beta: HA service - delete_notification
"""Service: delete the Plugwise Notification."""
LOGGER.debug(
"Service delete PW Notification called for %s", coordinator.api.smile_name
"Service delete PW Notification called for %s",
coordinator.api.smile_name,
)
try:
deleted = await coordinator.api.delete_notification()
LOGGER.debug("PW Notification deleted: %s", deleted)
await coordinator.api.delete_notification()
LOGGER.debug("PW Notification deleted")
except PlugwiseError:
LOGGER.debug(
"Failed to delete the Plugwise Notification for %s",
Expand All @@ -88,12 +89,12 @@ async def delete_notification(

async def _update_listener(
hass: HomeAssistant, entry: ConfigEntry
): # pragma: no cover # pw-beta
) -> None: # pragma: no cover # pw-beta
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(
entry, PLATFORMS_GATEWAY
Expand Down
19 changes: 9 additions & 10 deletions custom_components/plugwise/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ async def async_setup_entry(

entities: list[PlugwiseBinarySensorEntity] = []
for device_id, device in coordinator.data.devices.items():
if "binary_sensors" not in device:
continue
for description in PW_BINARY_SENSOR_TYPES:
if (
"binary_sensors" not in device
or description.key not in device["binary_sensors"]
):
if description.key not in device["binary_sensors"]:
continue

entities.append(
PlugwiseBinarySensorEntity(
coordinator,
Expand All @@ -45,6 +43,7 @@ async def async_setup_entry(
)
)
LOGGER.debug("Add %s binary sensor", description.key)

async_add_entities(entities)


Expand All @@ -66,17 +65,17 @@ def __init__(
self._notification: dict[str, str] = {} # pw-beta

@property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
if (
self._notification
): # pw-beta: show Plugwise notifications as HA persistent notifications
# pw-beta: show Plugwise notifications as HA persistent notifications
if self._notification:
for notify_id, message in self._notification.items():
self.hass.components.persistent_notification.async_create(
message, "Plugwise Notification:", f"{DOMAIN}.{notify_id}"
)

return self.device["binary_sensors"].get(self.entity_description.key)
# return self.device["binary_sensors"][self.entity_description.key] # type: ignore [literal-required]
return self.entity_description.value_fn(self.device)

@property
def icon(self) -> str | None:
Expand Down
18 changes: 9 additions & 9 deletions custom_components/plugwise/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def __init__(

self._attr_min_temp = self.device["thermostat"]["lower_bound"]
self._attr_max_temp = self.device["thermostat"]["upper_bound"]
# Ensure we don't drop below 0.1
# Fix unpractical resolution provided by Plugwise
self._attr_target_temperature_step = max(
self.device["thermostat"]["resolution"], 0.1
self.device["thermostat"]["resolution"], 0.5
)

@property
Expand Down Expand Up @@ -156,13 +156,13 @@ def hvac_action(self) -> HVACAction: # pw-beta add to Core
if control_state == "off":
return HVACAction.IDLE

hc_data = self.coordinator.data.devices[
self.coordinator.data.gateway["heater_id"]
]
if hc_data["binary_sensors"]["heating_state"]:
return HVACAction.HEATING
if hc_data["binary_sensors"].get("cooling_state", False): # pw-beta adds False
return HVACAction.COOLING
heater: str | None = self.coordinator.data.gateway["heater_id"]
if heater is not None:
heater_data = self.coordinator.data.devices[heater]
if heater_data["binary_sensors"]["heating_state"]:
return HVACAction.HEATING
if heater_data["binary_sensors"].get("cooling_state", False):
return HVACAction.COOLING

return HVACAction.IDLE

Expand Down
30 changes: 10 additions & 20 deletions custom_components/plugwise/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""DataUpdateCoordinator for Plugwise."""
from datetime import timedelta
from typing import NamedTuple, cast

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
Expand All @@ -10,8 +9,7 @@
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from plugwise import Smile
from plugwise.constants import DeviceData, GatewayData
from plugwise import PlugwiseData, Smile
from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication,
Expand All @@ -23,28 +21,25 @@
from .const import DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_USERNAME, DOMAIN, LOGGER


class PlugwiseData(NamedTuple):
"""Plugwise data stored in the DataUpdateCoordinator."""

gateway: GatewayData
devices: dict[str, DeviceData]


class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]):
"""Class to manage fetching Plugwise data from single endpoint."""

_connected: bool = False

def __init__(
self, hass: HomeAssistant, entry: ConfigEntry, cooldown: float
self,
hass: HomeAssistant,
entry: ConfigEntry,
cooldown: float,
update_interval: timedelta = timedelta(seconds=60),
) -> None: # pw-beta cooldown
"""Initialize the coordinator."""
super().__init__(
hass,
LOGGER,
name=DOMAIN,
# Core directly updates from const's DEFAULT_SCAN_INTERVAL
update_interval=timedelta(seconds=60),
update_interval=update_interval,
# Don't refresh immediately, give the device time to process
# the change in state before we query it.
request_refresh_debouncer=Debouncer(
Expand All @@ -65,12 +60,12 @@ def __init__(
)
self._entry = entry
self._unavailable_logged = False
self.update_interval = update_interval

async def _connect(self) -> None:
"""Connect to the Plugwise Smile."""
self._connected = await self.api.connect()
self.api.get_all_devices()
self.name = self.api.smile_name

self.update_interval = DEFAULT_SCAN_INTERVAL.get(
self.api.smile_type, timedelta(seconds=60)
Expand All @@ -88,9 +83,7 @@ async def _async_update_data(self) -> PlugwiseData:
if not self._connected:
await self._connect()
data = await self.api.async_update()
LOGGER.debug(
f"{self.api.smile_name} data: %s", PlugwiseData(data[0], data[1])
)
LOGGER.debug(f"{self.api.smile_name} data: %s", data)
if self._unavailable_logged:
self._unavailable_logged = False
except InvalidAuthentication as err:
Expand All @@ -112,7 +105,4 @@ async def _async_update_data(self) -> PlugwiseData:
self._unavailable_logged = True
raise UpdateFailed("Failed to connect") from err

return PlugwiseData(
gateway=cast(GatewayData, data[0]),
devices=cast(dict[str, DeviceData], data[1]),
)
return data
2 changes: 1 addition & 1 deletion custom_components/plugwise/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def available(self) -> bool:
"""Return if entity is available."""
return (
self._dev_id in self.coordinator.data.devices
and ("available" not in self.device or self.device["available"])
and ("available" not in self.device or self.device["available"] is True)
and super().available
)

Expand Down
4 changes: 2 additions & 2 deletions custom_components/plugwise/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["plugwise"],
"requirements": ["plugwise==0.31.0"],
"version": "0.40.1"
"requirements": ["plugwise==0.31.3"],
"version": "0.40.2"
}
Loading