Skip to content

Commit

Permalink
Add quantization_method option (#36)
Browse files Browse the repository at this point in the history
* Add quantization_method option

* Fix ref

* Update doc
  • Loading branch information
ericmatte authored Feb 21, 2021
1 parent 5b3e71f commit 56edb33
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 17 deletions.
41 changes: 27 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ RUN pip3 install Pillow
## App configuration

`config/appdaemon/apps/apps.yaml`

```yaml
media_lights_sync:
module: media_lights_sync
Expand All @@ -64,23 +65,35 @@ media_lights_sync:
state: "on"
```
| key | optional | type | default | description |
| ------------------------ | -------- | -------------- | ------------------- | ---------------------------------------------------------------------------------------------------------- |
| `module` | False | string | `media_lights_sync` | The module name of the app. |
| `class` | False | string | `MediaLightsSync` | The name of the Class. |
| `media_player` | False | string or list | | The entity_id(s) of the media player(s) to sync from<sup id="ha-url">[1](#ha-url-note)</sup>. |
| `lights` | False | list | | The list of all the lights entity_id to sync to. |
| `ha_url` | True | string | `null` | The URL to your Home Assistant. Examples: `https://my-ha.duckdns.org`, `http://192.168.1.123:8123`. |
| `reset_lights_after` | True | bool | `false` | Reset lights to their initial state after turning off a `medial_player`. Will not reset lights if `false`. |
| `use_saturated_colors` | True | bool | `false` | Increase the saturation and brightness of the colors. |
| `use_current_brightness` | True | bool | `false` | Do not change lights brightness. If `false`, it will always sets all lights to maximum brightness. |
| `transition` | True | number | `null` | Number that represents the time (in seconds) the light should take to transition to new states. |
| `condition` | True | object | | Sync lights only if the state of the condition entity is valid. |
| `condition.entity` | False | string | | The entity_id of the condition. |
| `condition.state` | False | string | | The state to match in order for the lights to sync. |
| key | optional | type | default | description |
| ------------------------ | -------- | -------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `module` | False | string | `media_lights_sync` | The module name of the app. |
| `class` | False | string | `MediaLightsSync` | The name of the Class. |
| `media_player` | False | string or list | | The entity_id(s) of the media player(s) to sync from<sup id="ha-url">[1](#ha-url-note)</sup>. |
| `lights` | False | list | | The list of all the lights entity_id to sync to. |
| `ha_url` | True | string | `null` | The URL to your Home Assistant. Examples: `https://my-ha.duckdns.org`, `http://192.168.1.123:8123`. |
| `reset_lights_after` | True | bool | `false` | Reset lights to their initial state after turning off a `medial_player`. Will not reset lights if `false`. |
| `quantization_method` | True | string | `MedianCut` | Supports `MedianCut`, `FastOctree`, `MaxCoverage` and `libimagequant`. More info [below](#selecting-a-quantization_method). |
| `use_saturated_colors` | True | bool | `false` | Increase the saturation and brightness of the colors. |
| `use_current_brightness` | True | bool | `false` | Do not change lights brightness. If `false`, it will always sets all lights to maximum brightness. |
| `transition` | True | number | `null` | Number that represents the time (in seconds) the light should take to transition to new states. |
| `condition` | True | object | | Sync lights only if the state of the condition entity is valid. |
| `condition.entity` | False | string | | The entity_id of the condition. |
| `condition.state` | False | string | | The state to match in order for the lights to sync. |

<b id="ha-url-note">[1](#ha-url)</b>: See `/developer-tools/state` in your Home Assistant instance. This app will listen to changes on `entity_picture_local` and/or `entity_picture` attributes of your `media_player` entities.

## Selecting a `quantization_method`

There is four [quantization method](https://pillow.readthedocs.io/en/stable/reference/Image.html?highlight=getpalette#quantization-methods) available, which change the way the colors palette is extracted:

- `MedianCut`: Default method. Mix colors in the image using their median value.
- `FastOctree`: Extract dominant colors. Use this option if you want more accurate colors.
- `MaxCoverage`: Mix colors based on their maximum coverage.
- `libimagequant`: High-quality conversion of RGBA images to 8-bit indexed-color (palette) images.

Alternatively, you can also combine this option with `use_saturated_colors` to get more vibrant colors.

## Compatibility

This app should work with any `media_player` and RGB light integrations available in Home Assitant.
Expand Down
22 changes: 19 additions & 3 deletions apps/media_lights_sync/media_lights_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import colorsys

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

PICTURE_ATTRIBUTES = ["entity_picture_local", "entity_picture"]


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

Expand All @@ -26,6 +25,7 @@ def initialize(self):
self.reset_lights_after = args.get("reset_lights_after", False)
self.use_saturated_colors = args.get("use_saturated_colors", False)
self.brightness = None if args.get("use_current_brightness", False) else 255
self.quantization_method = self.get_quantization_method(args.get("quantization_method", None))

self.media_player_callbacks = {}
self.initial_lights_states = None
Expand Down Expand Up @@ -112,8 +112,24 @@ def get_colors(self, url):
fd = urlopen(url)
f = io.BytesIO(fd.read())
im = Image.open(f)
palette = im.quantize(colors=len(self.lights)).getpalette()
palette = im.quantize(colors=len(self.lights), method=self.quantization_method).getpalette()
return self.extract_colors(palette, len(self.lights))

def get_quantization_method(self, value):
method = None
if value == "FastOctree":
method = Image.FASTOCTREE
elif value == "MedianCut":
method = Image.MEDIANCUT
elif value == "MaxCoverage":
method = Image.MAXCOVERAGE
elif value == "libimagequant":
if features.check_feature(feature="libimagequant"):
method = Image.LIBIMAGEQUANT
else:
self.log("Quantization method 'libimagequant' is unsupported by your platform.")
self.log("Using {method} quantization method".format(method="default (MedianCut)" if method is None else value))
return Image.MEDIANCUT if method is None else method

def extract_colors(self, palette, colors):
"""Extract an amount of colors corresponding to the amount of lights in the configuration."""
Expand Down

0 comments on commit 56edb33

Please sign in to comment.