Google Home 알람 및 타이머를 알림으로 받기

TL;DR
Using Home Assistant and Node-RED I receive actionable notifications on my android phone for Google Home alarms and timers. The notifications show all information, they are grouped and have a countdown.



목차


  • Alarms & Timers
  • Google Home integration
  • Goal

  • Node-RED
  • The subflow
  • Top flow
  • Bottom flow
  • How to use


  • 저는 Google(Nest) 스마트 기기의 열렬한 팬이며 스마트 홈 설정에 여러 개의 Hub 및 Mini 기기가 있습니다. Home Assistant 및 Node-RED와 페어링하여 모든 장치 및 자동화 흐름을 제어하는 ​​데 사용합니다.

    알람 및 타이머



    장치의 알람 및 타이머 기능은 내가 가장 자주 사용하는 기능이며 매일 사용합니다.
    그들은 잘 작동하지만 멋지게 만드는 몇 가지 기능이 없습니다.
  • 내 전화기에 표시되지 않음
  • 전화로 제어할 수 없습니다
  • 장치가 울리고 내가 같은 방에 없을 때 알람/타이머가 울리는 것을 알아차리지 못합니다

  • 홈어시스턴트 Google 홈 통합



    이번 주에 저는 알람과 타이머를 HA의 센서로 노출하는 "Google Home"이라는 새로운 커뮤니티 통합을 발견했습니다.


    레이코일자 / 하-구글홈


    홈어시스턴트 Google 홈 맞춤 구성요소




    HACS를 통해 설치했는데 정말 잘 작동합니다. 알람 및 타이머용 센서 엔티티가 장치별로 생성됩니다.

    나는 나를 위해 더 많은 기능을 추가할 몇 가지 새로운 자동화에 사용할 수 있는지, 특히 내 전화에 알림으로 사용할 수 있는지 궁금했습니다.

    목표




  • 내 전화기에서 알람 및 타이머를 알림으로 표시
  • 발사 시간까지 카운트다운하자
  • 알림이 울리면 다음과 같이 표시되어야 합니다
  • .
  • 알림을 해제하면 알람/타이머가 삭제되어야 함
  • Google 장치에서 삭제된 경우 알림을 제거해야 합니다
  • .

    Node-RED



    저는 모든 자동화에 Node-RED를 사용합니다. 흐름을 시각화하는 방식이 마음에 듭니다. 코드 논리를 적용하는 옵션도 좋아하는 개발자로서 종종 로우 코드 솔루션으로 설명됩니다.

    다음에 보시는 것은 몇 가지 시행 착오 프로그래밍의 최종 결과입니다. 일반적으로 저는 1개의 센서 입력과 정상적인 흐름으로 시작합니다. 그런 다음 미세 조정하고 종종 재사용 가능한 하위 흐름으로 변환합니다.

    기본 흐름은 다음과 같습니다.


    최종 결과에서 저는 모든 것을 제어할 수 있는 1개의 sublfow만 갖고 싶었고, 여러 개의 알람 및 타이머 센서를 입력으로 사용했습니다.

    하위 흐름




    상위 흐름은 모든 입력을 처리합니다.
    하단 흐름은 지워진 알림을 처리합니다.

    탑 플로우


  • 센서의 입력을 구문 분석합니다
  • .
  • 알람 또는 타이머 센서인지 확인
  • 활성 항목(유형 및 센서 정보 포함)을 메모리에 캐시합니다
  • .
  • 모든 활성 항목에 대해 알림 메시지 생성/업데이트
  • 모든 비활성 항목에 대해 clear_notification 메시지를 만듭니다
  • .
  • 호출 서비스 노드에 메시지를 보냅니다
  • .



    모든 어려운 작업을 수행하는 "Parse alarms & timers"기능 노드부터 시작하겠습니다.
    함수 노드의 "설정"탭을 사용하여 흐름에서 사용되는 몇 가지 기본값을 설정합니다.

    // Set type defaults
    const defaults = {
        timer: {
            id: 'timer_id',
            label: '',
            items: 'timers',
            name: 'Timers',
            deleteService: 'delete_timer'
        },
        alarm: {
            id: 'alarm_id',
            label: '',
            items: 'alarms',
            name: 'Alarms',
            deleteService: 'delete_alarm'
        },
    }
    flow.set('defaults', defaults);
    

    "기능"탭

    const defaults = flow.get('defaults');
    const service = env.get('service');
    const messages = [];
    let type = '';
    if(!msg.data) {
        return null;
    }
    // Determine type: alarm or timer
    if(typeof msg.data.new_state.attributes.timers !== 'undefined' || typeof msg.data.old_state.attributes.timers !== 'undefined' ) {
        type = 'timer';
    } else if (typeof msg.data.new_state.attributes.alarms !== 'undefined' || typeof msg.data.old_state.attributes.alarms !== 'undefined') {
        type = 'alarm';
    } else {
        return null;
    }
    
    const typeValues = defaults[type];
    
    // Create a alarm/timer item ready to be used by the service
    const createItem = (item, sensor, type, device) => {
        const doneTime = new Date(item.fire_time * 1000).toTimeString().substr(0, 5);
        let label = `${typeValues.label}`;
        let vibration = '';
        let chronometer = true;
        if(item.status === 'ringing') {
            label = item.label ? `${label} ${item.label} RINGING` : `${label} RINGING`;
            vibration = '100, 1000, 100, 1000, 100, 100, 1000, 100, 1000, 100';
            chronometer = false;
        } else {
            label = item.label ? `${label} ${doneTime} ${item.label}` : `${label} ${doneTime}`;
        }
        let title = label;
        if(item.duration) {
            title = `${label} (${item.duration})`;
        }
        return { 
            id: item[typeValues.id],
            sensor: sensor,
            device: device,
            type: type,
            status: item.status,
            data:{
                title: title,
                message: `${device}`,
                data: {
                    tag: item[typeValues.id],
                    chronometer: chronometer,
                    when: item.fire_time,
                    sticky: 'true',
                    group: typeValues.name,
                    vibrationPattern: vibration 
                }            
            }
        }
    };
    
    const mapItems = (arr) => {
        return arr.map((item) => { return createItem(item, msg.data.entity_id, type, msg.data.old_state.attributes.friendly_name) })
    }
    const activeItems = mapItems(msg.data.new_state.attributes[typeValues.items] || []).filter(item => item.status !== 'none');
    
    const cachedItems = flow.get('cachedItems') || new Map();
    
    // Update or create notifications for active items
    activeItems.forEach((item) => {
        messages.push({
            payload: {
                service: service,
                data: item.data    
            }
        });
        cachedItems.set(item.id, item);
    })
    
    // Clear expired/deleted notifications
    cachedItems.forEach((item, id) => {
        const findItem = activeItems.find(newItem => newItem.id === id);
        if(!findItem && item.sensor === msg.data.entity_id) {
            messages.push({
                payload: {
                    service: service,
                    data: {
                        message: 'clear_notification',
                        data: {
                            tag: id
                        }
                    }    
                }
            });
            cachedItems.delete(id);
        }
    })
    
    flow.set('cachedItems', cachedItems);
    
    // Send notifications as a stream
    messages.forEach((msg) => node.send(msg));
    
    // All done
    node.done();
    

    바닥 흐름


  • 지워진 알림 감지
  • 캐시에서 조회
  • 발견되면 delete_alarm/timer 서비스를 호출합니다
  • .
  • 캐시에서 항목 제거


  • const cachedItems = flow.get('cachedItems');
    const defaults = flow.get('defaults');
    
    // Find cleared item
    const findItem = cachedItems.get(msg.payload.event.tag);
    if(!findItem) {
        return null
    }
    node.send(
        {
            payload: {
                service: defaults[findItem.type].deleteService,
                data: {
                    entity_id: findItem.sensor,
                    [defaults[findItem.type].id]: findItem.id
                }
            }
        }
    )
    
    // Clean up
    cachedItems.delete(findItem.id);
    flow.set('cachedItems', cachedItems);        
    node.done();
    

    사용하는 방법


  • NR에서 하위 흐름을 가져옵니다.




  • 그런 다음 서비스에 대한 환경 변수를 편집합니다.
  • 하위 흐름을 추가하고 일부 알람 및/또는 타이머 센서를 events_state 노드로 연결합니다.

  • 모두 완료되었습니다. Google Home 기기에서 알람/타이머를 생성하여 시도해 보세요. 몇 초 안에 휴대폰으로 알림을 받아야 합니다.
  • 좋은 웹페이지 즐겨찾기