Rule creation for IR remotes
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Add only entities here that are auto-discovered (not FHEM and KNX devices)
|
||||
|
||||
default_view:
|
||||
view: true
|
||||
icon: mdi:home
|
||||
#default_view:
|
||||
# view: true
|
||||
# icon: mdi:home
|
||||
|
||||
living_area:
|
||||
name: Wohnbereich
|
||||
@@ -30,9 +30,9 @@ hallway:
|
||||
- light.gang_bogen
|
||||
- light.gang_einganglicht
|
||||
- light.gang_licht
|
||||
- switch.bewegungsmelder_west_led
|
||||
- switch.bewegungsmelder_ost_led
|
||||
- switch.bewegungsmelder_mitte_led
|
||||
- light.bewegungsmelder_west_led
|
||||
- light.bewegungsmelder_ost_led
|
||||
- light.bewegungsmelder_mitte_led
|
||||
|
||||
outside:
|
||||
name: Außen
|
||||
@@ -66,5 +66,7 @@ other:
|
||||
|
||||
first_floor:
|
||||
name: Oben
|
||||
entities:
|
||||
- light.wohnzimmer_stehlampe_oben
|
||||
|
||||
|
||||
|
||||
226
config_creation/ir_automations.py
Normal file
226
config_creation/ir_automations.py
Normal file
@@ -0,0 +1,226 @@
|
||||
import re
|
||||
import os
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
yaml = YAML()
|
||||
|
||||
|
||||
# -------------------------------------- put the config here -----------------------------------------------------------
|
||||
|
||||
def get_config():
|
||||
return {
|
||||
'bedroom': {
|
||||
'ir_host': 'bedroompi',
|
||||
'media_player': 'media_player.bedroompi',
|
||||
'group': 'group.bedroom',
|
||||
|
||||
'mapping': {
|
||||
'key_1': '[playlist] Good Morning',
|
||||
'key_2': '[playlist] Good Night Long',
|
||||
'key_3': '[playlist] Good Night',
|
||||
|
||||
'key_4': '[playlist] Bar Classics',
|
||||
'key_5': '[playlist] Sentimental Moods',
|
||||
'key_6': '[playlist] Pop',
|
||||
|
||||
'key_7': '[radio] B 5 aktuell',
|
||||
'key_8': '[radio] BR-Klassik',
|
||||
'key_9': '[playlist] http://opml.radiotime.com/Tune.ashx?id=s25028', # Klassik Radio
|
||||
|
||||
'key_numeric_star': '[radio] Antenne Bayern',
|
||||
'key_0': '[radio] Bayern 3',
|
||||
'key_numeric_pound': '[radio] Bayern 2',
|
||||
|
||||
'key_red': '[scene] schlafzimmer_orange',
|
||||
'key_green': '[scene] schlafzimmer_rot',
|
||||
'key_yellow': '[scene] schlafzimmer_ganz_hell',
|
||||
'key_blue': '[scene] schlafzimmer_blau',
|
||||
|
||||
'key_tv': '[timed_light_off] 30',
|
||||
'key_video': '[timed_light_off] 15',
|
||||
'key_music': '[timed_light_off] 10',
|
||||
'key_pictures': '[timed_light_off] 5',
|
||||
|
||||
'key_power': [ # Music & Lights off
|
||||
service('media_player.media_pause', 'media_player.bedroompi'),
|
||||
service('light.turn_off', 'group.bedroom'),
|
||||
],
|
||||
'key_ok': [ # Grosser Rollo zu, kleiner halb zu
|
||||
service('cover.close_cover', 'cover.schlafzimmer_rollo_gross'),
|
||||
service('cover_half.set_half', 'cover.schlafzimmer_rollo_klein'),
|
||||
],
|
||||
'key_mute': [service('light.turn_off', 'group.all_downstairs_but_bedroom_and_outside')],
|
||||
'key_channel': [service('light.turn_off', 'group.all_downstairs_but_bedroom')],
|
||||
}
|
||||
},
|
||||
'living_area': {
|
||||
'ir_host': 'kitchenpi',
|
||||
'media_player': 'media_player.kitchenpi',
|
||||
'group': 'group.living_area',
|
||||
|
||||
'mapping': {
|
||||
'key_4': '[playlist] Bar Classics',
|
||||
'key_5': '[playlist] Sentimental Moods',
|
||||
'key_6': '[playlist] Pop',
|
||||
|
||||
'key_7': '[radio] B 5 aktuell',
|
||||
'key_8': '[radio] BR-Klassik',
|
||||
'key_9': '[playlist] http://opml.radiotime.com/Tune.ashx?id=s25028', # Klassik Radio
|
||||
|
||||
'key_numeric_star': '[radio] Antenne Bayern',
|
||||
'key_0': '[radio] Bayern 3',
|
||||
'key_numeric_pound': '[radio] Bayern 2',
|
||||
|
||||
'key_red': '[scene] wohnbereich_orange',
|
||||
'key_green': '[scene] wohnbereich_grun',
|
||||
'key_yellow': '[scene] wohnbereich_hell',
|
||||
'key_blue': '[scene] wohnbereich_blau_grun',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
description_regex = re.compile(r'\[\s*(.*)\s*\](.*)')
|
||||
|
||||
|
||||
def split_description(d):
|
||||
res = description_regex.match(d)
|
||||
return res.group(1).strip(), res.group(2).strip()
|
||||
|
||||
|
||||
def automation_from_config(ir_description):
|
||||
ir_host = ir_description['ir_host']
|
||||
media_player = ir_description['media_player']
|
||||
group = ir_description['group']
|
||||
|
||||
result = []
|
||||
for key, description in ir_description['mapping'].items():
|
||||
automation = {'alias': f'IR {ir_host} {key}',
|
||||
'trigger': ir_trigger(ir_host, key)}
|
||||
if isinstance(description, list):
|
||||
action = description
|
||||
elif isinstance(description, str):
|
||||
function, value = split_description(description)
|
||||
if function == 'playlist':
|
||||
action = service('media_player.play_media', media_player, media_content_id=value)
|
||||
elif function == 'radio':
|
||||
action = service('media_player.play_media', media_player,
|
||||
media_content_id=value, media_content_type='channel')
|
||||
elif function == 'scene':
|
||||
action = service('scene.turn_on', 'scene.' + value)
|
||||
elif function == 'timed_light_off':
|
||||
action = service('light.turn_off', group, transition=str(60 * int(value)))
|
||||
else:
|
||||
raise ValueError("Invalid prefix " + function)
|
||||
else:
|
||||
raise ValueError("Invalid type for entry " + key)
|
||||
|
||||
automation['action'] = action
|
||||
result.append(automation)
|
||||
return result
|
||||
|
||||
|
||||
def ir_trigger(ir_host, button_name):
|
||||
return {
|
||||
'platform': 'event',
|
||||
'event_type': 'ir_command_received',
|
||||
'button_name': button_name,
|
||||
'repeat_counter': 0,
|
||||
'host': ir_host,
|
||||
}
|
||||
|
||||
|
||||
def service(service_name, entity_id, **kwargs):
|
||||
kwargs['entity_id'] = entity_id
|
||||
return {
|
||||
'service': service_name,
|
||||
'data': kwargs
|
||||
}
|
||||
|
||||
|
||||
def default_shutter_controls(device_group, ir_host):
|
||||
"""Default rules for Hauppauge IR for Shutter control with up, down, left, right buttons"""
|
||||
return [
|
||||
{
|
||||
'alias': f'IR {ir_host} Rollo auf',
|
||||
'trigger': ir_trigger(ir_host, 'key_up'),
|
||||
'action': service('cover.open_cover', device_group),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Rollo zu',
|
||||
'trigger': ir_trigger(ir_host, 'key_down'),
|
||||
'action': service('cover.close_cover', device_group),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Rollo halb',
|
||||
'trigger': [ir_trigger(ir_host, 'key_left'), ir_trigger(ir_host, 'key_right')],
|
||||
'action': service('cover_half.set_half', device_group),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def default_light_controls(device_group, ir_host):
|
||||
"""Default light rules for Hauppauge IR for light dimming with channel up/down and light off with stop button"""
|
||||
return [
|
||||
{
|
||||
'alias': f'IR {ir_host} Licht heller',
|
||||
'trigger': ir_trigger(ir_host, 'key_channelup'),
|
||||
'action': service('dimmer.dim', device_group, offset=30),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Licht dunkler',
|
||||
'trigger': ir_trigger(ir_host, 'key_channeldown'),
|
||||
'action': service('dimmer.dim', device_group, offset=-30),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Licht viel heller',
|
||||
'trigger': ir_trigger(ir_host, 'key_menu'),
|
||||
'action': service('dimmer.dim', device_group, offset=130),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Licht viel dunkler',
|
||||
'trigger': ir_trigger(ir_host, 'key_stop'),
|
||||
'action': service('dimmer.dim', device_group, offset=-130),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Licht aus',
|
||||
'trigger': ir_trigger(ir_host, 'key_goto'),
|
||||
'action': service('light.turn_off', device_group),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def default_music_controls(device_group, ir_host):
|
||||
"""Default music control (play, pause, next) for Hauppauge IR"""
|
||||
return [
|
||||
{
|
||||
'alias': f'IR {ir_host} Musik Play/Pause',
|
||||
'trigger': [ir_trigger(ir_host, 'key_play'), ir_trigger(ir_host, 'key_pause')],
|
||||
'action': service('media_player.media_play_pause', device_group),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Musik Next',
|
||||
'trigger': [ir_trigger(ir_host, 'key_forward'), ir_trigger(ir_host, 'key_fastforward')],
|
||||
'action': service('media_player.media_next_track', device_group),
|
||||
},
|
||||
{
|
||||
'alias': f'IR {ir_host} Musik Prev',
|
||||
'trigger': [ir_trigger(ir_host, 'key_previous'), ir_trigger(ir_host, 'key_rewind')],
|
||||
'action': service('media_player.media_next_track', device_group),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def create_rules(folder):
|
||||
for name, data in get_config().items():
|
||||
rules = automation_from_config(data)
|
||||
rules += default_light_controls(data['group'], data['ir_host'])
|
||||
rules += default_music_controls(data['media_player'], data['ir_host'])
|
||||
rules += default_shutter_controls(data['group'], data['ir_host'])
|
||||
file_name = os.path.join(folder, name + '.yaml')
|
||||
with open(file_name, 'w') as f:
|
||||
f.write("# Dont' edit manually! this is generated!\n\n")
|
||||
yaml.dump(rules, f)
|
||||
@@ -123,7 +123,9 @@ def create_lights(device_info: List[DeviceInfo],
|
||||
result = []
|
||||
for entry in device_info:
|
||||
try:
|
||||
on_off_write_addr = csv_contents[entry.csv_name + postfix_on_off_write]
|
||||
on_off_write_addr = csv_contents.get(entry.csv_name + postfix_on_off_write, None)
|
||||
if on_off_write_addr is None:
|
||||
on_off_write_addr = csv_contents[entry.csv_name]
|
||||
on_off_read_addr = csv_contents.get(entry.csv_name + postfix_on_off_read, None)
|
||||
brightness_write_addr = csv_contents.get(entry.csv_name + postfix_brightness_write, None)
|
||||
brightness_read_addr = csv_contents.get(entry.csv_name + postfix_brightness_read, None)
|
||||
|
||||
@@ -3,6 +3,7 @@ import argparse
|
||||
from util import DeviceInfo, add_to_group, name_to_id
|
||||
from ruamel.yaml import YAML
|
||||
import knx_conf as knx
|
||||
from ir_automations import create_rules as create_automation_rules
|
||||
|
||||
script_path = os.path.dirname(os.path.realpath(__file__))
|
||||
yaml = YAML()
|
||||
@@ -48,10 +49,14 @@ def add_knx_devices(devices, groups):
|
||||
DeviceInfo("LichtWaschküche", "Waschküche Licht", 'hallway'),
|
||||
# Normal lights
|
||||
DeviceInfo('AussenleuchteHaustüren', 'Haustür Licht', 'outside'),
|
||||
DeviceInfo('AussenleuchteObenNW', 'Haustür Licht NW', 'outside'),
|
||||
DeviceInfo('AussenleuchteObenNW', 'Haustür Licht NW', 'first_floor'),
|
||||
DeviceInfo('TreppenhausLicht', "Treppenhaus Licht", 'first_floor'),
|
||||
DeviceInfo('WCLicht', "WC Licht", 'other'),
|
||||
DeviceInfo('LampeVorratsraum', "Vorratsraum Licht", 'other'),
|
||||
# Bewegungsmelder LEDs
|
||||
DeviceInfo("BewegungsmelderMitte LED", "Bewegungsmelder Mitte LED", 'hallway'),
|
||||
DeviceInfo("BewegungsmelderWest LED", "Bewegungsmelder West LED", 'hallway'),
|
||||
DeviceInfo("BewegungsmelderOst LED", "Bewegungsmelder Ost LED", 'hallway'),
|
||||
]
|
||||
|
||||
shutters = [
|
||||
@@ -66,10 +71,6 @@ def add_knx_devices(devices, groups):
|
||||
DeviceInfo("KlingelOben", "Klingel Oben", 'first_floor'),
|
||||
DeviceInfo("Klingel Innen", "Klingel Innentür", 'other'),
|
||||
DeviceInfo("Klingel Aussen", "Klingen Außentür", 'other'),
|
||||
# Bewegungsmelder LEDs
|
||||
DeviceInfo("BewegungsmelderMitte LED", "Bewegungsmelder Mitte LED", 'hallway'),
|
||||
DeviceInfo("BewegungsmelderWest LED", "Bewegungsmelder West LED", 'hallway'),
|
||||
DeviceInfo("BewegungsmelderOst LED", "Bewegungsmelder Ost LED", 'hallway'),
|
||||
]
|
||||
scene_button_names = ['ObenLinks', 'ObenRechts', 'MitteLinks', 'MitteRechts', 'UntenLinks', 'UntenRechts']
|
||||
scene_button_names = [(i, e) for i, e in enumerate(scene_button_names)]
|
||||
@@ -174,15 +175,33 @@ def add_fhem_devices(devices, groups):
|
||||
devices[device_type].append(device)
|
||||
|
||||
|
||||
def add_light_groups(groups):
|
||||
light_groups = {
|
||||
f"light_{group_id}": {
|
||||
'name': content['name'] + " Lichter",
|
||||
'entities': [e for e in content['entities'] if e.startswith('light.')],
|
||||
}
|
||||
for group_id, content in groups.items() if 'name' in content
|
||||
def add_meta_groups(groups):
|
||||
all_devices = set()
|
||||
for group in groups.values():
|
||||
all_devices.update(group['entities'])
|
||||
first_floor = set(groups['first_floor']['entities'])
|
||||
bedroom = set(groups['bedroom']['entities'])
|
||||
outside = set(groups['outside']['entities'])
|
||||
|
||||
groups['all_downstairs'] = {
|
||||
'name': 'Unten Alles',
|
||||
'entities': [e for e in all_devices - first_floor]
|
||||
}
|
||||
|
||||
groups['all_downstairs_but_outside'] = {
|
||||
'name': 'Unten Alles Ausser Draussen',
|
||||
'entities': [e for e in all_devices - first_floor - outside]
|
||||
}
|
||||
|
||||
groups['all_downstairs_but_bedroom'] = {
|
||||
'name': 'Unten Alles Ausser Schlafzimmer',
|
||||
'entities': [e for e in all_devices - first_floor - bedroom],
|
||||
}
|
||||
|
||||
groups['all_downstairs_but_bedroom_and_outside'] = {
|
||||
'name': 'Unten Alles Ausser Schlafzimmer und Draussen',
|
||||
'entities': [e for e in all_devices - first_floor - bedroom - outside],
|
||||
}
|
||||
groups.update(light_groups)
|
||||
|
||||
|
||||
def make_sensor_exclude_list(all_devices, name_fragments):
|
||||
@@ -256,8 +275,9 @@ def create_config(target_directory, development=False):
|
||||
yaml.dump(all_devices, output)
|
||||
yaml.dump(logbook_config(all_devices), output)
|
||||
yaml.dump(recorder_config(all_devices), output)
|
||||
output.write("automation manual: !include_dir_merge_list automations\n")
|
||||
|
||||
add_light_groups(group_dict)
|
||||
add_meta_groups(group_dict)
|
||||
|
||||
with open(os.path.join(target_directory, 'groups.yaml'), 'w') as output:
|
||||
output.write("# Dont' edit manually! this is generated!\n\n")
|
||||
@@ -270,6 +290,8 @@ def create_config(target_directory, development=False):
|
||||
additional_file = 'secrets_development.yaml' if development else 'secrets_deploy.yaml'
|
||||
output.write(open(os.path.join(script_path, additional_file), 'r').read())
|
||||
|
||||
create_automation_rules(os.path.join(target_directory, 'automations'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
Reference in New Issue
Block a user