more rich work

This commit is contained in:
John Lancaster
2024-03-10 15:44:42 -05:00
parent b8e5a65347
commit e90ad5a071
3 changed files with 78 additions and 46 deletions

View File

@@ -4,26 +4,42 @@ import re
from appdaemon.adapi import ADAPI
from appdaemon.logging import AppNameFormatter
from rich.console import Console
from rich.highlighter import NullHighlighter, RegexHighlighter
from rich.highlighter import RegexHighlighter
from rich.logging import RichHandler
from rich.theme import Theme
class RCHighlighter(RegexHighlighter):
highlights = [
r'(?P<light>(light|switch)\.\w+)',
r'(?P<time>\d+:\d+:\d+)',
r'(?P<z2m>zigbee2mqtt/)',
r'(?P<sensor>binary_sensor\.\w+)',
# r"'state': '(?P<on>on)|(?P<off>off)'"
r'(?P<true>True)|(?P<false>False)',
]
console = Console(
width=150,
theme=Theme({
theme=Theme(
{
'log.time': 'none',
'logging.level.info': 'none',
'room': 'italic bright_cyan',
'component': 'dark_violet',
'entity_id': 'light_slate_blue',
'friendly_name': 'yellow',
'light': 'light_slate_blue',
'sensor': 'green',
'time': 'yellow',
'z2m': 'bright_black',
'topic': 'chartreuse2',
}),
'true': 'green',
'false': 'red',
}
),
log_time_format='%Y-%m-%d %I:%M:%S %p',
highlighter=RCHighlighter(),
)
@@ -61,14 +77,6 @@ class RoomControllerFormatter(logging.Formatter):
return super().format(record)
class RCHighlighter(RegexHighlighter):
highlights = [
r"(?P<entity_id>(light|switch)\.\w+)",
r'(?P<time>\d+:\d+:\d+)',
r'(?P<z2m>zigbee2mqtt/)'
]
def new_handler() -> RichHandler:
return RichHandler(
console=console,
@@ -93,16 +101,14 @@ def setup_component_logging(self):
self.logger = logger.getChild(typ)
if len(self.logger.handlers) == 0:
self.logger.setLevel(logging.INFO)
self.logger.addHandler(setup_handler(room=self.args["app"], component=typ))
self.logger.addHandler(setup_handler(room=self.args['app'], component=typ))
self.logger.propagate = False
def init_logging(self: ADAPI, level):
for h in logging.getLogger('AppDaemon').handlers:
og_formatter = h.formatter
h.setFormatter(
UnMarkupFormatter(fmt=og_formatter._fmt, datefmt=og_formatter.datefmt, style='{')
)
h.setFormatter(UnMarkupFormatter(fmt=og_formatter._fmt, datefmt=og_formatter.datefmt, style='{'))
if not any(isinstance(h, RichHandler) for h in self.logger.handlers):
self.logger.propagate = False

View File

@@ -30,6 +30,9 @@ class Motion(Hass):
self.app: RoomController = self.get_app(self.args['app'])
self.log(f'Connected to AD app [room]{self.app.name}[/]')
assert self.entity_exists(self.args['sensor'])
assert self.entity_exists(self.args['ref_entity'])
base_kwargs = dict(
entity_id=self.ref_entity.entity_id,
immediate=True, # avoids needing to sync the state
@@ -48,7 +51,9 @@ class Motion(Hass):
oneshot=True,
cause='motion on',
)
self.log(f'Waiting for motion on {self.sensor.friendly_name}')
self.log(f'Waiting for motion on [friendly_name]{self.sensor.friendly_name}[/]')
if self.sensor_state:
self.log(f'{self.sensor.friendly_name} is already on', level='WARNING')
def listen_motion_off(self, duration: timedelta):
"""Sets up the motion off callback to deactivate the room"""
@@ -61,18 +66,21 @@ class Motion(Hass):
oneshot=True,
cause='motion off',
)
self.log(f'Waiting for motion to stop on {self.sensor.friendly_name} for {duration}')
self.log(f'Waiting for motion to stop on [friendly_name]{self.sensor.friendly_name}[/] for {duration}')
if not self.sensor_state:
self.log(f'{self.sensor.friendly_name} is currently off', level='WARNING')
def callback_light_on(self, entity=None, attribute=None, old=None, new=None, kwargs=None):
"""Called when the light turns on"""
if new is not None:
self.log(f'{entity} turned on')
self.log(f'Detected {entity} turning on', level='DEBUG')
duration = self.app.off_duration()
self.listen_motion_off(duration)
def callback_light_off(self, entity=None, attribute=None, old=None, new=None, kwargs=None):
"""Called when the light turns off"""
self.log(f'{entity} turned off')
self.log(f'Detected {entity} turning off', level='DEBUG')
self.listen_motion_on()
def get_app_callbacks(self, name: str = None):
@@ -100,4 +108,4 @@ class Motion(Hass):
if (m := re.match('new=(?P<new>.*?)\s', kwargs)) is not None:
new = m.group('new')
self.cancel_listen_state(handle)
self.log(f'cancelled callback for sensor {entity} turning {new}')
self.log(f'cancelled callback for sensor {entity} turning {new}', level='DEBUG')

