Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
14 changes: 11 additions & 3 deletions homeassistant/components/bosch_alarm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@

from bosch_alarm_mode2 import Panel

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.typing import ConfigType

from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
from .services import setup_services
from .types import BoschAlarmConfigEntry

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)

PLATFORMS: list[Platform] = [
Platform.ALARM_CONTROL_PANEL,
Expand All @@ -22,7 +26,11 @@
Platform.SWITCH,
]

type BoschAlarmConfigEntry = ConfigEntry[Panel]

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up bosch alarm services."""
setup_services(hass)
return True


async def async_setup_entry(hass: HomeAssistant, entry: BoschAlarmConfigEntry) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import BoschAlarmConfigEntry
from .entity import BoschAlarmAreaEntity
from .types import BoschAlarmConfigEntry


async def async_setup_entry(
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/bosch_alarm/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Constants for the Bosch Alarm integration."""

DOMAIN = "bosch_alarm"
HISTORY_ATTR = "history"
ATTR_HISTORY = "history"
CONF_INSTALLER_CODE = "installer_code"
CONF_USER_CODE = "user_code"
ATTR_DATETIME = "datetime"
SERVICE_SET_DATE_TIME = "set_date_time"
ATTR_CONFIG_ENTRY_ID = "config_entry_id"
2 changes: 1 addition & 1 deletion homeassistant/components/bosch_alarm/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from homeassistant.const import CONF_PASSWORD
from homeassistant.core import HomeAssistant

from . import BoschAlarmConfigEntry
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE
from .types import BoschAlarmConfigEntry

TO_REDACT = [CONF_INSTALLER_CODE, CONF_USER_CODE, CONF_PASSWORD]

Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/bosch_alarm/icons.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"services": {
"set_date_time": {
"service": "mdi:clock-edit"
}
},
"entity": {
"sensor": {
"alarms_gas": {
Expand Down
10 changes: 2 additions & 8 deletions homeassistant/components/bosch_alarm/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ rules:
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
No custom actions are defined.
docs-actions: done
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
Expand All @@ -29,10 +26,7 @@ rules:
unique-config-entry: done

# Silver
action-exceptions:
status: exempt
comment: |
No custom actions are defined.
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
Expand Down
76 changes: 76 additions & 0 deletions homeassistant/components/bosch_alarm/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""Services for the bosch_alarm integration."""

from __future__ import annotations

import asyncio
import datetime as dt
from typing import Any

import voluptuous as vol

from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.util import dt as dt_util

from .const import ATTR_CONFIG_ENTRY_ID, ATTR_DATETIME, DOMAIN, SERVICE_SET_DATE_TIME
from .types import BoschAlarmConfigEntry


def validate_datetime(value: Any) -> dt.datetime:
"""Validate that a provided datetime is supported on a bosch alarm panel."""
date_val = cv.datetime(value)
if date_val.year < 2010:
raise vol.RangeInvalid("datetime must be after 2009")

if date_val.year > 2037:
raise vol.RangeInvalid("datetime must be before 2038")

return date_val


SET_DATE_TIME_SCHEMA = vol.Schema(
{
vol.Required(ATTR_CONFIG_ENTRY_ID): cv.string,
vol.Optional(ATTR_DATETIME): validate_datetime,
}
)


def setup_services(hass: HomeAssistant) -> None:
"""Set up the services for the bosch alarm integration."""

async def async_set_panel_date(call: ServiceCall) -> None:
"""Set the date and time on a bosch alarm panel."""
config_entry: BoschAlarmConfigEntry | None
value: dt.datetime = call.data.get(ATTR_DATETIME, dt_util.now())
entry_id = call.data[ATTR_CONFIG_ENTRY_ID]
if not (config_entry := hass.config_entries.async_get_entry(entry_id)):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="integration_not_found",
translation_placeholders={"target": entry_id},
)
if config_entry.state is not ConfigEntryState.LOADED:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_loaded",
translation_placeholders={"target": config_entry.title},
)
panel = config_entry.runtime_data
try:
await panel.set_panel_date(value)
except asyncio.InvalidStateError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="connection_error",
translation_placeholders={"target": config_entry.title},
) from err

hass.services.async_register(
DOMAIN,
SERVICE_SET_DATE_TIME,
async_set_panel_date,
schema=SET_DATE_TIME_SCHEMA,
)
12 changes: 12 additions & 0 deletions homeassistant/components/bosch_alarm/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
set_date_time:
fields:
config_entry_id:
required: true
selector:
config_entry:
integration: bosch_alarm
datetime:
required: false
example: "2025-05-10 00:00:00"
selector:
datetime:
28 changes: 28 additions & 0 deletions homeassistant/components/bosch_alarm/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@
}
},
"exceptions": {
"integration_not_found": {
"message": "Integration \"{target}\" not found in registry."
},
"not_loaded": {
"message": "{target} is not loaded."
},
"connection_error": {
"message": "Could not connect to \"{target}\"."
},
"unknown_error": {
"message": "An unknown error occurred while setting the date and time on \"{target}\"."
},
"cannot_connect": {
"message": "Could not connect to panel."
},
Expand All @@ -61,6 +73,22 @@
"message": "Door cannot be manipulated while it is being cycled."
}
},
"services": {
"set_date_time": {
"name": "Set date & time",
"description": "Sets the date and time on the alarm panel.",
"fields": {
"datetime": {
"name": "Date & time",
"description": "The date and time to set. The time zone of the Home Assistant instance is assumed. If omitted, the current date and time is used."
},
"config_entry_id": {
"name": "Config entry",
"description": "The Bosch Alarm integration ID."
}
}
}
},
"entity": {
"binary_sensor": {
"panel_fault_battery_mising": {
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/bosch_alarm/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Types for the Bosch Alarm integration."""

from bosch_alarm_mode2 import Panel

from homeassistant.config_entries import ConfigEntry

type BoschAlarmConfigEntry = ConfigEntry[Panel]
Loading