Compare commits

..

4 Commits

Author SHA1 Message Date
Robert Resch
d2867d9e0f Fix 2026-02-04 23:35:53 +01:00
Robert Resch
368eae89b1 Add machine action 2026-02-04 23:09:25 +01:00
Robert Resch
1f4656fa3e Add shell 2026-02-04 21:40:14 +01:00
Robert Resch
19d8dab6fd Use composite builder action 2026-02-04 21:26:55 +01:00
19 changed files with 291 additions and 295 deletions

View File

@@ -0,0 +1,118 @@
name: "Image builder"
description: "Build a Docker image"
inputs:
base-image:
description: "Base image to use for the build"
required: true
# example: 'ghcr.io/home-assistant/amd64-homeassistant-base:2024.6.0'
tags:
description: "Tag(s) for the built image (can be multiline for multiple tags)"
required: true
# example: 'ghcr.io/home-assistant/amd64-homeassistant:2026.2.0' or multiline for multiple tags
arch:
description: "Architecture for the build (used for default labels)"
required: true
# example: 'amd64'
version:
description: "Version for the build (used for default labels)"
required: true
# example: '2026.2.0'
dockerfile:
description: "Path to the Dockerfile to build"
required: true
# example: './Dockerfile'
cosign-base-identity:
description: "Certificate identity regexp for base image verification"
required: true
# example: 'https://github.com/home-assistant/docker/.*'
additional-labels:
description: "Additional labels to add to the built image (merged with default labels)"
required: false
default: ""
# example: 'custom.label=value'
push:
description: "Whether to push the image to the registry"
required: false
default: "true"
# example: 'true' or 'false'
runs:
using: "composite"
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.5.3"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Verify base image signature
shell: bash
run: |
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "${{ inputs.cosign-base-identity }}" \
"${{ inputs.base-image }}"
- name: Verify cache image signature
id: cache
continue-on-error: true
shell: bash
run: |
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "https://github.com/home-assistant/core/.*" \
"ghcr.io/home-assistant/${{ inputs.arch }}-homeassistant:latest"
- name: Prepare labels
id: labels
shell: bash
run: |
# Generate creation timestamp
CREATED=$(date --rfc-3339=seconds --utc)
# Build default labels array
LABELS=(
"io.hass.arch=${{ inputs.arch }}"
"io.hass.version=${{ inputs.version }}"
"org.opencontainers.image.created=${CREATED}"
"org.opencontainers.image.version=${{ inputs.version }}"
)
# Append additional labels if provided
if [ -n "${{ inputs.additional-labels }}" ]; then
while IFS= read -r label; do
[ -n "$label" ] && LABELS+=("$label")
done <<< "${{ inputs.additional-labels }}"
fi
# Output the combined labels using EOF delimiter for multiline
{
echo 'result<<EOF'
printf '%s\n' "${LABELS[@]}"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Build base image
id: build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ${{ inputs.dockerfile }}
platforms: ${{ steps.vars.outputs.platform }}
push: ${{ inputs.push }}
cache-from: ${{ steps.cache.outcome == 'success' && format('ghcr.io/home-assistant/{0}-homeassistant:latest', inputs.arch) || '' }}
build-args: |
BUILD_FROM=${{ inputs.base-image }}
tags: ${{ inputs.tags }}
outputs: type=image,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true
labels: ${{ steps.labels.outputs.result }}
- name: Sign image
if: ${{ inputs.push == 'true' }}
shell: bash
run: |
# Sign each tag
while IFS= read -r tag; do
[ -n "$tag" ] && cosign sign --yes "${tag}@${{ steps.build.outputs.digest }}"
done <<< "${{ inputs.tags }}"

View File

@@ -0,0 +1,68 @@
name: "Machine image builder"
description: "Build or copy a machine-specific Docker image"
inputs:
machine:
description: "Machine name"
required: true
# example: 'raspberrypi4-64'
version:
description: "Version for the build"
required: true
# example: '2026.2.0'
arch:
description: "Architecture for the build"
required: true
# example: 'aarch64'
runs:
using: "composite"
steps:
- name: Prepare build variables
id: vars
shell: bash
run: |
echo "base_image=ghcr.io/home-assistant/${{ inputs.arch }}-homeassistant:${{ inputs.version }}" >> "$GITHUB_OUTPUT"
# Build tags array with version-specific tag
TAGS=(
"ghcr.io/home-assistant/${{ inputs.machine }}-homeassistant:${{ inputs.version }}"
)
# Add general tag based on version
if [[ "${{ inputs.version }}" =~ d ]]; then
TAGS+=("ghcr.io/home-assistant/${{ inputs.machine }}-homeassistant:dev")
elif [[ "${{ inputs.version }}" =~ b ]]; then
TAGS+=("ghcr.io/home-assistant/${{ inputs.machine }}-homeassistant:beta")
else
TAGS+=("ghcr.io/home-assistant/${{ inputs.machine }}-homeassistant:stable")
fi
# Output tags using EOF delimiter for multiline
{
echo 'tags<<EOF'
printf '%s\n' "${TAGS[@]}"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
LABELS=(
"io.hass.type=core"
"io.hass.machine=${{ inputs.machine }}"
"org.opencontainers.image.source=https://github.com/home-assistant/core"
)
# Output the labels using EOF delimiter for multiline
{
echo 'labels<<EOF'
printf '%s\n' "${LABELS[@]}"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Build machine image
uses: ./.github/actions/builder/generic
with:
base-image: ${{ steps.vars.outputs.base_image }}
tags: ${{ steps.vars.outputs.tags }}
arch: ${{ inputs.arch }}
version: ${{ inputs.version }}
dockerfile: machine/${{ inputs.machine }}
cosign-base-identity: "https://github.com/home-assistant/core/.*"
additional-labels: ${{ steps.vars.outputs.labels }}

View File

@@ -10,12 +10,12 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.14.3"
DEFAULT_PYTHON: "3.14.2"
PIP_TIMEOUT: 60
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
# Base image version from https://github.com/home-assistant/docker
BASE_IMAGE_VERSION: "2026.02.0"
BASE_IMAGE_VERSION: "2026.01.0"
ARCHITECTURES: '["amd64", "aarch64"]'
jobs:
@@ -190,103 +190,53 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- &install_cosign
name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.5.3"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Build variables
id: vars
shell: bash
run: |
echo "base_image=ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant-base:${{ env.BASE_IMAGE_VERSION }}" >> "$GITHUB_OUTPUT"
echo "cache_image=ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:latest" >> "$GITHUB_OUTPUT"
echo "created=$(date --rfc-3339=seconds --utc)" >> "$GITHUB_OUTPUT"
- name: Verify base image signature
run: |
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "https://github.com/home-assistant/docker/.*" \
"${{ steps.vars.outputs.base_image }}"
- name: Verify cache image signature
id: cache
continue-on-error: true
run: |
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "https://github.com/home-assistant/core/.*" \
"${{ steps.vars.outputs.cache_image }}"
- name: Build base image
id: build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
uses: ./.github/actions/builder/generic
with:
context: .
file: ./Dockerfile
platforms: ${{ steps.vars.outputs.platform }}
push: true
cache-from: ${{ steps.cache.outcome == 'success' && steps.vars.outputs.cache_image || '' }}
build-args: |
BUILD_FROM=${{ steps.vars.outputs.base_image }}
base-image: ${{ steps.vars.outputs.base_image }}
tags: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:${{ needs.init.outputs.version }}
outputs: type=image,push=true,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true
labels: |
io.hass.arch=${{ matrix.arch }}
io.hass.version=${{ needs.init.outputs.version }}
org.opencontainers.image.created=${{ steps.vars.outputs.created }}
org.opencontainers.image.version=${{ needs.init.outputs.version }}
- name: Sign image
run: |
cosign sign --yes "ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:${{ needs.init.outputs.version }}@${{ steps.build.outputs.digest }}"
arch: ${{ matrix.arch }}
version: ${{ needs.init.outputs.version }}
dockerfile: ./Dockerfile
cosign-base-identity: "https://github.com/home-assistant/docker/.*"
build_machine:
name: Build ${{ matrix.machine }} machine core image
name: Build ${{ matrix.machine.name }} machine core image
if: github.repository_owner == 'home-assistant'
needs: ["init", "build_base"]
runs-on: ubuntu-latest
runs-on: ${{ matrix.machine.arch == 'amd64' && 'ubuntu-latest' || 'ubuntu-24.04-arm' }}
permissions:
contents: read
packages: write
id-token: write
strategy:
fail-fast: false
matrix:
machine:
- generic-x86-64
- intel-nuc
- khadas-vim3
- odroid-c2
- odroid-c4
- odroid-m1
- odroid-n2
- qemuarm-64
- qemux86-64
- raspberrypi3-64
- raspberrypi4-64
- raspberrypi5-64
- yellow
- green
- { name: generic-x86-64, arch: amd64 }
- { name: intel-nuc, arch: amd64 }
- { name: qemux86-64, arch: amd64 }
- { name: khadas-vim3, arch: aarch64 }
- { name: odroid-c2, arch: aarch64 }
- { name: odroid-c4, arch: aarch64 }
- { name: odroid-m1, arch: aarch64 }
- { name: odroid-n2, arch: aarch64 }
- { name: qemuarm-64, arch: aarch64 }
- { name: raspberrypi3-64, arch: aarch64 }
- { name: raspberrypi4-64, arch: aarch64 }
- { name: raspberrypi5-64, arch: aarch64 }
- { name: yellow, arch: aarch64 }
- { name: green, arch: aarch64 }
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set build additional args
run: |
# Create general tags
if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then
echo "BUILD_ARGS=--additional-tag dev" >> $GITHUB_ENV
elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then
echo "BUILD_ARGS=--additional-tag beta" >> $GITHUB_ENV
else
echo "BUILD_ARGS=--additional-tag stable" >> $GITHUB_ENV
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
@@ -294,15 +244,12 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# home-assistant/builder doesn't support sha pinning
- name: Build base image
uses: home-assistant/builder@2025.11.0
- name: Build machine image
uses: ./.github/actions/builder/machine
with:
args: |
$BUILD_ARGS \
--target /data/machine \
--cosign \
--machine "${{ needs.init.outputs.version }}=${{ matrix.machine }}"
machine: ${{ matrix.machine.name }}
version: ${{ needs.init.outputs.version }}
arch: ${{ matrix.machine.arch }}
publish_ha:
name: Publish version files
@@ -355,7 +302,10 @@ jobs:
matrix:
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
steps:
- *install_cosign
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.5.3"
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'

View File

@@ -41,8 +41,8 @@ env:
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.3"
DEFAULT_PYTHON: "3.14.3"
ALL_PYTHON_VERSIONS: "['3.14.3']"
DEFAULT_PYTHON: "3.14.2"
ALL_PYTHON_VERSIONS: "['3.14.2']"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support

View File

@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.14.3"
DEFAULT_PYTHON: "3.14.2"
jobs:
upload:

View File

@@ -17,7 +17,7 @@ on:
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.14.3"
DEFAULT_PYTHON: "3.14.2"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name}}

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["google_air_quality_api"],
"quality_scale": "bronze",
"requirements": ["google_air_quality_api==3.0.1"]
"requirements": ["google_air_quality_api==3.0.0"]
}

