diff --git a/README.md b/README.md
index 0c17ca68..de109aba 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,11 @@
-## Python wrapper for Homeassistant's [REST API](https://developers.home-assistant.io/docs/api/rest/)
+## Python wrapper for Homeassistant's [Websocket API](https://developers.home-assistant.io/docs/api/websocket/) and [REST API](https://developers.home-assistant.io/docs/api/rest/)
+
+> Note: As of [this comment](https://github.com/home-assistant/architecture/discussions/1074#discussioncomment-9196867) the REST API is not getting any new features or endpoints.
+> However, it is not going to be deprecated according to [this comment](https://github.com/home-assistant/developers.home-assistant/pull/2150#pullrequestreview-2017433583)
+> But it is recommended to use the Websocket API for new integrations.
Here is a quick example.
@@ -18,17 +22,15 @@ Here is a quick example.
from homeassistant_api import Client
with Client(
- '',
+ '', # i.e. 'http://homeassistant.local:8123/api/'
''
) as client:
-
- light = client.get_domain("light")
-
- light.turn_on(entity_id="light.living_room_lamp")
+ light = client.trigger_service('light', 'turn_on', {'entity_id': 'light.living_room'})
```
-All the methods also support async!
-Just prefix the method with `async_`
+All the methods also support async/await!
+Just prefix the method with `async_` and pass the `use_async=True` argument to the `Client` constructor.
+Then you can use the methods as coroutines
(i.e. `await light.async_turn_on(...)`).
## Documentation
diff --git a/docs/conf.py b/docs/conf.py
index f9f26fd8..8f8e4f98 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,4 +1,5 @@
"""Configuration file for the Sphinx documentation builder."""
+
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
diff --git a/homeassistant_api/__init__.py b/homeassistant_api/__init__.py
index 0c061f24..8f6ca386 100644
--- a/homeassistant_api/__init__.py
+++ b/homeassistant_api/__init__.py
@@ -1,6 +1,5 @@
"""Interact with your Homeassistant Instance remotely."""
-
__all__ = (
"Client",
"State",
diff --git a/homeassistant_api/client.py b/homeassistant_api/client.py
index 878b437e..94c23c86 100644
--- a/homeassistant_api/client.py
+++ b/homeassistant_api/client.py
@@ -1,6 +1,9 @@
"""Module containing the primary Client class."""
+
import logging
from typing import Any
+import urllib.parse as urlparse
+import warnings
from .rawasyncclient import RawAsyncClient
from .rawclient import RawClient
@@ -21,12 +24,26 @@ class Client(RawClient, RawAsyncClient):
def __init__(
self,
- *args: Any,
+ api_url: str,
+ token: str,
use_async: bool = False,
verify_ssl: bool = True,
- **kwargs: Any
+ **kwargs: Any,
) -> None:
- if use_async:
- RawAsyncClient.__init__(self, *args, verify_ssl=verify_ssl, **kwargs)
+ parsed = urlparse.urlparse(api_url)
+
+ if parsed.scheme in {"http", "https"}:
+ if use_async:
+ RawAsyncClient.__init__(
+ self, api_url, token, verify_ssl=verify_ssl, **kwargs
+ )
+ else:
+ RawClient.__init__(
+ self, api_url, token, verify_ssl=verify_ssl, **kwargs
+ )
+ warnings.warn(
+ "The REST API is being phased out and will be removed in a far future release. Please use the WebSocket API instead.",
+ DeprecationWarning,
+ )
else:
- RawClient.__init__(self, *args, verify_ssl=verify_ssl, **kwargs)
+ raise ValueError(f"Unknown scheme {parsed.scheme} in {api_url}")
diff --git a/homeassistant_api/errors.py b/homeassistant_api/errors.py
index a6396cda..015c93ad 100644
--- a/homeassistant_api/errors.py
+++ b/homeassistant_api/errors.py
@@ -3,7 +3,7 @@
from typing import Union
-class HomeassistantAPIError(BaseException):
+class HomeassistantAPIError(Exception):
"""Base class for custom errors"""
@@ -84,3 +84,11 @@ class UnexpectedStatusCodeError(ResponseError):
def __init__(self, status_code: int) -> None:
super().__init__(f"Response has unexpected status code: {status_code!r}")
+
+
+class WebsocketError(HomeassistantAPIError):
+ """Error raised when an issue occurs with the websocket connection."""
+
+
+class ReceivingError(WebsocketError):
+ """Error raised when an issue occurs when receiving a message from the websocket server."""
diff --git a/homeassistant_api/models/__init__.py b/homeassistant_api/models/__init__.py
index 3beeda4c..d34928b2 100644
--- a/homeassistant_api/models/__init__.py
+++ b/homeassistant_api/models/__init__.py
@@ -1,4 +1,5 @@
"""The Model objects for the entire library."""
+
from .base import BaseModel
from .domains import Domain, Service, ServiceField
from .entity import Entity, Group
diff --git a/homeassistant_api/models/base.py b/homeassistant_api/models/base.py
index 4e6a7789..92b9b418 100644
--- a/homeassistant_api/models/base.py
+++ b/homeassistant_api/models/base.py
@@ -7,12 +7,14 @@
DatetimeIsoField = Annotated[
- datetime, PlainSerializer(lambda x: x.isoformat(), return_type=str, when_used='json')
+ datetime,
+ PlainSerializer(lambda x: x.isoformat(), return_type=str, when_used="json"),
]
class BaseModel(PydanticBaseModel):
"""Base model that all Library Models inherit from."""
+
model_config = ConfigDict(
arbitrary_types_allowed=True,
validate_assignment=True,
diff --git a/homeassistant_api/models/domains.py b/homeassistant_api/models/domains.py
index b6a4639d..5af69f17 100644
--- a/homeassistant_api/models/domains.py
+++ b/homeassistant_api/models/domains.py
@@ -1,10 +1,13 @@
"""File for Service and Domain data models"""
+
import gc
import inspect
from typing import TYPE_CHECKING, Any, Coroutine, Dict, Optional, Tuple, Union, cast
from pydantic import Field
+from homeassistant_api.errors import RequestError
+
from .base import BaseModel
from .states import State
@@ -36,9 +39,7 @@ def __init__(self, *args, _client: Optional["Client"] = None, **kwargs) -> None:
def from_json(cls, json: Dict[str, Any], client: "Client") -> "Domain":
"""Constructs Domain and Service models from json data."""
if "domain" not in json or "services" not in json:
- raise ValueError(
- "Missing services or attribute attribute in json argument."
- )
+ raise ValueError("Missing services or domain attribute in json argument.")
domain = cls(domain_id=cast(str, json.get("domain")), _client=client)
services = json.get("services")
assert isinstance(services, dict)
@@ -67,7 +68,13 @@ def __getattr__(self, attr: str):
"""Allows services accessible as attributes"""
if attr in self.services:
return self.get_service(attr)
- return super().__getattribute__(attr)
+ try:
+ return super().__getattribute__(attr)
+ except AttributeError as err:
+ try:
+ return object.__getattribute__(self, attr)
+ except AttributeError as e:
+ raise e from err
class ServiceField(BaseModel):
@@ -89,26 +96,49 @@ class Service(BaseModel):
description: Optional[str] = None
fields: Optional[Dict[str, ServiceField]] = None
- def trigger(self, **service_data) -> Tuple[State, ...]:
- """Triggers the service associated with this object."""
- return self.domain._client.trigger_service(
- self.domain.domain_id,
- self.service_id,
- **service_data,
- )
-
- async def async_trigger(self, **service_data) -> Tuple[State, ...]:
+ def trigger(
+ self, **service_data
+ ) -> Union[Tuple[State, ...], Tuple[Tuple[State, ...], Dict[str, Any]]]:
"""Triggers the service associated with this object."""
- return await self.domain._client.async_trigger_service(
- self.domain.domain_id,
- self.service_id,
- **service_data,
- )
+ try:
+ return self.domain._client.trigger_service_with_response(
+ self.domain.domain_id,
+ self.service_id,
+ **service_data,
+ )
+ except RequestError:
+ return self.domain._client.trigger_service(
+ self.domain.domain_id,
+ self.service_id,
+ **service_data,
+ )
- def __call__(
+ async def async_trigger(
self, **service_data
- ) -> Union[Tuple[State, ...], Coroutine[Any, Any, Tuple[State, ...]]]:
+ ) -> Union[Tuple[State, ...], Tuple[Tuple[State, ...], Dict[str, Any]]]:
"""Triggers the service associated with this object."""
+ try:
+ return await self.domain._client.async_trigger_service_with_response(
+ self.domain.domain_id,
+ self.service_id,
+ **service_data,
+ )
+ except RequestError:
+ return await self.domain._client.async_trigger_service(
+ self.domain.domain_id,
+ self.service_id,
+ **service_data,
+ )
+
+ def __call__(self, **service_data) -> Union[
+ Union[Tuple[State, ...], Tuple[Tuple[State, ...], Dict[str, Any]]],
+ Coroutine[
+ Any, Any, Union[Tuple[State, ...], Tuple[Tuple[State, ...], Dict[str, Any]]]
+ ],
+ ]:
+ """
+ Triggers the service associated with this object.
+ """
assert (frame := inspect.currentframe()) is not None
assert (parent_frame := frame.f_back) is not None
try:
diff --git a/homeassistant_api/models/history.py b/homeassistant_api/models/history.py
index 8b746b07..f683d471 100644
--- a/homeassistant_api/models/history.py
+++ b/homeassistant_api/models/history.py
@@ -1,4 +1,5 @@
"""Module for the History model."""
+
from typing import Tuple
from pydantic import Field
diff --git a/homeassistant_api/models/logbook.py b/homeassistant_api/models/logbook.py
index da38772a..08e21c7d 100644
--- a/homeassistant_api/models/logbook.py
+++ b/homeassistant_api/models/logbook.py
@@ -1,4 +1,5 @@
"""Module for the Logbook Entry model."""
+
from typing import Optional
from pydantic import Field
diff --git a/homeassistant_api/models/states.py b/homeassistant_api/models/states.py
index ebf71bd4..0ff01844 100644
--- a/homeassistant_api/models/states.py
+++ b/homeassistant_api/models/states.py
@@ -1,4 +1,5 @@
"""Module for the Entity State model."""
+
from datetime import datetime
from typing import Any, Dict, Optional
diff --git a/homeassistant_api/rawasyncclient.py b/homeassistant_api/rawasyncclient.py
index 35f74ca6..18c6197c 100644
--- a/homeassistant_api/rawasyncclient.py
+++ b/homeassistant_api/rawasyncclient.py
@@ -1,4 +1,5 @@
"""Module for interacting with Home Assistant asyncronously."""
+
from __future__ import annotations
import asyncio
@@ -176,11 +177,14 @@ async def async_get_rendered_template(self, template: str) -> str:
:code:`POST /api/template`
"""
try:
- return cast(str, await self.async_request(
- "template",
- json=dict(template=template),
- method="POST",
- ))
+ return cast(
+ str,
+ await self.async_request(
+ "template",
+ json=dict(template=template),
+ method="POST",
+ ),
+ )
except RequestError as err:
raise BadTemplateError(
"Your template is invalid. "
@@ -291,6 +295,34 @@ async def async_trigger_service(
)
return tuple(map(State.from_json, cast(List[Dict[Any, Any]], data)))
+ async def async_trigger_service_with_response(
+ self,
+ domain: str,
+ service: str,
+ **service_data: Union[Dict[str, Any], List[Any], str],
+ ) -> tuple[tuple[State, ...], dict[str, Any]]:
+ """
+ Tells Home Assistant to trigger a service, returns the response from the service call.
+ :code:`POST /api/services//`
+
+ Returns a list of the states changed and the response from the service call.
+ """
+ data = cast(
+ dict[str, Any],
+ await self.async_request(
+ join("services", domain, service) + "?return_response",
+ method="POST",
+ json=service_data,
+ ),
+ )
+ states = tuple(
+ map(
+ State.from_json,
+ cast(List[Dict[Any, Any]], data.get("changed_states", [])),
+ )
+ )
+ return states, data.get("service_response", {})
+
# EntityState methods
async def async_get_state( # pylint: disable=duplicate-code
self,
diff --git a/homeassistant_api/rawbaseclient.py b/homeassistant_api/rawbaseclient.py
index 6f3c2df6..b5c2a8af 100644
--- a/homeassistant_api/rawbaseclient.py
+++ b/homeassistant_api/rawbaseclient.py
@@ -104,15 +104,14 @@ def prepare_get_entity_histories_params(
end_timestamp: Optional[datetime] = None,
significant_changes_only: bool = False,
) -> Tuple[Dict[str, Optional[str]], str]:
-
"""Pre-logic for `Client.get_entity_histories` and `Client.async_get_entity_histories`."""
params: Dict[str, Optional[str]] = {}
if entities is not None:
params["filter_entity_id"] = ",".join([ent.entity_id for ent in entities])
if end_timestamp is not None:
- params[
- "end_time"
- ] = end_timestamp.isoformat() # Params are automatically URL encoded
+ params["end_time"] = (
+ end_timestamp.isoformat()
+ ) # Params are automatically URL encoded
if significant_changes_only:
params["significant_changes_only"] = None
if start_timestamp is not None:
@@ -134,9 +133,11 @@ def prepare_get_logbook_entry_params(
if filter_entities is not None:
params.update(
{
- "entity": filter_entities
- if isinstance(filter_entities, str)
- else ",".join(filter_entities)
+ "entity": (
+ filter_entities
+ if isinstance(filter_entities, str)
+ else ",".join(filter_entities)
+ )
}
)
if end_timestamp is not None:
diff --git a/homeassistant_api/rawclient.py b/homeassistant_api/rawclient.py
index 99c56141..2f4b92e8 100644
--- a/homeassistant_api/rawclient.py
+++ b/homeassistant_api/rawclient.py
@@ -1,4 +1,5 @@
"""Module for all interaction with homeassistant."""
+
from __future__ import annotations
import json
@@ -290,6 +291,34 @@ def trigger_service(
)
return tuple(map(State.from_json, cast(List[Dict[str, Any]], data)))
+ def trigger_service_with_response(
+ self,
+ domain: str,
+ service: str,
+ **service_data,
+ ) -> tuple[tuple[State, ...], dict[str, Any]]:
+ """
+ Tells Home Assistant to trigger a service, returns the response from the service call.
+ :code:`POST /api/services//`
+
+ Returns a list of the states changed and the response from the service call.
+ """
+ data = cast(
+ dict[str, Any],
+ self.request(
+ join("services", domain, service) + "?return_response",
+ method="POST",
+ json=service_data,
+ ),
+ )
+ states = tuple(
+ map(
+ State.from_json,
+ cast(List[Dict[Any, Any]], data.get("changed_states", [])),
+ )
+ )
+ return states, data.get("service_response", {})
+
# EntityState methods
def get_state( # pylint: disable=duplicate-code
self,
diff --git a/poetry.lock b/poetry.lock
index bf433a7f..6b5521fa 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -196,9 +196,6 @@ files = [
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
-[package.dependencies]
-typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
-
[[package]]
name = "appdirs"
version = "1.4.4"
@@ -262,7 +259,6 @@ files = [
]
[package.dependencies]
-importlib-metadata = {version = ">1", markers = "python_version <= \"3.8\""}
pydantic = ">=2.0,<3.0.0"
pydantic-settings = ">=2.0,<3.0.0"
Sphinx = ">=4.0"
@@ -284,9 +280,6 @@ files = [
{file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
]
-[package.dependencies]
-pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
-
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
@@ -1318,17 +1311,6 @@ files = [
[package.extras]
cli = ["click (>=5.0)"]
-[[package]]
-name = "pytz"
-version = "2024.2"
-description = "World timezone definitions, modern and historical"
-optional = false
-python-versions = "*"
-files = [
- {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
- {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
-]
-
[[package]]
name = "pyyaml"
version = "6.0.2"
@@ -1915,6 +1897,84 @@ platformdirs = ">=3.9.1,<5"
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+[[package]]
+name = "websockets"
+version = "14.1"
+description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"},
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"},
+ {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"},
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"},
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"},
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"},
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"},
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"},
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"},
+ {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"},
+ {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"},
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"},
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"},
+ {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"},
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"},
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"},
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"},
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"},
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"},
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"},
+ {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"},
+ {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"},
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"},
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"},
+ {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"},
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"},
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"},
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"},
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"},
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"},
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"},
+ {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"},
+ {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"},
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"},
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"},
+ {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"},
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"},
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"},
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"},
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"},
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"},
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"},
+ {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"},
+ {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"},
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"},
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"},
+ {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"},
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"},
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"},
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"},
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"},
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"},
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"},
+ {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"},
+ {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"},
+ {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"},
+ {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"},
+ {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"},
+ {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
+]
+
[[package]]
name = "yarl"
version = "1.11.1"
@@ -2041,5 +2101,5 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
-python-versions = "^3.8,<4.0.0"
-content-hash = "d34208b22c2ba46b75659320716599123140bfe47a61eb0af570447f6e9f1d8d"
+python-versions = "^3.9,<4.0.0"
+content-hash = "f952a0fbb8688f207621a984abf9b372012048efe1021a59d20ef11b1a4cda15"
diff --git a/pyproject.toml b/pyproject.toml
index cd52b2db..151ca1df 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,10 +19,11 @@ packages = [{ include = "homeassistant_api" }]
aiohttp = "^3.8.1"
aiohttp-client-cache = ">=0.6.1"
pydantic = ">=2.0,<2.9"
-python = "^3.8,<4.0.0"
+python = "^3.9,<4.0.0"
requests = "^2.27.1"
requests-cache = "^0.9.2"
simplejson = "^3.17.6"
+websockets = "^14.1"
[tool.poetry.group.docs]
optional = true
diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py
index 6175961b..587fc140 100644
--- a/tests/test_endpoints.py
+++ b/tests/test_endpoints.py
@@ -171,6 +171,28 @@ async def test_async_trigger_service(async_cached_client: Client) -> None:
assert isinstance(resp, tuple)
+def test_trigger_service_with_response(cached_client: Client) -> None:
+ """Tests the `POST /api/services//?return_response` endpoint."""
+ weather = cached_client.get_domain("weather")
+ assert weather is not None
+ changed_states, data = weather.get_forecasts(
+ entity_id="weather.forecast_home",
+ type="hourly",
+ )
+ assert data is not None
+
+
+async def test_async_trigger_service_with_response(async_cached_client: Client) -> None:
+ """Tests the `POST /api/services//?return_response` endpoint."""
+ weather = await async_cached_client.async_get_domain("weather")
+ assert weather is not None
+ changed_states, data = await weather.get_forecasts(
+ entity_id="weather.forecast_home",
+ type="hourly",
+ )
+ assert data is not None
+
+
def test_get_states(cached_client: Client) -> None:
"""Tests the `GET /api/states` endpoint."""
states = cached_client.get_states()
diff --git a/tests/test_errors.py b/tests/test_errors.py
index c1ae8029..ad84d02d 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -2,7 +2,7 @@
import json
import os
-import unittest
+import unittest.mock
from typing import Dict
import aiohttp
@@ -204,3 +204,8 @@ def test_exception_response_error() -> None:
def test_exception_unexpected_status_code() -> None:
with pytest.raises(UnexpectedStatusCodeError):
Processing(make_response(0, "", {})).process()
+
+
+def test_unkown_scheme(cached_client: Client) -> None:
+ with pytest.raises(ValueError):
+ Client("ftp://example.com", "token")
\ No newline at end of file
diff --git a/tests/test_models.py b/tests/test_models.py
index be62cc04..e96c8f5e 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,4 +1,5 @@
"""Module that tests model methods."""
+
import copy
from datetime import datetime
diff --git a/volumes/config/.storage/auth b/volumes/config/.storage/auth
index 2233a3f4..a095fe2e 100644
--- a/volumes/config/.storage/auth
+++ b/volumes/config/.storage/auth
@@ -77,6 +77,7 @@
"jwt_key": "d8213eff81721687a0ea17e8d56e29f8c140015bd046c3924b2b2b4e2b8d77c095e80de14610e955d52650f503a5ec701f46626700496d2021b653f42dff8982",
"last_used_at": "2023-03-11T22:29:03.495460+00:00",
"last_used_ip": "127.0.0.1",
+ "expire_at": null,
"credential_id": "711894da0c83415095b3a6314ff20d86",
"version": "2023.3.3"
},
@@ -93,6 +94,7 @@
"jwt_key": "66b526e5aea268d6751e986e2d486c62ef0274113c1f660d6716b33c347db39591f11ce6be6f3af63d2afa4febc5f2322fac581f05c2e4b6d24060c79631be65",
"last_used_at": "2023-03-11T22:35:32.104562+00:00",
"last_used_ip": "127.0.0.1",
+ "expire_at": null,
"credential_id": "711894da0c83415095b3a6314ff20d86",
"version": "2023.3.3"
},
@@ -109,9 +111,27 @@
"jwt_key": "254697dbff6aafdda02191c86a90529745f774c0e122dd6bdbc22bf0790698f2bb7d4c956736aa8d7c804c8d92266c6b365cce3b191f8d6f10aa706ca3863aa7",
"last_used_at": "2023-03-11T22:41:41.322766+00:00",
"last_used_ip": null,
+ "expire_at": null,
"credential_id": null,
"version": "2023.3.3"
},
+ {
+ "id": "701b4cc2befd4b72ac99a06d7b38181e",
+ "user_id": "e85fc7b7b8924dc9b024ce90ad23799e",
+ "client_id": "http://localhost:8123/",
+ "client_name": null,
+ "client_icon": null,
+ "token_type": "normal",
+ "created_at": "2024-12-20T22:49:28.919077+00:00",
+ "access_token_expiration": 1800.0,
+ "token": "783a5dd38b8e987a6dc7263605d2ae9629487fff7a48884e4b83f39983064a49e89474b7ba0d47ec78b76af8c7ab0e5a0ecce3c0d6ec751af62355a8a3611b72",
+ "jwt_key": "dab8d894766cbdc856cc2475b7f3fd4cb7c1279fbb499a6c1709d5f4264d11842882a28cb3b67259cc983ff7b156781063835d81d76f0356ad0d477d1b09404d",
+ "last_used_at": "2024-12-20T22:49:28.919136+00:00",
+ "last_used_ip": "172.18.0.1",
+ "expire_at": 1742510968.919136,
+ "credential_id": "711894da0c83415095b3a6314ff20d86",
+ "version": "2024.12.3"
+ },
{
"id": "cabbfc0d5e024c2f90b998ffdbea7e61",
"user_id": "6ebcf011775a47c1bc279a1777a1e5a7",
@@ -125,6 +145,7 @@
"jwt_key": "fdeaf63844d1b702572ce014db18ff05074e775f04460ea5674653cee999b7fc0733f9eb99c835b7d6e6317e84bf418c21fcf2ceb65060675782be333896e135",
"last_used_at": null,
"last_used_ip": null,
+ "expire_at": null,
"credential_id": null,
"version": "2022.12.9"
}
diff --git a/volumes/config/.storage/core.area_registry b/volumes/config/.storage/core.area_registry
index 660ab768..2e08d5c2 100644
--- a/volumes/config/.storage/core.area_registry
+++ b/volumes/config/.storage/core.area_registry
@@ -1,6 +1,6 @@
{
"version": 1,
- "minor_version": 1,
+ "minor_version": 7,
"key": "core.area_registry",
"data": {
"areas": [
@@ -8,19 +8,34 @@
"name": "Living Room",
"id": "living_room",
"picture": null,
- "aliases": []
+ "aliases": [],
+ "icon": null,
+ "floor_id": null,
+ "labels": [],
+ "created_at": "1970-01-01T00:00:00+00:00",
+ "modified_at": "1970-01-01T00:00:00+00:00"
},
{
"name": "Kitchen",
"id": "kitchen",
"picture": null,
- "aliases": []
+ "aliases": [],
+ "icon": null,
+ "floor_id": null,
+ "labels": [],
+ "created_at": "1970-01-01T00:00:00+00:00",
+ "modified_at": "1970-01-01T00:00:00+00:00"
},
{
"name": "Bedroom",
"id": "bedroom",
"picture": null,
- "aliases": []
+ "aliases": [],
+ "icon": null,
+ "floor_id": null,
+ "labels": [],
+ "created_at": "1970-01-01T00:00:00+00:00",
+ "modified_at": "1970-01-01T00:00:00+00:00"
}
]
}
diff --git a/volumes/config/.storage/core.config b/volumes/config/.storage/core.config
index e02abd30..a17e6aa1 100644
--- a/volumes/config/.storage/core.config
+++ b/volumes/config/.storage/core.config
@@ -1,6 +1,6 @@
{
"version": 1,
- "minor_version": 3,
+ "minor_version": 4,
"key": "core.config",
"data": {
"latitude": 52.3731339,
@@ -13,6 +13,7 @@
"internal_url": null,
"currency": "USD",
"country": "US",
- "language": "en"
+ "language": "en",
+ "radius": 100
}
}
\ No newline at end of file
diff --git a/volumes/config/.storage/core.config_entries b/volumes/config/.storage/core.config_entries
index 3486ee31..22d6390e 100644
--- a/volumes/config/.storage/core.config_entries
+++ b/volumes/config/.storage/core.config_entries
@@ -1,35 +1,12 @@
{
"version": 1,
- "minor_version": 1,
+ "minor_version": 4,
"key": "core.config_entries",
"data": {
"entries": [
- {
- "entry_id": "5f8426fa502435857743f302651753c9",
- "version": 1,
- "domain": "sun",
- "title": "Sun",
- "data": {},
- "options": {},
- "pref_disable_new_entities": false,
- "pref_disable_polling": false,
- "source": "import",
- "unique_id": null,
- "disabled_by": null
- },
- {
- "entry_id": "bb18b688994e8cfa5e4e880452735628",
- "version": 1,
- "domain": "radio_browser",
- "title": "Radio Browser",
- "data": {},
- "options": {},
- "pref_disable_new_entities": false,
- "pref_disable_polling": false,
- "source": "onboarding",
- "unique_id": null,
- "disabled_by": null
- }
+ {"created_at":"1970-01-01T00:00:00+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"sun","entry_id":"5f8426fa502435857743f302651753c9","minor_version":1,"modified_at":"1970-01-01T00:00:00+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"import","title":"Sun","unique_id":null,"version":1},
+ {"created_at":"1970-01-01T00:00:00+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"radio_browser","entry_id":"bb18b688994e8cfa5e4e880452735628","minor_version":1,"modified_at":"1970-01-01T00:00:00+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"onboarding","title":"Radio Browser","unique_id":null,"version":1},
+ {"created_at":"2024-12-20T17:06:07.193748+00:00","data":{"elevation":1000.0,"latitude":52.3731339,"longitude":4.8903147,"name":"Home"},"disabled_by":null,"discovery_keys":{},"domain":"met","entry_id":"01JFJGH76SD417XC4YJTG8QJWB","minor_version":1,"modified_at":"2024-12-20T17:06:07.193752+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Home","unique_id":null,"version":1}
]
}
}
\ No newline at end of file
diff --git a/volumes/config/.storage/core.device_registry b/volumes/config/.storage/core.device_registry
new file mode 100644
index 00000000..192d9d5d
--- /dev/null
+++ b/volumes/config/.storage/core.device_registry
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "minor_version": 8,
+ "key": "core.device_registry",
+ "data": {
+ "devices": [
+ {"area_id":null,"config_entries":["5f8426fa502435857743f302651753c9"],"configuration_url":null,"connections":[],"created_at":"2024-12-17T01:58:48.779854+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"b3d3fadee11d077d99d948c173631abc","identifiers":[["sun","5f8426fa502435857743f302651753c9"]],"labels":[],"manufacturer":null,"model":null,"model_id":null,"modified_at":"2024-12-17T01:58:48.779886+00:00","name_by_user":null,"name":"Sun","primary_config_entry":"5f8426fa502435857743f302651753c9","serial_number":null,"sw_version":null,"via_device_id":null},
+ {"area_id":null,"config_entries":["01JFJGH76SD417XC4YJTG8QJWB"],"configuration_url":"https://www.met.no/en","connections":[],"created_at":"2024-12-20T17:06:07.780759+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"6586dbdeb10a88ae4603b47e600c6124","identifiers":[["met","01JFJGH76SD417XC4YJTG8QJWB"]],"labels":[],"manufacturer":"Met.no","model":"Forecast","model_id":null,"modified_at":"2024-12-20T17:06:07.780798+00:00","name_by_user":null,"name":"Forecast","primary_config_entry":"01JFJGH76SD417XC4YJTG8QJWB","serial_number":null,"sw_version":null,"via_device_id":null}
+ ],
+ "deleted_devices": []
+ }
+}
\ No newline at end of file
diff --git a/volumes/config/.storage/core.entity_registry b/volumes/config/.storage/core.entity_registry
index 3f870b68..2d820259 100644
--- a/volumes/config/.storage/core.entity_registry
+++ b/volumes/config/.storage/core.entity_registry
@@ -1,34 +1,21 @@
{
"version": 1,
- "minor_version": 8,
+ "minor_version": 15,
"key": "core.entity_registry",
"data": {
"entities": [
- {
- "area_id": null,
- "capabilities": null,
- "config_entry_id": null,
- "device_class": null,
- "device_id": null,
- "disabled_by": null,
- "entity_category": null,
- "entity_id": "person.test_user",
- "hidden_by": null,
- "icon": null,
- "id": "e6de57e591560fad68f1c3b52bf0b295",
- "has_entity_name": false,
- "name": null,
- "options": {},
- "original_device_class": null,
- "original_icon": null,
- "original_name": "Test User",
- "platform": "person",
- "supported_features": 0,
- "unique_id": "test_user",
- "unit_of_measurement": null,
- "translation_key": null,
- "aliases": []
- }
- ]
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":null,"created_at":"1970-01-01T00:00:00+00:00","device_class":null,"device_id":null,"disabled_by":null,"entity_category":null,"entity_id":"person.test_user","hidden_by":null,"icon":null,"id":"e6de57e591560fad68f1c3b52bf0b295","has_entity_name":false,"labels":[],"modified_at":"1970-01-01T00:00:00+00:00","name":null,"options":{},"original_device_class":null,"original_icon":null,"original_name":"Test User","platform":"person","supported_features":0,"translation_key":null,"unique_id":"test_user","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.780004+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_dawn","hidden_by":null,"icon":null,"id":"d7e14c775a20078d98703325848bd203","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.780072+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next dawn","platform":"sun","supported_features":0,"translation_key":"next_dawn","unique_id":"5f8426fa502435857743f302651753c9-next_dawn","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.780360+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_dusk","hidden_by":null,"icon":null,"id":"e1a35874a09227109783c6d1301a10cf","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.780404+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next dusk","platform":"sun","supported_features":0,"translation_key":"next_dusk","unique_id":"5f8426fa502435857743f302651753c9-next_dusk","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.780603+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_midnight","hidden_by":null,"icon":null,"id":"9ec059b13886380e2d5d4a9002e89879","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.780633+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next midnight","platform":"sun","supported_features":0,"translation_key":"next_midnight","unique_id":"5f8426fa502435857743f302651753c9-next_midnight","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.780796+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_noon","hidden_by":null,"icon":null,"id":"04bc6ee0081da1191cb27e2ee5842148","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.780832+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next noon","platform":"sun","supported_features":0,"translation_key":"next_noon","unique_id":"5f8426fa502435857743f302651753c9-next_noon","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.780976+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_rising","hidden_by":null,"icon":null,"id":"ec87eec8ebe365ec0cd1119962627570","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.781008+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next rising","platform":"sun","supported_features":0,"translation_key":"next_rising","unique_id":"5f8426fa502435857743f302651753c9-next_rising","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.781147+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":null,"entity_category":"diagnostic","entity_id":"sensor.sun_next_setting","hidden_by":null,"icon":null,"id":"594acbedee290ea279f1d63cf6efbb3b","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.781172+00:00","name":null,"options":{},"original_device_class":"timestamp","original_icon":null,"original_name":"Next setting","platform":"sun","supported_features":0,"translation_key":"next_setting","unique_id":"5f8426fa502435857743f302651753c9-next_setting","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":{"state_class":"measurement"},"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.781300+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":"integration","entity_category":"diagnostic","entity_id":"sensor.sun_solar_elevation","hidden_by":null,"icon":null,"id":"0df1d222a5fedd8d5e62a2ff2be79094","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.781322+00:00","name":null,"options":{},"original_device_class":null,"original_icon":null,"original_name":"Solar elevation","platform":"sun","supported_features":0,"translation_key":"solar_elevation","unique_id":"5f8426fa502435857743f302651753c9-solar_elevation","previous_unique_id":null,"unit_of_measurement":"°"},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":{"state_class":"measurement"},"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.781397+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":"integration","entity_category":"diagnostic","entity_id":"sensor.sun_solar_azimuth","hidden_by":null,"icon":null,"id":"104de75b64e950af398ec4fe9998338c","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.781417+00:00","name":null,"options":{},"original_device_class":null,"original_icon":null,"original_name":"Solar azimuth","platform":"sun","supported_features":0,"translation_key":"solar_azimuth","unique_id":"5f8426fa502435857743f302651753c9-solar_azimuth","previous_unique_id":null,"unit_of_measurement":"°"},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"5f8426fa502435857743f302651753c9","created_at":"2024-12-17T01:58:48.781483+00:00","device_class":null,"device_id":"b3d3fadee11d077d99d948c173631abc","disabled_by":"integration","entity_category":"diagnostic","entity_id":"sensor.sun_solar_rising","hidden_by":null,"icon":null,"id":"888993d87923ff452ad1e53f6b3a943f","has_entity_name":true,"labels":[],"modified_at":"2024-12-17T01:58:48.781506+00:00","name":null,"options":{},"original_device_class":null,"original_icon":null,"original_name":"Solar rising","platform":"sun","supported_features":0,"translation_key":"solar_rising","unique_id":"5f8426fa502435857743f302651753c9-solar_rising","previous_unique_id":null,"unit_of_measurement":null},
+ {"aliases":[],"area_id":null,"categories":{},"capabilities":null,"config_entry_id":"01JFJGH76SD417XC4YJTG8QJWB","created_at":"2024-12-20T17:06:07.780994+00:00","device_class":null,"device_id":"6586dbdeb10a88ae4603b47e600c6124","disabled_by":null,"entity_category":null,"entity_id":"weather.forecast_home","hidden_by":null,"icon":null,"id":"6ae588672696a2601b0152f22ee235f3","has_entity_name":true,"labels":[],"modified_at":"2024-12-20T17:06:07.781055+00:00","name":null,"options":{},"original_device_class":null,"original_icon":null,"original_name":"Home","platform":"met","supported_features":3,"translation_key":null,"unique_id":"52.3731339-4.8903147","previous_unique_id":null,"unit_of_measurement":null}
+ ],
+ "deleted_entities": []
}
}
\ No newline at end of file
diff --git a/volumes/config/.storage/core.restore_state b/volumes/config/.storage/core.restore_state
index 32d5f535..803d1e2c 100644
--- a/volumes/config/.storage/core.restore_state
+++ b/volumes/config/.storage/core.restore_state
@@ -4,25 +4,9 @@
"key": "core.restore_state",
"data": [
{
- "state": {
- "entity_id": "person.test_user",
- "state": "unknown",
- "attributes": {
- "editable": true,
- "id": "test_user",
- "user_id": "e85fc7b7b8924dc9b024ce90ad23799e",
- "friendly_name": "Test User"
- },
- "last_changed": "2023-03-11T22:49:37.025955+00:00",
- "last_updated": "2023-03-11T22:49:38.001160+00:00",
- "context": {
- "id": "01GV9DV0YHA545RWX832E5W3XW",
- "parent_id": null,
- "user_id": null
- }
- },
+ "state": {"entity_id":"person.test_user","state":"unknown","attributes":{"editable":true,"id":"test_user","device_trackers":[],"user_id":"e85fc7b7b8924dc9b024ce90ad23799e","friendly_name":"Test User"},"last_changed":"2024-12-20T16:29:12.365217+00:00","last_reported":"2024-12-20T16:29:13.064958+00:00","last_updated":"2024-12-20T16:29:13.064958+00:00","context":{"id":"01JFJEDMZ89PTCR8TSCGBK4PP6","parent_id":null,"user_id":null}},
"extra_data": null,
- "last_seen": "2023-03-11T22:49:38.002949+00:00"
+ "last_seen": "2024-12-22T02:24:58.979320+00:00"
}
]
}
\ No newline at end of file
diff --git a/volumes/config/.storage/frontend.user_data_e85fc7b7b8924dc9b024ce90ad23799e b/volumes/config/.storage/frontend.user_data_e85fc7b7b8924dc9b024ce90ad23799e
index 2a759c58..97f5c02c 100644
--- a/volumes/config/.storage/frontend.user_data_e85fc7b7b8924dc9b024ce90ad23799e
+++ b/volumes/config/.storage/frontend.user_data_e85fc7b7b8924dc9b024ce90ad23799e
@@ -7,6 +7,8 @@
"language": "en",
"number_format": "language",
"time_format": "language",
+ "date_format": "language",
+ "time_zone": "local",
"first_weekday": "language"
}
}
diff --git a/volumes/config/.storage/http b/volumes/config/.storage/http
index b517dacf..06543f78 100644
--- a/volumes/config/.storage/http
+++ b/volumes/config/.storage/http
@@ -7,12 +7,17 @@
"127.0.0.1"
],
"use_x_forwarded_for": true,
- "ssl_profile": "modern",
- "ip_ban_enabled": true,
"login_attempts_threshold": -1,
- "server_port": 8123,
+ "server_host": [
+ "0.0.0.0",
+ "::"
+ ],
+ "ip_ban_enabled": true,
+ "use_x_frame_options": true,
"cors_allowed_origins": [
"https://cast.home-assistant.io"
- ]
+ ],
+ "server_port": 8123,
+ "ssl_profile": "modern"
}
}
\ No newline at end of file
diff --git a/volumes/config/configuration.yaml b/volumes/config/configuration.yaml
index 86776625..0db73a01 100644
--- a/volumes/config/configuration.yaml
+++ b/volumes/config/configuration.yaml
@@ -12,6 +12,7 @@ trace:
counter:
tag:
notify:
+weather:
logger:
default: info