View File

@@ -10,8 +10,9 @@ from appdaemon.entity import Entity
from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt
from astral import SunDirection
from console import setup_handler
from rich.table import Table
from console import console, setup_handler
from rich.console import Console, ConsoleOptions, RenderResult, Group
from rich.table import Table, Column
def str_to_timedelta(input_str: str) -> datetime.timedelta:
@@ -63,6 +64,12 @@ class RoomState:
def from_json(cls, json_input):
return cls(**json_input)
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
table = Table('Entity ID', 'State')
for name, state in self.scene.items():
table.add_row(name, str(state))
yield table
@dataclass
class RoomConfig:
@@ -90,6 +97,17 @@ class RoomConfig:
cfg: Dict = yaml.load(f, Loader=yaml.SafeLoader)[app_name]
return cls.from_app_config(cfg)
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
table = Table(
Column('Time', width=15),
Column('Scene'),
highlight=True, padding=1, collapse_padding=True,
)
for state in self.states:
lines = [f'{name:20}{state["state"]} Brightness: {state["brightness"]:<4} Temp: {state["color_temp"]}' for name, state in state.scene.items()]
table.add_row(state.time.strftime('%I:%M:%S %p'), '\n'.join(lines))
yield table
def rich_table(self, app_name: str) -> Table:
table = Table(title=app_name, expand=True, highlight=True, padding=1, collapse_padding=True)
table.add_column('Time')
@@ -154,7 +172,7 @@ class RoomController(Hass, Mqtt):
def initialize(self):
self.logger = logger.getChild(self.name)
if not self.logger.hasHandlers():
self.logger.setLevel(logging.INFO)
self.logger.setLevel(self.args.get('rich', logging.INFO))
self.logger.addHandler(setup_handler(room=self.name))
# console.log(f'[yellow]Added RichHandler to {self.logger.name}[/]')
@@ -209,9 +227,9 @@ class RoomController(Hass, Mqtt):
assert isinstance(state.time, datetime.time), f'Invalid time: {state.time}'
# if self.rich_logging:
if self.logger.isEnabledFor(logging.DEBUG):
# table = self._room_config.rich_table(self.name)
# console.log(table, highlight=False)
console.print(self._room_config)
self.states = sorted(self.states, key=lambda s: s.time, reverse=True)
@@ -245,12 +263,10 @@ class RoomController(Hass, Mqtt):
def current_scene(self, now: datetime.time = None) -> Dict[str, Dict[str, str | int]]:
state = self.current_state(now)
# print(f'{type(state).__name__}')
# assert isinstance(state, RoomState), f'Invalid state: {type(state).__name__}'
assert type(state).__name__ == 'RoomState' # needed for the reloading to work
# self.log(f'Current scene: {state}')
assert type(state).__name__ == 'RoomState' # needed this way instead of isinstance(...) for the reloading to work
if self.logger.isEnabledFor(logging.DEBUG):
self.log('Current scene:')
self.log(state)
console.print(state)
return state.scene
def app_entity_states(self) -> Dict[str, str]:
@@ -329,7 +345,9 @@ class RoomController(Hass, Mqtt):
scene[entity]['state'] = 'on'
self.call_service('scene/apply', entities=scene, transition=0)
self.log(f'Applied scene: {scene}')
if self.logger.isEnabledFor(logging.INFO):
self.log('Applied scene:')
console.print(scene)
elif scene is None:
self.log('No scene, ignoring...')