Color mode implementation (#74)

Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
Oxan van Leeuwen 2021-07-29 19:16:25 +02:00 committed by GitHub
parent f90ffcf3b8
commit 32d2df8e24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1279 additions and 848 deletions

View File

@ -353,6 +353,18 @@ message FanCommandRequest {
}
// ==================== LIGHT ====================
enum ColorMode {
COLOR_MODE_UNKNOWN = 0;
COLOR_MODE_ON_OFF = 1;
COLOR_MODE_BRIGHTNESS = 2;
COLOR_MODE_WHITE = 7;
COLOR_MODE_COLOR_TEMPERATURE = 11;
COLOR_MODE_COLD_WARM_WHITE = 19;
COLOR_MODE_RGB = 35;
COLOR_MODE_RGB_WHITE = 39;
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
}
message ListEntitiesLightResponse {
option (id) = 15;
option (source) = SOURCE_SERVER;
@ -363,10 +375,12 @@ message ListEntitiesLightResponse {
string name = 3;
string unique_id = 4;
bool supports_brightness = 5;
bool supports_rgb = 6;
bool supports_white_value = 7;
bool supports_color_temperature = 8;
repeated ColorMode supported_color_modes = 12;
// next four supports_* are for legacy clients, newer clients should use color modes
bool legacy_supports_brightness = 5 [deprecated=true];
bool legacy_supports_rgb = 6 [deprecated=true];
bool legacy_supports_white_value = 7 [deprecated=true];
bool legacy_supports_color_temperature = 8 [deprecated=true];
float min_mireds = 9;
float max_mireds = 10;
repeated string effects = 11;
@ -380,11 +394,15 @@ message LightStateResponse {
fixed32 key = 1;
bool state = 2;
float brightness = 3;
ColorMode color_mode = 11;
float color_brightness = 10;
float red = 4;
float green = 5;
float blue = 6;
float white = 7;
float color_temperature = 8;
float cold_white = 12;
float warm_white = 13;
string effect = 9;
}
message LightCommandRequest {
@ -398,6 +416,10 @@ message LightCommandRequest {
bool state = 3;
bool has_brightness = 4;
float brightness = 5;
bool has_color_mode = 22;
ColorMode color_mode = 23;
bool has_color_brightness = 20;
float color_brightness = 21;
bool has_rgb = 6;
float red = 7;
float green = 8;
@ -406,6 +428,10 @@ message LightCommandRequest {
float white = 11;
bool has_color_temperature = 12;
float color_temperature = 13;
bool has_cold_white = 24;
float cold_white = 25;
bool has_warm_white = 26;
float warm_white = 27;
bool has_transition_length = 14;
uint32 transition_length = 15;
bool has_flash_length = 16;

View File

@ -1,9 +1,8 @@
# type: ignore
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: api_options.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
@ -22,7 +21,8 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='',
syntax='proto2',
serialized_options=None,
serialized_pb=_b('\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto\"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse')
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto\"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse'
,
dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
@ -31,19 +31,23 @@ _APISOURCETYPE = _descriptor.EnumDescriptor(
full_name='APISourceType',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SOURCE_BOTH', index=0, number=0,
serialized_options=None,
type=None),
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SOURCE_SERVER', index=1, number=1,
serialized_options=None,
type=None),
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SOURCE_CLIENT', index=2, number=2,
serialized_options=None,
type=None),
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
@ -64,7 +68,7 @@ needs_setup_connection = _descriptor.FieldDescriptor(
has_default_value=True, default_value=True,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
NEEDS_AUTHENTICATION_FIELD_NUMBER = 1039
needs_authentication = _descriptor.FieldDescriptor(
name='needs_authentication', full_name='needs_authentication', index=1,
@ -72,7 +76,7 @@ needs_authentication = _descriptor.FieldDescriptor(
has_default_value=True, default_value=True,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
ID_FIELD_NUMBER = 1036
id = _descriptor.FieldDescriptor(
name='id', full_name='id', index=2,
@ -80,7 +84,7 @@ id = _descriptor.FieldDescriptor(
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
SOURCE_FIELD_NUMBER = 1037
source = _descriptor.FieldDescriptor(
name='source', full_name='source', index=3,
@ -88,15 +92,15 @@ source = _descriptor.FieldDescriptor(
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
IFDEF_FIELD_NUMBER = 1038
ifdef = _descriptor.FieldDescriptor(
name='ifdef', full_name='ifdef', index=4,
number=1038, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
LOG_FIELD_NUMBER = 1039
log = _descriptor.FieldDescriptor(
name='log', full_name='log', index=5,
@ -104,7 +108,7 @@ log = _descriptor.FieldDescriptor(
has_default_value=True, default_value=True,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
NO_DELAY_FIELD_NUMBER = 1040
no_delay = _descriptor.FieldDescriptor(
name='no_delay', full_name='no_delay', index=6,
@ -112,7 +116,7 @@ no_delay = _descriptor.FieldDescriptor(
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
serialized_options=None, file=DESCRIPTOR)
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key)
_VOID = _descriptor.Descriptor(
@ -121,6 +125,7 @@ _VOID = _descriptor.Descriptor(
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
],
extensions=[
@ -149,11 +154,11 @@ DESCRIPTOR.extensions_by_name['log'] = log
DESCRIPTOR.extensions_by_name['no_delay'] = no_delay
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
void = _reflection.GeneratedProtocolMessageType('void', (_message.Message,), dict(
DESCRIPTOR = _VOID,
__module__ = 'api_options_pb2'
void = _reflection.GeneratedProtocolMessageType('void', (_message.Message,), {
'DESCRIPTOR' : _VOID,
'__module__' : 'api_options_pb2'
# @@protoc_insertion_point(class_scope:void)
))
})
_sym_db.RegisterMessage(void)
google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(needs_setup_connection)

File diff suppressed because one or more lines are too long

View File

@ -88,6 +88,7 @@ from .model import (
FanState,
HomeassistantServiceCall,
LegacyCoverCommand,
LightColorMode,
LightInfo,
LightState,
LogLevel,
@ -430,9 +431,13 @@ class APIClient:
key: int,
state: Optional[bool] = None,
brightness: Optional[float] = None,
color_mode: Optional[LightColorMode] = None,
color_brightness: Optional[float] = None,
rgb: Optional[Tuple[float, float, float]] = None,
white: Optional[float] = None,
color_temperature: Optional[float] = None,
cold_white: Optional[float] = None,
warm_white: Optional[float] = None,
transition_length: Optional[float] = None,
flash_length: Optional[float] = None,
effect: Optional[str] = None,
@ -447,6 +452,12 @@ class APIClient:
if brightness is not None:
req.has_brightness = True
req.brightness = brightness
if color_mode is not None:
req.has_color_mode = True
req.color_mode = color_mode
if color_brightness is not None:
req.has_color_brightness = True
req.color_brightness = color_brightness
if rgb is not None:
req.has_rgb = True
req.red = rgb[0]
@ -458,6 +469,12 @@ class APIClient:
if color_temperature is not None:
req.has_color_temperature = True
req.color_temperature = color_temperature
if cold_white is not None:
req.has_cold_white = True
req.cold_white = cold_white
if warm_white is not None:
req.has_warm_white = True
req.warm_white = warm_white
if transition_length is not None:
req.has_transition_length = True
req.transition_length = int(round(transition_length * 1000))

View File

@ -207,26 +207,75 @@ class FanState(EntityState):
# ==================== LIGHT ====================
class LightColorMode(APIIntEnum):
UNKNOWN = 0
ON_OFF = 1
BRIGHTNESS = 2
WHITE = 7
COLOR_TEMPERATURE = 11
COLD_WARM_WHITE = 19
RGB = 35
RGB_WHITE = 39
RGB_COLOR_TEMPERATURE = 47
RGB_COLD_WARM_WHITE = 51
@dataclass(frozen=True)
class LightInfo(EntityInfo):
supports_brightness: bool = False
supports_rgb: bool = False
supports_white_value: bool = False
supports_color_temperature: bool = False
supported_color_modes: List[LightColorMode] = converter_field(
default_factory=list, converter=LightColorMode.convert_list
)
min_mireds: float = 0.0
max_mireds: float = 0.0
effects: List[str] = converter_field(default_factory=list, converter=list)
# deprecated, do not use
legacy_supports_brightness: bool = False
legacy_supports_rgb: bool = False
legacy_supports_white_value: bool = False
legacy_supports_color_temperature: bool = False
def supported_color_modes_compat(
self, api_version: APIVersion
) -> List[LightColorMode]:
if api_version < APIVersion(1, 6):
key = (
self.legacy_supports_brightness,
self.legacy_supports_rgb,
self.legacy_supports_white_value,
self.legacy_supports_color_temperature,
)
# map legacy flags to color modes,
# key: (brightness, rgb, white, color_temp)
modes_map = {
(False, False, False, False): [LightColorMode.ON_OFF],
(True, False, False, False): [LightColorMode.BRIGHTNESS],
(True, False, False, True): [LightColorMode.COLOR_TEMPERATURE],
(True, True, False, False): [LightColorMode.RGB],
(True, True, True, False): [LightColorMode.RGB_WHITE],
(True, True, True, True): [LightColorMode.RGB_COLOR_TEMPERATURE],
}
return modes_map[key] if key in modes_map else []
return self.supported_color_modes
@dataclass(frozen=True)
class LightState(EntityState):
state: bool = False
brightness: float = 0.0
color_mode: Optional[LightColorMode] = converter_field(
default=LightColorMode.UNKNOWN, converter=LightColorMode.convert
)
color_brightness: float = 0.0
red: float = 0.0
green: float = 0.0
blue: float = 0.0
white: float = 0.0
color_temperature: float = 0.0
cold_white: float = 0.0
warm_white: float = 0.0
effect: str = ""