Skip to content

Commit

Permalink
Merge pull request #315 from ikalchev/v3.3.0
Browse files Browse the repository at this point in the history
V3.3.0
  • Loading branch information
ikalchev authored Feb 13, 2021
2 parents 43d0cef + 0f27eda commit ec0a350
Show file tree
Hide file tree
Showing 22 changed files with 1,302 additions and 456 deletions.
33 changes: 32 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,35 @@ jobs:
- name: pylint with tox
run: TOXENV=pylint tox
- name: Docs with tox
run: TOXENV=docs tox
run: TOXENV=docs tox

coverage:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: [3.7]

steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Test with tox
run: TOXENV=codecov tox
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
files: ./coverage.xml
directory: ./coverage/reports/
flags: unittests
env_vars: OS,PYTHON
name: codecov-umbrella
fail_ci_if_error: true
path_to_write_report: ./coverage/codecov_report.txt
verbose: true
47 changes: 0 additions & 47 deletions .travis.yml

This file was deleted.

11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ Sections
### Developers
-->

## [3.3.0] - 2021-02-13

### Fixed
- Fix an issue that would cause pairing to fail (implement `list pairings`). [#307](https://github.com/ikalchev/HAP-python/pull/307)
- Remove unsupported characters from Accessory names. [#310](https://github.com/ikalchev/HAP-python/pull/310)
- Speed up event subscription for new connections. [#308](https://github.com/ikalchev/HAP-python/pull/308)
- Properly handle camera snapshots. [#311](https://github.com/ikalchev/HAP-python/pull/311)
- Properly handle pairing attempt when already paired. [#314](https://github.com/ikalchev/HAP-python/pull/314)
### Changed
- Use github actions for codecov. [#312](https://github.com/ikalchev/HAP-python/pull/312), [#313](https://github.com/ikalchev/HAP-python/pull/313)

## [3.2.0] - 2021-01-31

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![PyPI version](https://badge.fury.io/py/HAP-python.svg)](https://badge.fury.io/py/HAP-python) [![Build Status](https://travis-ci.org/ikalchev/HAP-python.svg?branch=master)](https://travis-ci.org/ikalchev/HAP-python) [![codecov](https://codecov.io/gh/ikalchev/HAP-python/branch/master/graph/badge.svg)](https://codecov.io/gh/ikalchev/HAP-python) [![Documentation Status](https://readthedocs.org/projects/hap-python/badge/?version=latest)](http://hap-python.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://pepy.tech/badge/hap-python)](https://pepy.tech/project/hap-python)
[![PyPI version](https://badge.fury.io/py/HAP-python.svg)](https://badge.fury.io/py/HAP-python) [![Build Status](https://github.com/ikalchev/HAP-python/workflows/CI/badge.svg)](https://github.com/ikalchev/HAP-python) [![codecov](https://codecov.io/gh/ikalchev/HAP-python/branch/master/graph/badge.svg)](https://codecov.io/gh/ikalchev/HAP-python) [![Documentation Status](https://readthedocs.org/projects/hap-python/badge/?version=latest)](http://hap-python.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://pepy.tech/badge/hap-python)](https://pepy.tech/project/hap-python)
# HAP-python

HomeKit Accessory Protocol implementation in python 3.
Expand Down
56 changes: 43 additions & 13 deletions pyhap/accessory_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
from concurrent.futures import ThreadPoolExecutor
import functools
import hashlib
import tempfile
import json
import logging
import os
import socket
import sys
import threading
import re

from zeroconf import ServiceInfo, Zeroconf

Expand All @@ -48,6 +50,7 @@
from pyhap.loader import Loader
from pyhap.params import get_srp_context
from pyhap.state import State
from .util import callback

logger = logging.getLogger(__name__)

Expand All @@ -56,6 +59,7 @@
SERVICE_CALLBACK = 0
SERVICE_CALLBACK_DATA = 1
HAP_SERVICE_TYPE = "_hap._tcp.local."
VALID_MDNS_REGEX = re.compile(r"[^A-Za-z0-9\-]+")


def is_callback(func):
Expand All @@ -82,7 +86,7 @@ def __init__(self, accessory, state):
adv_data = self._get_advert_data()
# Append part of MAC address to prevent name conflicts
name = "{} {}.{}".format(
self.accessory.display_name,
self._valid_name(),
self.state.mac[-8:].replace(":", ""),
HAP_SERVICE_TYPE,
)
Expand All @@ -96,6 +100,9 @@ def __init__(self, accessory, state):
addresses=[socket.inet_aton(self.state.address)],
)

def _valid_name(self):
return re.sub(VALID_MDNS_REGEX, " ", self.accessory.display_name).strip()

def _setup_hash(self):
setup_hash_material = self.state.setup_id + self.state.mac
temp_hash = hashlib.sha512()
Expand All @@ -105,7 +112,7 @@ def _setup_hash(self):
def _get_advert_data(self):
"""Generate advertisement data from the accessory."""
return {
"md": self.accessory.display_name,
"md": self._valid_name(),
"pv": "1.0",
"id": self.state.mac,
# represents the 'configuration version' of an Accessory.
Expand Down Expand Up @@ -305,9 +312,11 @@ def start_service(self):
self.accessory.setup_message()

# Start the accessory so it can do stuff.
logger.debug("Starting accessory.")
logger.debug("Starting accessory %s", self.accessory.display_name)
self.add_job(self.accessory.run)
logger.debug("AccessoryDriver started successfully")
logger.debug(
"AccessoryDriver for %s started successfully", self.accessory.display_name
)

def stop(self):
"""Method to stop pyhap."""
Expand All @@ -330,7 +339,9 @@ async def async_stop(self):
)
await self.async_add_job(self.accessory.stop)

logger.debug("AccessoryDriver stopped successfully")
logger.debug(
"AccessoryDriver for %s stopped successfully", self.accessory.display_name
)

# Executor=None means a loop wasn't passed in
if self.executor is not None:
Expand All @@ -345,7 +356,7 @@ def _do_stop(self):
logger.debug("Setting stop events, stopping accessory")
self.stop_event.set()

logger.debug("Stopping mDNS advertising")
logger.debug("Stopping mDNS advertising for %s", self.accessory.display_name)
self.advertiser.unregister_service(self.mdns_service_info)
self.advertiser.close()

Expand Down Expand Up @@ -491,16 +502,36 @@ def config_changed(self):

def update_advertisement(self):
"""Updates the mDNS service info for the accessory."""
logger.debug("Updating mDNS advertisement")
self.mdns_service_info = AccessoryMDNSServiceInfo(self.accessory, self.state)
self.advertiser.update_service(self.mdns_service_info)

@callback
def async_persist(self):
"""Saves the state of the accessory.
Must be run in the event loop.
"""
loop = asyncio.get_event_loop()
asyncio.ensure_future(loop.run_in_executor(None, self.persist))

def persist(self):
"""Saves the state of the accessory.
Must run in executor.
"""
with open(self.persist_file, "w") as file_handle:
self.encoder.persist(file_handle, self.state)
tmp_filename = None
try:
temp_dir = os.path.dirname(self.persist_file)
with tempfile.NamedTemporaryFile(
mode="w", dir=temp_dir, delete=False
) as file_handle:
tmp_filename = file_handle.name
self.encoder.persist(file_handle, self.state)
os.replace(tmp_filename, self.persist_file)
finally:
if tmp_filename and os.path.exists(tmp_filename):
os.remove(tmp_filename)

def load(self):
"""Load the persist file.
Expand All @@ -510,6 +541,7 @@ def load(self):
with open(self.persist_file, "r") as file_handle:
self.encoder.load_into(file_handle, self.state)

@callback
def pair(self, client_uuid, client_public):
"""Called when a client has paired with the accessory.
Expand All @@ -524,14 +556,12 @@ def pair(self, client_uuid, client_public):
:return: Whether the pairing is successful.
:rtype: bool
"""
# TODO: Adding a client is a change in the acc. configuration. Then, should we
# let the accessory call config_changed, which will persist and update mDNS?
# See also unpair.
logger.info("Paired with %s.", client_uuid)
self.state.add_paired_client(client_uuid, client_public)
self.persist()
self.async_persist()
return True

@callback
def unpair(self, client_uuid):
"""Removes the paired client from the accessory.
Expand All @@ -542,7 +572,7 @@ def unpair(self, client_uuid):
"""
logger.info("Unpairing client %s.", client_uuid)
self.state.remove_paired_client(client_uuid)
self.persist()
self.async_persist()

def finish_pair(self):
"""Finishing pairing or unpairing.
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 = 3
MINOR_VERSION = 2
MINOR_VERSION = 3
PATCH_VERSION = 0
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
Expand Down
Loading

0 comments on commit ec0a350

Please sign in to comment.