Skip to content

Commit

Permalink
Add tests + CI validation (#37)
Browse files Browse the repository at this point in the history
* Add tests + CI validation

* Fix tests
  • Loading branch information
ericmatte authored Feb 23, 2021
1 parent f09e27b commit 3851157
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 22 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Tests

on:
push:

jobs:
validate:
name: Validate HACS App
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: "actions/checkout@v2"

- name: HACS validation
uses: "hacs/action@main"
with:
category: "appdaemon"
comment: "false"

tests:
name: Tests & Linting
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: "actions/checkout@v2"

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.9"

- name: Install dependencies
run: pip install -r requirements.txt

- name: Run pytest
run: pytest

- name: Run linting with autopep8
if: always()
run: autopep8 --diff --recursive --exit-code .
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
3 changes: 3 additions & 0 deletions .pep8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pycodestyle]
max_line_length = 160
aggressive = 1
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"editor.formatOnSave": true,
"python.formatting.provider": "autopep8",
"python.formatting.autopep8Args": ["--max-line-length=120"]
"python.formatting.provider": "autopep8"
}
9 changes: 7 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ The app shall remain simple with that focused task in mind.
## Local development

1. Clone this repository.
1. Copy the `ad-media-lights-sync` folder into `appdaemon/apps/` on your Home Assistant instance.
1. Run `pip3 install -r requirements.txt`.
1. Edit code, add tests and run `pytest`.

### Testing on a Home Assistant instance

1. Copy the [`media_lights_sync`](./apps) folder into `appdaemon/apps/` on your Home Assistant instance.
1. Update `appdaemon/apps/apps.yaml` with the proper configuration for your setup (see [`info.md`](./info.md)).

## Pull Request Process

1. Ensure that the proposed changes do not break the current functionalities ot the app.
1. Update the `README.md` and `indo.md` with details of changes to the configuration or setup.
1. Update the `README.md` with details of changes to the configuration or setup.
1. If breaking changes are needed, make sure to specify exactly what needs to change.

For new version, the versioning scheme we use is [SemVer](http://semver.org/).
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# Media Player Lights Sync

[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs) [![](https://img.shields.io/github/release/ericmatte/ad-media-lights-sync/all.svg?style=for-the-badge)](https://github.com/ericmatte/ad-media-lights-sync/releases)

<a href="https://www.buymeacoffee.com/ericmatte" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>

_AppDaemon App that synchronizes the color of RGB lights with the thumbnail of a media player in Home Assistant._

---
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs) [![](https://img.shields.io/github/release/ericmatte/ad-media-lights-sync/all.svg?style=for-the-badge)](https://github.com/ericmatte/ad-media-lights-sync/releases) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/ericmatte/ad-media-lights-sync/Tests?style=for-the-badge)](https://github.com/ericmatte/ad-media-lights-sync/actions)

<a href="https://www.buymeacoffee.com/ericmatte" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>

<img src="https://github.com/ericmatte/ad-media-lights-sync/raw/master/examples/example-1.jpg" alt="Example 1" width="400"> <img src="https://github.com/ericmatte/ad-media-lights-sync/raw/master/examples/example-2.jpg" alt="Example 1" width="400">

Expand Down
29 changes: 16 additions & 13 deletions apps/media_lights_sync/media_lights_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@

from threading import Thread
from PIL import Image, features
from urllib.parse import urljoin
from urllib.parse import urljoin, urlparse
from urllib.request import urlopen
from urllib.error import HTTPError
from urllib.error import HTTPError, URLError

PICTURE_ATTRIBUTES = ["entity_picture_local", "entity_picture"]


class MediaLightsSync(hass.Hass):
"""MediaLightsSync class."""

Expand Down Expand Up @@ -43,18 +44,18 @@ def change_lights_color(self, entity, attribute, old_url, new_url, kwargs):

if new_url is not None:
self.store_initial_lights_states()
log_message = "New picture received from '{entity}' ({attr})\n"
current_pictures = [self.get_state(entity, attr) for attr in PICTURE_ATTRIBUTES]
log_message = "New picture received from '{entity}' ({attribute})\n"
current_pictures = [self.get_state(entity, attribute=attribute) for attribute in PICTURE_ATTRIBUTES]

if self.media_player_callbacks.get(entity, None) == current_pictures:
# Image already processed from another callback
return self.log(log_message.format(entity=entity, attr=attribute+"; skipped"))
self.log(log_message.format(entity=entity, attr=attribute))
return self.log(log_message.format(entity=entity, attribute=attribute + "; skipped"))
self.log(log_message.format(entity=entity, attribute=attribute))

try:
url = self.format_url(new_url, entity, attribute)
rgb_colors = self.get_colors(url)
except HTTPError as error:
except (HTTPError, URLError) as error:
self.error("Unable to fetch artwork: {error}\nURL: {url}\n".format(url=url, error=error))
return

Expand All @@ -81,7 +82,8 @@ def reset_lights(self):
for i in range(len(self.lights)):
state = self.initial_lights_states[i]["state"]
attributes = self.initial_lights_states[i]["attributes"]
self.set_light(state.lower(), self.lights[i], color=attributes.get("rgb_color", None), brightness=attributes.get("brightness", None), transition=self.transition)
self.set_light(state.lower(), self.lights[i], color=attributes.get("rgb_color", None),
brightness=attributes.get("brightness", None), transition=self.transition)
self.initial_lights_states = None
self.media_player_callbacks = {}

Expand All @@ -98,7 +100,7 @@ def set_light(self, new_state, entity, color=None, brightness=None, transition=N
attributes["rgb_color"] = color
if brightness is not None:
attributes["brightness"] = brightness
self.log("Set '{entity}' light:\n{attr}".format(entity=entity, attr=attributes))
self.log("Set '{entity}' light:\n{attributes}".format(entity=entity, attributes=attributes))
Thread(target=self.turn_on, args=[entity], kwargs=attributes).start()

def get_saturated_color(self, color):
Expand Down Expand Up @@ -146,10 +148,11 @@ def extract_colors(self, palette, colors):

def format_url(self, url, entity, attribute):
"""Append ha_url if this is a relative url"""
is_relative = not url.startswith("http")
if not is_relative:
is_absolute = bool(urlparse(url).netloc) or url.startswith("file:///")
if is_absolute:
return url
elif is_relative and self.ha_url is None:
raise ValueError("A relative URL was received on '{entity}.{attribute}'.\nha_url must be specified in the configuration for relative URLs.".format(entity=entity, attribute=attribute))
elif not is_absolute and self.ha_url is None:
raise ValueError("A relative URL was received on '{entity}.{attribute}'.\nha_url must be specified in the configuration for relative URLs.".format(
entity=entity, attribute=attribute))
else:
return urljoin(self.ha_url, url)
1 change: 1 addition & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from appdaemontestframework.pytest_conftest import *
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
appdaemontestframework
autopep8
pytest
Pillow
Loading

0 comments on commit 3851157

Please sign in to comment.