Rule creation for IR remotes
This commit is contained in:
parent
bf0a15bf5f
commit
08c18b3dee
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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.')],
|
||||
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]
|
||||
}
|
||||
for group_id, content in groups.items() if 'name' in content
|
||||
|
||||
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()
|
||||
|
|
|
@ -6,6 +6,7 @@ import sys
|
|||
from typing import List, Tuple
|
||||
import voluptuous as vol
|
||||
from ..reconnecting_client import ReconnectingClient
|
||||
from .radio import find_local_radio_url_by_name
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDevice, PLATFORM_SCHEMA)
|
||||
|
@ -485,6 +486,9 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist.
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
if media_type == 'channel':
|
||||
media_id = find_local_radio_url_by_name(media_id)
|
||||
|
||||
if kwargs.get(ATTR_MEDIA_ENQUEUE):
|
||||
return self._add_uri_to_playlist(media_id)
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import xml.etree.ElementTree as ET
|
||||
from urllib.request import urlopen
|
||||
|
||||
ompl_radio_browse_url = 'http://opml.radiotime.com/Browse.ashx'
|
||||
|
||||
|
||||
def radio_name_cleanup(radio_name):
|
||||
"""Removes tokens with brackets or points
|
||||
"Radio Bamberg 106.1 (Top 40/Pop)" -> "Radio Bamberg 106.1"
|
||||
"""
|
||||
radio_name = radio_name.split()
|
||||
res = []
|
||||
for token in radio_name:
|
||||
if '(' in token or ')' in token:
|
||||
continue
|
||||
res.append(token)
|
||||
return " ".join(res)
|
||||
|
||||
|
||||
def get_sender_information(queryURL):
|
||||
"""
|
||||
Example Query: GET http://opml.radiotime.com/Browse.ashx?partnerId=<partnerid>&serial=<serial>
|
||||
partnerid and serial does not seem to be necessary - so we do not use it here,
|
||||
"""
|
||||
xmlfile = urlopen(queryURL)
|
||||
tree = ET.ElementTree(file=xmlfile)
|
||||
rootnode = tree.getroot()
|
||||
|
||||
result = dict()
|
||||
for node in rootnode.iter('outline'):
|
||||
if 'type' not in node.attrib or node.attrib['type'] != "audio":
|
||||
continue
|
||||
station_name = radio_name_cleanup(node.attrib['text'])
|
||||
station_url = node.attrib['URL']
|
||||
station_id = node.attrib['guide_id']
|
||||
print(station_name)
|
||||
result[station_id] = {'name': station_name, 'url': station_url}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_related_radio_stations(stationID):
|
||||
query_url = ompl_radio_browse_url + '?id=' + stationID
|
||||
return get_sender_information(query_url)
|
||||
|
||||
|
||||
def get_local_radio_stations():
|
||||
# goto ompl.radiotime with parameter ?c=local and look for your city there then paste id here
|
||||
query_url = ompl_radio_browse_url + '?id=r100765'
|
||||
result = get_sender_information(query_url)
|
||||
print(result)
|
||||
return result
|
||||
|
||||
|
||||
def find_local_radio_url_by_name(search_name):
|
||||
"""Searches at the Radiotime database for a local radiostation which contains the searchName """
|
||||
search_name = search_name.lower().strip()
|
||||
if search_name.startswith('http'):
|
||||
return search_name
|
||||
|
||||
for radioId, radio in find_local_radio_url_by_name.stations.items():
|
||||
if search_name in radio['name'].lower():
|
||||
return radio['url']
|
||||
|
||||
raise ValueError("Could not find radio station " + search_name)
|
||||
|
||||
|
||||
find_local_radio_url_by_name.stations = get_local_radio_stations()
|
57
scenes.yaml
57
scenes.yaml
|
@ -202,6 +202,51 @@
|
|||
xy_color: [0.2075, 0.6584]
|
||||
|
||||
|
||||
- name: Wohnbereich Hell
|
||||
entities:
|
||||
light.esszimmer_deckenlampe_west:
|
||||
state: on
|
||||
brightness: 255
|
||||
light.wohnzimmer_deckenlampe:
|
||||
state: on
|
||||
brightness: 255
|
||||
light.kuche_deckenlampe:
|
||||
state: on
|
||||
brightness: 255
|
||||
light.wohnzimmer_regal_links:
|
||||
state: on
|
||||
brightness: 255
|
||||
xy_color: [0.527, 0.447]
|
||||
light.kuche_links:
|
||||
state: on
|
||||
brightness: 255
|
||||
xy_color: [0.537, 0.438]
|
||||
light.kuche_rechts:
|
||||
state: on
|
||||
brightness: 255
|
||||
xy_color: [0.557, 0.403]
|
||||
light.wohnzimmer_regal_rechts:
|
||||
state: on
|
||||
brightness: 255
|
||||
xy_color: [0.616, 0.371]
|
||||
light.wohnzimmer_stehlampe_oben:
|
||||
state: on
|
||||
brightness: 255
|
||||
color_temp: 492
|
||||
light.wohnzimmer_stehlampe:
|
||||
state: on
|
||||
brightness: 255
|
||||
color_temp: 492
|
||||
light.wohnzimmer_kugel:
|
||||
state: on
|
||||
brightness: 255
|
||||
color_temp: 492
|
||||
light.kuche_vorne:
|
||||
state: on
|
||||
brightness: 255
|
||||
color_temp: 492
|
||||
|
||||
|
||||
- name: Wohnbereich Meditation
|
||||
entities:
|
||||
light.kuche_deckenlampe:
|
||||
|
@ -330,12 +375,20 @@
|
|||
|
||||
- name: Schlafzimmer Bettlicht dunkel
|
||||
entities:
|
||||
light.schlafzimmer_deckenlampe:
|
||||
state: off
|
||||
light.schlafzimmer_schrank:
|
||||
state: off
|
||||
light.bett_martin:
|
||||
state: on
|
||||
brightness: 2
|
||||
brightness: 5
|
||||
xy_color: [0.502, 0.414]
|
||||
light.bett_rebecca:
|
||||
state: on
|
||||
brightness: 2
|
||||
brightness: 5
|
||||
xy_color: [0.502, 0.414]
|
||||
light.schlafzimmer_fluter:
|
||||
state: off
|
||||
|
||||
|
||||
- name: Schlafzimmer Orange
|
||||
|
|
Loading…
Reference in New Issue