Skip to content

Commit

Permalink
V4.7.0
Browse files Browse the repository at this point in the history
V4.7.0
  • Loading branch information
ikalchev authored Jun 18, 2023
2 parents 8b62c19 + e346dab commit 54d174d
Show file tree
Hide file tree
Showing 19 changed files with 282 additions and 154 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, "3.10"]
python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]

steps:
- uses: actions/checkout@v1
Expand All @@ -35,7 +35,7 @@ jobs:

strategy:
matrix:
python-version: [3.9]
python-version: ["3.10"]

steps:
- uses: actions/checkout@v1
Expand Down Expand Up @@ -66,7 +66,7 @@ jobs:

strategy:
matrix:
python-version: [3.9]
python-version: ["3.10"]

steps:
- uses: actions/checkout@v1
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ Sections
### Developers
-->

## [4.7.0] - 2023-06-18

- Allow passing multiple ip to advertise on to AccessoryDriver. [#442](https://github.com/ikalchev/HAP-python/pull/442)
- Fix for the new home architecture - retain the original format of the UUID. [#441](https://github.com/ikalchev/HAP-python/pull/441)
- Add python 3.11 to the CI. [#440](https://github.com/ikalchev/HAP-python/pull/440)
- Use orjson.loads in loader to speed up startup. [#436](https://github.com/ikalchev/HAP-python/pull/436)

## [4.6.0] - 2022-12-10

- Patch for [WinError 5] Access Denied. [#421](https://github.com/ikalchev/HAP-python/pull/421)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The project was developed for a Raspberry Pi, but it should work on other platfo
you can open `main.py` or `busy_home.py`, where you will find some fake accessories.
Just run one of them, for example `python3 busy_home.py`, and you can add it in
the Home app (be sure to be in the same network).
Stop it by hitting Ctrl+C.
Stop it by hitting <kbd>Ctrl</kbd>+<kbd>C</kbd>.

There are example accessories as well as integrations with real products
in [the accessories folder](accessories). See how to configure your camera in
Expand Down Expand Up @@ -90,7 +90,7 @@ class TemperatureSensor(Accessory):
"""
print('Temperature changed to: ', value)

@Acessory.run_at_interval(3) # Run this method every 3 seconds
@Accessory.run_at_interval(3) # Run this method every 3 seconds
# The `run` method can be `async` as well
def run(self):
"""We override this method to implement what the accessory will do when it is
Expand Down Expand Up @@ -151,7 +151,7 @@ class Light(Accessory):
if "Brightness" in char_values:
print('Brightness changed to: ', char_values["Brightness"])

@Acessory.run_at_interval(3) # Run this method every 3 seconds
@Accessory.run_at_interval(3) # Run this method every 3 seconds
# The `run` method can be `async` as well
def run(self):
"""We override this method to implement what the accessory will do when it is
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autodoc', 'sphinx.ext.viewcode'
]

# Add any paths that contain templates here, relative to this directory.
Expand Down
47 changes: 29 additions & 18 deletions pyhap/accessory_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
import logging
import os
import re
import socket
import sys
import tempfile
import time
import threading
import time
from typing import Optional

from zeroconf import ServiceInfo
from zeroconf.asyncio import AsyncZeroconf

from pyhap import util
from pyhap.accessory import get_topic
from pyhap.accessory import Accessory, get_topic
from pyhap.characteristic import CharacteristicError
from pyhap.const import (
HAP_PERMISSION_NOTIFY,
Expand All @@ -41,9 +41,9 @@
HAP_REPR_AID,
HAP_REPR_CHARS,
HAP_REPR_IID,
HAP_REPR_TTL,
HAP_REPR_PID,
HAP_REPR_STATUS,
HAP_REPR_TTL,
HAP_REPR_VALUE,
STANDALONE_AID,
)
Expand Down Expand Up @@ -122,7 +122,7 @@ class AccessoryMDNSServiceInfo(ServiceInfo):

def __init__(self, accessory, state, zeroconf_server=None):
self.accessory = accessory
self.state = state
self.state: State = state

adv_data = self._get_advert_data()
valid_name = self._valid_name()
Expand All @@ -139,7 +139,7 @@ def __init__(self, accessory, state, zeroconf_server=None):
weight=0,
priority=0,
properties=adv_data,
addresses=[socket.inet_aton(self.state.address)],
parsed_addresses=self.state.addresses,
)

def _valid_name(self):
Expand Down Expand Up @@ -244,10 +244,10 @@ def __init__(
If not given, the value of the address parameter will be used.
:type listen_address: str
:param advertised_address: The address of the HAPServer announced via mDNS.
:param advertised_address: The addresses of the HAPServer announced via mDNS.
This can be used to announce an external address from behind a NAT.
If not given, the value of the address parameter will be used.
:type advertised_address: str
:type advertised_address: str | list[str]
:param interface_choice: The zeroconf interfaces to listen on.
:type InterfacesType: [InterfaceChoice.Default, InterfaceChoice.All]
Expand Down Expand Up @@ -279,7 +279,7 @@ def __init__(

self.loop = loop

self.accessory = None
self.accessory: Optional[Accessory] = None
self.advertiser = async_zeroconf_instance
self.zeroconf_server = zeroconf_server
self.interface_choice = interface_choice
Expand Down Expand Up @@ -366,9 +366,9 @@ async def async_start(self):
self.aio_stop_event = asyncio.Event()

logger.info(
"Starting accessory %s on address %s, port %s.",
"Starting accessory %s on addresses %s, port %s.",
self.accessory.display_name,
self.state.address,
self.state.addresses,
self.state.port,
)

Expand Down Expand Up @@ -428,7 +428,7 @@ async def async_stop(self):
logger.info(
"Stopping accessory %s on address %s, port %s.",
self.accessory.display_name,
self.state.address,
self.state.addresses,
self.state.port,
)

Expand Down Expand Up @@ -643,7 +643,9 @@ def persist(self):
) as file_handle:
tmp_filename = file_handle.name
self.encoder.persist(file_handle, self.state)
if os.name == 'nt': # Or `[WinError 5] Access Denied` will be raised on Windows
if (
os.name == "nt"
): # Or `[WinError 5] Access Denied` will be raised on Windows
os.chmod(tmp_filename, 0o644)
os.chmod(self.persist_file, 0o644)
os.replace(tmp_filename, self.persist_file)
Expand All @@ -663,13 +665,18 @@ def load(self):
self.encoder.load_into(file_handle, self.state)

@callback
def pair(self, client_uuid, client_public, client_permissions):
def pair(
self,
client_username_bytes: bytes,
client_public: bytes,
client_permissions: bytes,
) -> bool:
"""Called when a client has paired with the accessory.
Persist the new accessory state.
:param client_uuid: The client uuid.
:type client_uuid: uuid.UUID
:param client_username_bytes: The client username bytes.
:type client_username_bytes: bytes
:param client_public: The client's public key.
:type client_public: bytes
Expand All @@ -681,9 +688,13 @@ def pair(self, client_uuid, client_public, client_permissions):
:rtype: bool
"""
logger.info(
"Paired with %s with permissions %s.", client_uuid, client_permissions
"Paired with %s with permissions %s.",
client_username_bytes,
client_permissions,
)
self.state.add_paired_client(
client_username_bytes, client_public, client_permissions
)
self.state.add_paired_client(client_uuid, client_public, client_permissions)
self.async_persist()
return True

Expand Down
2 changes: 1 addition & 1 deletion pyhap/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""This module contains constants used by other modules."""
MAJOR_VERSION = 4
MINOR_VERSION = 6
MINOR_VERSION = 7
PATCH_VERSION = 0
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
Expand Down
13 changes: 11 additions & 2 deletions pyhap/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cryptography.hazmat.primitives.asymmetric import ed25519

from .const import CLIENT_PROP_PERMS
from .state import State


class AccessoryEncoder:
Expand Down Expand Up @@ -45,7 +46,7 @@ class AccessoryEncoder:
"""

@staticmethod
def persist(fp, state):
def persist(fp, state: State):
"""Persist the state of the given Accessory to the given file object.
Persists:
Expand All @@ -61,12 +62,16 @@ def persist(fp, state):
client_properties = {
str(client): props for client, props in state.client_properties.items()
}
client_uuid_to_bytes = {
str(client): bytes.hex(key) for client, key in state.uuid_to_bytes.items()
}
config_state = {
"mac": state.mac,
"config_version": state.config_version,
"paired_clients": paired_clients,
"client_properties": client_properties,
"accessories_hash": state.accessories_hash,
"client_uuid_to_bytes": client_uuid_to_bytes,
"private_key": bytes.hex(
state.private_key.private_bytes(
encoding=serialization.Encoding.Raw,
Expand All @@ -84,7 +89,7 @@ def persist(fp, state):
json.dump(config_state, fp)

@staticmethod
def load_into(fp, state):
def load_into(fp, state: State) -> None:
"""Load the accessory state from the given file object into the given Accessory.
@see: AccessoryEncoder.persist
Expand Down Expand Up @@ -115,3 +120,7 @@ def load_into(fp, state):
state.public_key = ed25519.Ed25519PublicKey.from_public_bytes(
bytes.fromhex(loaded["public_key"])
)
state.uuid_to_bytes = {
uuid.UUID(client): bytes.fromhex(key)
for client, key in loaded.get("client_uuid_to_bytes", {}).items()
}
Loading

0 comments on commit 54d174d

Please sign in to comment.