View File

@@ -7,7 +7,7 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["pyliebherrhomeapi"],
"quality_scale": "silver",
"quality_scale": "bronze",
"requirements": ["pyliebherrhomeapi==0.2.1"],
"zeroconf": [
{

View File

@@ -34,7 +34,7 @@ rules:
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
log-when-unavailable: todo
parallel-updates: done
reauthentication-flow: done
test-coverage: done

View File

@@ -1,7 +1,6 @@
"""The syncthing integration."""
import asyncio
from asyncio import Task
import logging
import aiosyncthing
@@ -14,7 +13,7 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_send
@@ -58,8 +57,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async def cancel_listen_task(event: Event) -> None:
"""Cancel the listen task on Home Assistant stop."""
async def cancel_listen_task(_):
await syncthing.unsubscribe()
entry.async_on_unload(
@@ -82,46 +80,44 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class SyncthingClient:
"""A Syncthing client."""
def __init__(
self, hass: HomeAssistant, client: aiosyncthing.Syncthing, server_id: str
) -> None:
def __init__(self, hass, client, server_id):
"""Initialize the client."""
self._hass = hass
self._client = client
self._server_id = server_id
self._listen_task: Task[None] | None = None
self._listen_task = None
@property
def server_id(self) -> str:
def server_id(self):
"""Get server id."""
return self._server_id
@property
def url(self) -> str:
def url(self):
"""Get server URL."""
return self._client.url
@property
def database(self) -> aiosyncthing.Database:
def database(self):
"""Get database namespace client."""
return self._client.database
@property
def system(self) -> aiosyncthing.System:
def system(self):
"""Get system namespace client."""
return self._client.system
def subscribe(self) -> None:
def subscribe(self):
"""Start event listener coroutine."""
self._listen_task = asyncio.create_task(self._listen())
async def unsubscribe(self) -> None:
async def unsubscribe(self):
"""Stop event listener coroutine."""
if self._listen_task:
self._listen_task.cancel()
await self._client.close()
async def _listen(self) -> None:
async def _listen(self):
"""Listen to Syncthing events."""
events = self._client.events
server_was_unavailable = False
@@ -146,7 +142,11 @@ class SyncthingClient:
continue
signal_name = EVENTS[event["type"]]
folder = event["data"].get("folder") or event["data"]["id"]
folder = None
if "folder" in event["data"]:
folder = event["data"]["folder"]
else: # A workaround, some events store folder id under `id` key
folder = event["data"]["id"]
async_dispatcher_send(
self._hass,
f"{signal_name}-{self._server_id}-{folder}",
@@ -168,8 +168,7 @@ class SyncthingClient:
server_was_unavailable = True
continue
async def _server_available(self) -> bool:
"""Check if the Syncthing server is available."""
async def _server_available(self):
try:
await self._client.system.ping()
except aiosyncthing.exceptions.SyncthingError:

View File

@@ -21,7 +21,7 @@ DATA_SCHEMA = vol.Schema(
)
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, str]:
async def validate_input(hass: HomeAssistant, data):
"""Validate the user input allows us to connect."""
try:

View File

@@ -1,20 +1,16 @@
"""Support for Syncthing sensors."""
from collections.abc import Mapping
from typing import Any
"""Support for monitoring the Syncthing instance."""
import aiosyncthing
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from . import SyncthingClient
from .const import (
DOMAIN,
FOLDER_PAUSED_RECEIVED,
@@ -90,21 +86,14 @@ class FolderSensor(SensorEntity):
"stateChanged": "state_changed",
}
def __init__(
self,
syncthing: SyncthingClient,
server_id: str,
folder_id: str,
folder_label: str,
version: str,
) -> None:
def __init__(self, syncthing, server_id, folder_id, folder_label, version):
"""Initialize the sensor."""
self._syncthing = syncthing
self._server_id = server_id
self._folder_id = folder_id
self._folder_label = folder_label
self._state: dict[str, Any] | None = None
self._unsub_timer: CALLBACK_TYPE | None = None
self._state = None
self._unsub_timer = None
self._short_server_id = server_id.split("-")[0]
self._attr_name = f"{self._short_server_id} {folder_id} {folder_label}"
@@ -118,9 +107,9 @@ class FolderSensor(SensorEntity):
)
@property
def native_value(self) -> str | None:
def native_value(self):
"""Return the state of the sensor."""
return self._state["state"] if self._state else None
return self._state["state"]
@property
def available(self) -> bool:
@@ -128,11 +117,11 @@ class FolderSensor(SensorEntity):
return self._state is not None
@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
def extra_state_attributes(self):
"""Return the state attributes."""
return self._state
async def async_update_status(self) -> None:
async def async_update_status(self):
"""Request folder status and update state."""
try:
state = await self._syncthing.database.status(self._folder_id)
@@ -142,11 +131,11 @@ class FolderSensor(SensorEntity):
self._state = self._filter_state(state)
self.async_write_ha_state()
def subscribe(self) -> None:
def subscribe(self):
"""Start polling syncthing folder status."""
if self._unsub_timer is None:
async def refresh(event_time) -> None:
async def refresh(event_time):
"""Get the latest data from Syncthing."""
await self.async_update_status()
@@ -155,7 +144,7 @@ class FolderSensor(SensorEntity):
)
@callback
def unsubscribe(self) -> None:
def unsubscribe(self):
"""Stop polling syncthing folder status."""
if self._unsub_timer is not None:
self._unsub_timer()
@@ -165,9 +154,8 @@ class FolderSensor(SensorEntity):
"""Handle entity which will be added."""
@callback
def handle_folder_summary(event: dict[str, Any]) -> None:
"""Handle folder summary event."""
if self._state:
def handle_folder_summary(event):
if self._state is not None:
self._state = self._filter_state(event["data"]["summary"])
self.async_write_ha_state()
@@ -180,9 +168,8 @@ class FolderSensor(SensorEntity):
)
@callback
def handle_state_changed(event: dict[str, Any]) -> None:
"""Handle folder state changed event."""
if self._state:
def handle_state_changed(event):
if self._state is not None:
self._state["state"] = event["data"]["to"]
self.async_write_ha_state()
@@ -195,9 +182,8 @@ class FolderSensor(SensorEntity):
)
@callback
def handle_folder_paused(event: dict[str, Any]) -> None:
"""Handle folder paused event."""
if self._state:
def handle_folder_paused(event):
if self._state is not None:
self._state["state"] = "paused"
self.async_write_ha_state()
@@ -210,8 +196,7 @@ class FolderSensor(SensorEntity):
)
@callback
def handle_server_unavailable() -> None:
"""Handle server becoming unavailable."""
def handle_server_unavailable():
self._state = None
self.unsubscribe()
self.async_write_ha_state()
@@ -224,8 +209,7 @@ class FolderSensor(SensorEntity):
)
)
async def handle_server_available() -> None:
"""Handle server becoming available."""
async def handle_server_available():
self.subscribe()
await self.async_update_status()
@@ -242,20 +226,20 @@ class FolderSensor(SensorEntity):
await self.async_update_status()
def _filter_state(self, state: dict[str, Any]) -> dict[str, Any]:
"""Filter and map state attributes."""
filtered_state: dict[str, Any] = {
def _filter_state(self, state):
# Select only needed state attributes and map their names
state = {
self.STATE_ATTRIBUTES[key]: value
for key, value in state.items()
if key in self.STATE_ATTRIBUTES
}
# A workaround, for some reason, state of paused folders is an empty string
if filtered_state["state"] == "":
filtered_state["state"] = "paused"
if state["state"] == "":
state["state"] = "paused"
# Add some useful attributes
filtered_state["id"] = self._folder_id
filtered_state["label"] = self._folder_label
state["id"] = self._folder_id
state["label"] = self._folder_label
return filtered_state
return state

View File

@@ -1,15 +0,0 @@
{
"entity": {
"sensor": {
"actual_compressor_speed": {
"default": "mdi:speedometer"
},
"airflow_current_speed": {
"default": "mdi:fan"
},
"mode": {
"default": "mdi:gauge"
}
}
}
}

View File

@@ -18,93 +18,89 @@ from homeassistant.util import slugify
from . import UPDATE_TOPIC, WaterFurnaceConfigEntry, WaterFurnaceData
SENSORS = [
SensorEntityDescription(name="Furnace Mode", key="mode", icon="mdi:gauge"),
SensorEntityDescription(
key="mode",
translation_key="mode",
),
SensorEntityDescription(
name="Total Power",
key="totalunitpower",
translation_key="total_unit_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Active Setpoint",
key="tstatactivesetpoint",
translation_key="tstat_active_setpoint",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Leaving Air",
key="leavingairtemp",
translation_key="leaving_air_temp",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Room Temp",
key="tstatroomtemp",
translation_key="room_temp",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Loop Temp",
key="enteringwatertemp",
translation_key="entering_water_temp",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Humidity Set Point",
key="tstathumidsetpoint",
translation_key="tstat_humid_setpoint",
icon="mdi:water-percent",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Humidity",
key="tstatrelativehumidity",
icon="mdi:water-percent",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Compressor Power",
key="compressorpower",
translation_key="compressor_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Fan Power",
key="fanpower",
translation_key="fan_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Aux Power",
key="auxpower",
translation_key="aux_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
name="Loop Pump Power",
key="looppumppower",
translation_key="loop_pump_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="actualcompressorspeed",
translation_key="actual_compressor_speed",
name="Compressor Speed", key="actualcompressorspeed", icon="mdi:speedometer"
),
SensorEntityDescription(
key="airflowcurrentspeed",
translation_key="airflow_current_speed",
name="Fan Speed", key="airflowcurrentspeed", icon="mdi:fan"
),
]
@@ -128,7 +124,6 @@ class WaterFurnaceSensor(SensorEntity):
"""Implementing the Waterfurnace sensor."""
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(
self, client: WaterFurnaceData, description: SensorEntityDescription

View File

@@ -26,49 +26,6 @@
}
}
},
"entity": {
"sensor": {
"actual_compressor_speed": {
"name": "Compressor speed"
},
"airflow_current_speed": {
"name": "Fan speed"
},
"aux_power": {
"name": "Aux power"
},
"compressor_power": {
"name": "Compressor power"
},
"entering_water_temp": {
"name": "Loop temperature"
},
"fan_power": {
"name": "Fan power"
},
"leaving_air_temp": {
"name": "Leaving air temperature"
},
"loop_pump_power": {
"name": "Loop pump power"
},
"mode": {
"name": "Furnace mode"
},
"room_temp": {
"name": "Room temperature"
},
"total_unit_power": {
"name": "Total power"
},
"tstat_active_setpoint": {
"name": "Active setpoint"
},
"tstat_humid_setpoint": {
"name": "Humidity setpoint"
}
}
},
"issues": {
"deprecated_yaml_import_issue_cannot_connect": {
"description": "Configuring {integration_title} via YAML is deprecated and will be removed in a future release. While importing your configuration, we could not connect to {integration_title}. Please check your YAML configuration and restart Home Assistant, or remove the {domain} key from your configuration and configure the integration via the UI.",

View File

@@ -2865,53 +2865,3 @@ class ServiceRegistry:
if TYPE_CHECKING:
target = cast(Callable[..., ServiceResponse], target)
return await self._hass.async_add_executor_job(target, service_call)
# mypy: disable-error-code="attr-defined,no-untyped-def,unused-ignore,no-untyped-call"
# fmt: off
def _old_chain_future(source, destination):
"""Revert of https://github.com/python/cpython/pull/142358."""
from asyncio import futures # noqa: PLC0415
if not futures.isfuture(source) and not isinstance(
source, concurrent.futures.Future
):
raise TypeError("A future is required for source argument")
if not futures.isfuture(destination) and not isinstance(
destination, concurrent.futures.Future
):
raise TypeError("A future is required for destination argument")
source_loop = futures._get_loop(source) if futures.isfuture(source) else None # noqa: SLF001
dest_loop = (
futures._get_loop(destination) if futures.isfuture(destination) else None # noqa: SLF001
)
def _set_state(future, other):
if futures.isfuture(future):
futures._copy_future_state(other, future) # noqa: SLF001
else:
futures._set_concurrent_future_state(future, other) # noqa: SLF001
def _call_check_cancel(destination):
if destination.cancelled():
if source_loop is None or source_loop is dest_loop:
source.cancel()
else:
source_loop.call_soon_threadsafe(source.cancel)
def _call_set_state(source):
if destination.cancelled() and dest_loop is not None and dest_loop.is_closed():
return
if dest_loop is None or dest_loop is source_loop:
_set_state(destination, source)
else:
if dest_loop.is_closed():
return
dest_loop.call_soon_threadsafe(_set_state, destination, source)
destination.add_done_callback(_call_check_cancel)
source.add_done_callback(_call_set_state)
# monkey-patch asyncio to revert to 3.14.2 behavior
asyncio.futures._chain_future = _old_chain_future # noqa: SLF001

View File

@@ -1,10 +0,0 @@
image: ghcr.io/home-assistant/{machine}-homeassistant
build_from:
aarch64: "ghcr.io/home-assistant/aarch64-homeassistant:"
amd64: "ghcr.io/home-assistant/amd64-homeassistant:"
cosign:
base_identity: https://github.com/home-assistant/core/.*
identity: https://github.com/home-assistant/core/.*
labels:
io.hass.type: core
org.opencontainers.image.source: https://github.com/home-assistant/core

2
requirements_all.txt generated
View File

@@ -1105,7 +1105,7 @@ google-nest-sdm==9.1.2
google-photos-library-api==0.12.1
# homeassistant.components.google_air_quality
google_air_quality_api==3.0.1
google_air_quality_api==3.0.0
# homeassistant.components.slide
# homeassistant.components.slide_local

View File

@@ -981,7 +981,7 @@ google-nest-sdm==9.1.2
google-photos-library-api==0.12.1
# homeassistant.components.google_air_quality
google_air_quality_api==3.0.1
google_air_quality_api==3.0.0
# homeassistant.components.slide
# homeassistant.components.slide_local