diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8194e3150e..20493fea0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,127 +61,260 @@ test2: <<: *docker-builder stage: build script: + - docker run --rm --privileged hassioaddons/qemu-user-static:latest + - BUILD_FROM=homeassistant/${ADDON_ARCH}-base-ubuntu:latest + - ADDON_VERSION="${CI_COMMIT_TAG#v}" + - ADDON_VERSION="${ADDON_VERSION:-${CI_COMMIT_SHA:0:7}}" + - ESPHOMELIB_VERSION="${ESPHOMELIB_VERSION:-dev}" + - echo "Build from ${BUILD_FROM}" + - echo "Add-on version ${ADDON_VERSION}" + - echo "Esphomelib version ${ESPHOMELIB_VERSION}" + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - | - hassio-builder.sh \ - -t . \ - -i ottowinter/esphomeyaml-hassio-${ADDON_ARCH} \ - -d "$CI_REGISTRY" \ - --${ADDON_ARCH} + docker build \ + --build-arg "BUILD_FROM=${BUILD_FROM}" \ + --build-arg "ADDON_ARCH=${ADDON_ARCH}" \ + --build-arg "ADDON_VERSION=${ADDON_VERSION}" \ + --build-arg "ESPHOMELIB_VERSION=${ESPHOMELIB_VERSION}" \ + --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" \ + --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + --file "docker/Dockerfile.hassio" \ + . - | - docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" - retry: 2 + if [ "${DO_PUSH:-true}" = true ]; then + echo "Pushing to CI registry" + docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA} + docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev + fi # Generic deploy template -.deploy: &deploy +.deploy-release: &deploy-release <<: *docker-builder stage: deploy script: - - version=${CI_COMMIT_TAG:1} - - echo "Publishing version ${version}" + - version="${CI_COMMIT_TAG#v}" + - echo "Publishing release version ${version}" + - docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - | docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest" - | docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest" + + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - | docker tag \ - "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + only: + - /^v\d+\.\d+\.\d+$/ + except: + - /^(?!master).+@/ + +.deploy-beta: &deploy-beta + <<: *docker-builder + stage: deploy + script: + - version="${CI_COMMIT_TAG#v}" + - echo "Publishing beta version ${version}" + - docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" + + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - | + docker tag \ + "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + only: + - /^v\d+\.\d+\.\d+b\d+$/ + except: + - /^(?!rc).+@/ + # Build jobs build:normal: <<: *docker-builder stage: build script: - - docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" . - - | - docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" \ - "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" + - docker build -t "${CI_REGISTRY}/esphomeyaml:dev" . -build:armhf: +.build-hassio-edge: &build-hassio-edge <<: *build-hassio - variables: - ADDON_ARCH: armhf - -#build:aarch64: -# <<: *build -# variables: -# ADDON_ARCH: aarch64 - -build:i386: - <<: *build-hassio - variables: - ADDON_ARCH: i386 - -build:amd64: - <<: *build-hassio - variables: - ADDON_ARCH: amd64 - -# Deploy jobs -deploy:armhf: - <<: *deploy - variables: - ADDON_ARCH: armhf - only: - - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ except: - - /^(?!master).+@/ + - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+b\d+$/ -#deploy:aarch64: -# <<: *deploy +.build-hassio-release: &build-hassio-release + <<: *build-hassio + only: + - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+b\d+$/ + +build:hassio-armhf-edge: + <<: *build-hassio-edge + variables: + ADDON_ARCH: armhf + DO_PUSH: "false" + +build:hassio-armhf: + <<: *build-hassio-release + variables: + ADDON_ARCH: armhf + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +#build:hassio-aarch64-edge: +# <<: *build-hassio-edge # variables: # ADDON_ARCH: aarch64 -# only: -# - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ -# except: -# - /^(?!master).+@/ +# DO_PUSH: "false" -deploy:i386: - <<: *deploy +#build:hassio-aarch64: +# <<: *build-hassio-release +# variables: +# ADDON_ARCH: aarch64 +# ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +build:hassio-i386-edge: + <<: *build-hassio-edge variables: ADDON_ARCH: i386 - only: - - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ - except: - - /^(?!master).+@/ + DO_PUSH: "false" -deploy:amd64: - <<: *deploy +build:hassio-i386: + <<: *build-hassio-release + variables: + ADDON_ARCH: i386 + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +build:hassio-amd64-edge: + <<: *build-hassio-edge variables: ADDON_ARCH: amd64 + DO_PUSH: "false" + +build:hassio-amd64: + <<: *build-hassio-release + variables: + ADDON_ARCH: amd64 + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +# Deploy jobs +deploy-release:armhf: + <<: *deploy-release + variables: + ADDON_ARCH: armhf + +deploy-beta:armhf: + <<: *deploy-beta + variables: + ADDON_ARCH: armhf + +#deploy-release:aarch64: +# <<: *deploy-release +# variables: +# ADDON_ARCH: aarch64 +# +#deploy-beta:aarch64: +# <<: *deploy-beta +# variables: +# ADDON_ARCH: aarch64 + +deploy-release:i386: + <<: *deploy-release + variables: + ADDON_ARCH: i386 + +deploy-beta:i386: + <<: *deploy-beta + variables: + ADDON_ARCH: i386 + +deploy-release:amd64: + <<: *deploy-release + variables: + ADDON_ARCH: amd64 + +deploy-beta:amd64: + <<: *deploy-beta + variables: + ADDON_ARCH: amd64 + +.deploy-pypi: &deploy-pypi + stage: deploy + before_script: + - pip install -e . + - pip install twine + script: + - python setup.py sdist + - twine upload dist/* + tags: + - python2.7 + - esphomeyaml-test + +deploy-release:pypi: + <<: *deploy-pypi only: - - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ + - /^v\d+\.\d+\.\d+$/ except: - /^(?!master).+@/ -deploy:pypi: - stage: deploy - before_script: - - pip install -e . - - pip install twine - script: - - python setup.py sdist - - twine upload dist/* - tags: - - python2.7 - - esphomeyaml-test +deploy-beta:pypi: + <<: *deploy-pypi only: - - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ + - /^v\d+\.\d+\.\d+b\d+$/ except: - - /^(?!master).+@/ + - /^(?!rc).+@/ diff --git a/Dockerfile b/Dockerfile index 81eefcd244..48342c0124 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,28 @@ -FROM python:2.7 +ARG BUILD_FROM=python:2.7 +FROM ${BUILD_FROM} MAINTAINER Otto Winter RUN apt-get update && apt-get install -y \ python-pil \ - && rm -rf /var/lib/apt/lists/* + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 ENV ESPHOMEYAML_OTA_HOST_PORT=6123 EXPOSE 6123 VOLUME /config WORKDIR /usr/src/app -RUN pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /usr/src/app/ -RUN platformio settings set enable_telemetry No && \ - platformio run -e espressif32 -e espressif8266; exit 0 +COPY docker/platformio.ini /pio/platformio.ini +RUN platformio run -d /pio; rm -rf /pio COPY . . -RUN pip install --no-cache-dir -e . && \ - pip install --no-cache-dir tzlocal pillow +RUN pip install --no-cache-dir --no-binary :all: -e . && \ + pip install --no-cache-dir --no-binary :all: tzlocal WORKDIR /config ENTRYPOINT ["esphomeyaml"] diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 deleted file mode 100644 index e61ff0f92c..0000000000 --- a/docker/Dockerfile.aarch64 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for aarch64 version of HassIO add-on -FROM arm64v8/ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.amd64 b/docker/Dockerfile.amd64 deleted file mode 100644 index 694e7c6762..0000000000 --- a/docker/Dockerfile.amd64 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for amd64 version of HassIO add-on -FROM ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf deleted file mode 100644 index 3b2cfedf8f..0000000000 --- a/docker/Dockerfile.armhf +++ /dev/null @@ -1,31 +0,0 @@ -# Dockerfile for armhf version of HassIO add-on -FROM homeassistant/armhf-base:latest - -RUN apk add --no-cache \ - python2 \ - python2-dev \ - py2-pip \ - git \ - gcc \ - openssh \ - libc6-compat \ - jpeg-dev \ - zlib-dev \ - freetype-dev \ - lcms2-dev \ - openjpeg-dev \ - tiff-dev \ - libc-dev \ - linux-headers \ - && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio-esp8266.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir pillow tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.builder b/docker/Dockerfile.builder index d8148da3bd..2077b7bde1 100644 --- a/docker/Dockerfile.builder +++ b/docker/Dockerfile.builder @@ -27,6 +27,4 @@ RUN apt-get update && apt-get install -y \ binfmt-support \ && rm -rf /var/lib/apt/lists/* -COPY docker/hassio-builder.sh /usr/bin/ - WORKDIR /data diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio new file mode 100644 index 0000000000..651f8b8a68 --- /dev/null +++ b/docker/Dockerfile.hassio @@ -0,0 +1,42 @@ +# Dockerfile for HassIO add-on +ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest +FROM ${BUILD_FROM} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python \ + python-pip \ + python-setuptools \ + python-pil \ + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 + +COPY docker/platformio.ini /pio/platformio.ini +RUN platformio run -d /pio; rm -rf /pio + +ARG ESPHOMELIB_VERSION="dev" +RUN platformio lib -g install "https://github.com/OttoWinter/esphomelib.git#${ESPHOMELIB_VERSION}" + +COPY . . +RUN pip install --no-cache-dir --no-binary :all: -e . && \ + pip install --no-cache-dir --no-binary :all: tzlocal + +CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] + +# Build arugments +ARG ADDON_ARCH +ARG ADDON_VERSION + +# Labels +LABEL \ + io.hass.name="esphomeyaml" \ + io.hass.description="esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices." \ + io.hass.arch="${ADDON_ARCH}" \ + io.hass.type="addon" \ + io.hass.version="${ADDON_VERSION}" \ + io.hass.url="https://esphomelib.com/esphomeyaml/index.html" \ + maintainer="Otto Winter " diff --git a/docker/Dockerfile.i386 b/docker/Dockerfile.i386 deleted file mode 100644 index 06ef39f6ef..0000000000 --- a/docker/Dockerfile.i386 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for i386 version of HassIO add-on -FROM i386/ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/hassio-builder.sh b/docker/hassio-builder.sh deleted file mode 100755 index 699e028748..0000000000 --- a/docker/hassio-builder.sh +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/env bash -# Based on Home Assistant's docker builder -###################### -# Hass.io Build-env -###################### -set -e - -echo -- "$@" - -#### Variable #### - -DOCKER_TIMEOUT=20 -DOCKER_PID=-1 -DOCKER_HUB="" -DOCKER_CACHE="true" -DOCKER_LOCAL="false" -TARGET="" -IMAGE="" -BUILD_LIST=() -BUILD_TASKS=() - -#### Misc functions #### - -function print_help() { - cat << EOF -Hass.io build-env for ecosystem: -docker run --rm homeassistant/{arch}-builder:latest [options] - -Options: - -h, --help - Display this help and exit. - - Repository / Data - -t, --target - Set local folder or path inside repository for build. - - Version/Image handling - -i, --image - Overwrite image name of build / support {arch} - - Architecture - --armhf - Build for arm. - --amd64 - Build for intel/amd 64bit. - --aarch64 - Build for arm 64bit. - --i386 - Build for intel/amd 32bit. - --all - Build all architecture. - - Build handling - --no-cache - Disable cache for the build (from latest). - -d, --docker-hub - Set or overwrite the docker repository. - - Use the host docker socket if mapped into container: - /var/run/docker.sock - -EOF - - exit 1 -} - -#### Docker functions #### - -function start_docker() { - local starttime - local endtime - - if [ -S "/var/run/docker.sock" ]; then - echo "[INFO] Use host docker setup with '/var/run/docker.sock'" - DOCKER_LOCAL="true" - return 0 - fi - - echo "[INFO] Starting docker." - dockerd 2> /dev/null & - DOCKER_PID=$! - - echo "[INFO] Waiting for docker to initialize..." - starttime="$(date +%s)" - endtime="$(date +%s)" - until docker info >/dev/null 2>&1; do - if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then - sleep 1 - endtime=$(date +%s) - else - echo "[ERROR] Timeout while waiting for docker to come up" - exit 1 - fi - done - echo "[INFO] Docker was initialized" -} - - -function stop_docker() { - local starttime - local endtime - - if [ "$DOCKER_LOCAL" == "true" ]; then - return 0 - fi - - echo "[INFO] Stopping in container docker..." - if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then - starttime="$(date +%s)" - endtime="$(date +%s)" - - # Now wait for it to die - kill "$DOCKER_PID" - while kill -0 "$DOCKER_PID" 2> /dev/null; do - if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then - sleep 1 - endtime=$(date +%s) - else - echo "[ERROR] Timeout while waiting for container docker to die" - exit 1 - fi - done - else - echo "[WARN] Your host might have been left with unreleased resources" - fi -} - -function run_build() { - local build_dir=$1 - local repository=$2 - local image=$3 - local version=$4 - local build_arch=$5 - local docker_cli=("${!6}") - - local push_images=() - - # Overwrites - if [ ! -z "$DOCKER_HUB" ]; then repository="$DOCKER_HUB"; fi - if [ ! -z "$IMAGE" ]; then image="$IMAGE"; fi - - # Init Cache - if [ "$DOCKER_CACHE" == "true" ]; then - echo "[INFO] Init cache for $repository/$image:$version" - if docker pull "$repository/$image:latest" > /dev/null 2>&1; then - docker_cli+=("--cache-from" "$repository/$image:latest") - else - docker_cli+=("--no-cache") - echo "[WARN] No cache image found. Cache is disabled for build" - fi - else - docker_cli+=("--no-cache") - fi - - # Build image - echo "[INFO] Run build for $repository/$image:$version" - docker build --pull -t "$repository/$image:$version" \ - --label "io.hass.version=$version" \ - --label "io.hass.arch=$build_arch" \ - -f "$TARGET/docker/Dockerfile.$build_arch" \ - "${docker_cli[@]}" \ - "$build_dir" - - echo "[INFO] Finish build for $repository/$image:$version" - docker tag "$repository/$image:$version" "$repository/$image:dev" -} - - -#### HassIO functions #### - -function build_addon() { - local build_arch=$1 - - local docker_cli=() - local image="" - local repository="" - local raw_image="" - local name="" - local description="" - local url="" - local args="" - - # Read addon config.json - name="$(jq --raw-output '.name // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")" - description="$(jq --raw-output '.description // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")" - url="$(jq --raw-output '.url // empty' "$TARGET/esphomeyaml/config.json")" - version="$(jq --raw-output '.version' "$TARGET/esphomeyaml/config.json")" - raw_image="$(jq --raw-output '.image // empty' "$TARGET/esphomeyaml/config.json")" - - # Read data from image - if [ ! -z "$raw_image" ]; then - repository="$(echo "$raw_image" | cut -f 1 -d '/')" - image="$(echo "$raw_image" | cut -f 2 -d '/')" - fi - - # Set additional labels - docker_cli+=("--label" "io.hass.name=$name") - docker_cli+=("--label" "io.hass.description=$description") - docker_cli+=("--label" "io.hass.type=addon") - - if [ ! -z "$url" ]; then - docker_cli+=("--label" "io.hass.url=$url") - fi - - # Start build - run_build "$TARGET" "$repository" "$image" "$version" \ - "$build_arch" docker_cli[@] -} - -#### initialized cross-build #### - -function init_crosscompile() { - echo "[INFO] Setup crosscompiling feature" - ( - mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc - update-binfmts --enable qemu-arm - update-binfmts --enable qemu-aarch64 - ) > /dev/null 2>&1 || echo "[WARN] Can't enable crosscompiling feature" -} - - -function clean_crosscompile() { - echo "[INFO] Clean crosscompiling feature" - if [ -f /proc/sys/fs/binfmt_misc ]; then - umount /proc/sys/fs/binfmt_misc || true - fi - - ( - update-binfmts --disable qemu-arm - update-binfmts --disable qemu-aarch64 - ) > /dev/null 2>&1 || echo "[WARN] No crosscompiling feature found for cleanup" -} - -#### Error handling #### - -function error_handling() { - stop_docker - clean_crosscompile - - exit 1 -} -trap 'error_handling' SIGINT SIGTERM - -#### Parse arguments #### - -while [[ $# -gt 0 ]]; do - key=$1 - case ${key} in - -h|--help) - print_help - ;; - -t|--target) - TARGET=$2 - shift - ;; - -i|--image) - IMAGE=$2 - shift - ;; - --no-cache) - DOCKER_CACHE="false" - ;; - -d|--docker-hub) - DOCKER_HUB=$2 - shift - ;; - --armhf) - BUILD_LIST+=("armhf") - ;; - --amd64) - BUILD_LIST+=("amd64") - ;; - --i386) - BUILD_LIST+=("i386") - ;; - --aarch64) - BUILD_LIST+=("aarch64") - ;; - --all) - BUILD_LIST=("armhf" "amd64" "i386" "aarch64") - ;; - - *) - echo "[WARN] $0 : Argument '$1' unknown will be Ignoring" - ;; - esac - shift -done - -# Check if an architecture is available -if [ "${#BUILD_LIST[@]}" -eq 0 ]; then - echo "[ERROR] You need select an architecture for build!" - exit 1 -fi - - -#### Main #### - -mkdir -p /data - -# Setup docker env -init_crosscompile -start_docker - -# Select arch build -for arch in "${BUILD_LIST[@]}"; do - (build_addon "$arch") & - BUILD_TASKS+=($!) -done - -# Wait until all build jobs are done -wait "${BUILD_TASKS[@]}" - -# Cleanup docker env -clean_crosscompile -stop_docker - -exit 0 diff --git a/docker/platformio-esp8266.ini b/docker/platformio-esp8266.ini deleted file mode 100644 index ed48f777cf..0000000000 --- a/docker/platformio-esp8266.ini +++ /dev/null @@ -1,7 +0,0 @@ -; This file allows the docker build file to install the required platformio -; platforms - -[env:espressif8266] -platform = espressif8266 -board = nodemcuv2 -framework = arduino diff --git a/esphomeyaml-beta/config.json b/esphomeyaml-beta/config.json index c8b9c4c8f3..066498b383 100644 --- a/esphomeyaml-beta/config.json +++ b/esphomeyaml-beta/config.json @@ -3,7 +3,7 @@ "version": "1.9.0b6", "slug": "esphomeyaml-beta", "description": "Beta version of esphomeyaml HassIO add-on.", - "url": "https://esphomelib.com/esphomeyaml/index.html", + "url": "https://beta.esphomelib.com/esphomeyaml/index.html", "startup": "application", "webui": "http://[HOST]:[PORT:6052]", "boot": "auto", diff --git a/esphomeyaml-edge/Dockerfile b/esphomeyaml-edge/Dockerfile index be1babac3b..6c81bcaf29 100644 --- a/esphomeyaml-edge/Dockerfile +++ b/esphomeyaml-edge/Dockerfile @@ -1,59 +1,24 @@ -# Dockerfile for HassIO add-on -ARG BUILD_FROM=ubuntu:bionic +# Dockerfile for HassIO edge add-on +ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest FROM ${BUILD_FROM} -# Re-declare BUILD_FROM to fix weird docker issue -ARG BUILD_FROM +RUN apt-get update && apt-get install -y --no-install-recommends \ + python \ + python-pip \ + python-setuptools \ + python-pil \ + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 -# On amd64 and alike, using ubuntu as the base is better as building -# for the ESP32 only works with glibc (and ubuntu). However, on armhf -# the build toolchain frequently procudes segfaults under ubuntu. -# -> Use ubuntu for most architectures, except alpine for armhf -# -# * python and related required because this is a python project -# * git required for platformio library dependencies downloads -# * libc6-compat and openssh required on alpine for weird reasons -# * disable platformio telemetry on install -RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \ - apt-get update && apt-get install -y --no-install-recommends \ - python python-pip python-setuptools python-pil git && \ - rm -rf /var/lib/apt/lists/* /tmp/*; \ - else \ - apk add --no-cache \ - python2 \ - python2-dev \ - py2-pip \ - git \ - gcc \ - openssh \ - libc6-compat \ - jpeg-dev \ - zlib-dev \ - freetype-dev \ - lcms2-dev \ - openjpeg-dev \ - tiff-dev \ - libc-dev \ - linux-headers; \ - fi" && \ - pip install --no-cache-dir platformio && \ - platformio settings set enable_telemetry No - - -# Create fake project to make platformio install all depdencies. -# * Ignore build errors from platformio - empty project -# * On alpine, only install ESP8266 toolchain COPY platformio.ini /pio/platformio.ini -RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \ - platformio run -e espressif32 -e espressif8266 -d /pio; exit 0; \ - else \ - echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \ - platformio run -e espressif8266 -d /pio; exit 0; \ - fi" +RUN platformio run -d /pio; rm -rf /pio -# Install latest esphomeyaml from git -RUN pip install --no-cache-dir \ - git+git://github.com/OttoWinter/esphomeyaml.git && \ +RUN pip install --no-cache-dir git+https://github.com/OttoWinter/esphomeyaml.git@dev#egg=esphomeyaml && \ pip install --no-cache-dir pillow tzlocal CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/esphomeyaml-edge/build.json b/esphomeyaml-edge/build.json index a306cb0cc3..f60fa4f7fe 100644 --- a/esphomeyaml-edge/build.json +++ b/esphomeyaml-edge/build.json @@ -1,10 +1,10 @@ { "squash": false, "build_from": { - "aarch64": "arm64v8/ubuntu:bionic", - "amd64": "ubuntu:bionic", - "armhf": "homeassistant/armhf-base:latest", - "i386": "i386/ubuntu:bionic" + "aarch64": "homeassistant/aarch64-base-ubuntu:latest", + "amd64": "homeassistant/amd64-base-ubuntu:latest", + "armhf": "homeassistant/armhf-base-ubuntu:latest", + "i386": "homeassistant/i386-base-ubuntu:latest" }, "args": {} } diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index 36ebfba424..af6c3223e0 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -1,22 +1,22 @@ from __future__ import print_function import argparse +from collections import OrderedDict import logging import os import random import sys from datetime import datetime -from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util +from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util, platformio_api from esphomeyaml.config import get_component, iter_components, read_config from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \ CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \ CONF_WIFI, ESP_PLATFORM_ESP8266 from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \ - _EXPRESSIONS, add, \ - add_job, color, flush_tasks, indent, quote, statement, relative_path -from esphomeyaml.util import safe_print + _EXPRESSIONS, add, add_job, color, flush_tasks, indent, statement, relative_path +from esphomeyaml.util import safe_print, run_external_command _LOGGER = logging.getLogger(__name__) @@ -62,34 +62,6 @@ def choose_serial_port(config): return result[opt][0] -def run_platformio(*cmd, **kwargs): - def mock_exit(return_code): - raise SystemExit(return_code) - - orig_argv = sys.argv - orig_exit = sys.exit # mock sys.exit - full_cmd = u' '.join(quote(x) for x in cmd) - _LOGGER.info(u"Running: %s", full_cmd) - try: - func = kwargs.get('main') - if func is None: - import platformio.__main__ - func = platformio.__main__.main - sys.argv = list(cmd) - sys.exit = mock_exit - return func() or 0 - except KeyboardInterrupt: - return 1 - except SystemExit as err: - return err.args[0] - except Exception as err: # pylint: disable=broad-except - _LOGGER.error(u"Running platformio failed: %s", err) - _LOGGER.error(u"Please try running %s locally.", full_cmd) - finally: - sys.argv = orig_argv - sys.exit = orig_exit - - def run_miniterm(config, port, escape=False): import serial if CONF_LOGGER not in config: @@ -100,6 +72,7 @@ def run_miniterm(config, port, escape=False): _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.") _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate) + backtrace_state = False with serial.Serial(port, baudrate=baud_rate) as ser: while True: try: @@ -114,6 +87,9 @@ def run_miniterm(config, port, escape=False): message = message.replace('\033', '\\033') safe_print(message) + backtrace_state = platformio_api.process_stacktrace( + config, line, backtrace_state=backtrace_state) + def write_cpp(config): _LOGGER.info("Generating C++ source...") @@ -154,11 +130,7 @@ def write_cpp(config): def compile_program(args, config): _LOGGER.info("Compiling app...") - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - command = ['platformio', 'run', '-d', build_path] - if args.verbose: - command.append('-v') - return run_platformio(*command) + return platformio_api.run_compile(config, args.verbose) def get_upload_host(config): @@ -176,10 +148,10 @@ def upload_using_esptool(config, port): build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') + cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', + '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] # pylint: disable=protected-access - return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset', - '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', - path, main=esptool._main) + return run_external_command(esptool._main, *cmd) def upload_program(config, args, port): @@ -190,11 +162,7 @@ def upload_program(config, args, port): if port != 'OTA' and serial_port: if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: return upload_using_esptool(config, port) - command = ['platformio', 'run', '-d', build_path, - '-t', 'upload', '--upload-port', port] - if args.verbose: - command.append('-v') - return run_platformio(*command) + return platformio_api.run_upload(config, args.verbose, port) if 'ota' not in config: _LOGGER.error("No serial port found and OTA not enabled. Can't upload!") @@ -243,7 +211,7 @@ def clean_mqtt(config, args): def setup_log(debug=False): log_level = logging.DEBUG if debug else logging.INFO logging.basicConfig(level=log_level) - fmt = "%(levelname)s [%(name)s] %(message)s" + fmt = "%(levelname)s %(message)s" colorfmt = "%(log_color)s{}%(reset)s".format(fmt) datefmt = '%H:%M:%S' @@ -367,6 +335,28 @@ def command_clean(args, config): return 0 +def command_hass_config(args, config): + from esphomeyaml.components import mqtt as mqtt_component + + _LOGGER.info("This is what you should put in your Home Assistant YAML configuration.") + _LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.") + data = mqtt_component.GenerateHassConfigData(config) + hass_config = OrderedDict() + for domain, component, conf in iter_components(config): + if not hasattr(component, 'to_hass_config'): + continue + func = getattr(component, 'to_hass_config') + ret = func(data, conf) + if not isinstance(ret, (list, tuple)): + ret = [ret] + ret = [x for x in ret if x is not None] + domain_conf = hass_config.setdefault(domain.split('.')[0], []) + domain_conf += ret + + safe_print(yaml_util.dump(hass_config)) + return 0 + + def command_dashboard(args): from esphomeyaml.dashboard import dashboard @@ -388,6 +378,7 @@ POST_CONFIG_ACTIONS = { 'clean-mqtt': command_clean_mqtt, 'mqtt-fingerprint': command_mqtt_fingerprint, 'clean': command_clean, + 'hass-config': command_hass_config, } @@ -469,6 +460,9 @@ def parse_args(argv): dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", action='store_true') + subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added" + "to Home Assistant when not using MQTT discovery.") + return parser.parse_args(argv[1:]) diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index ee3a8113dc..de32e7d39c 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -1,3 +1,5 @@ +import copy + import voluptuous as vol from esphomeyaml import core @@ -8,7 +10,8 @@ from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMAT CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, add_job, \ - esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable + esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable, PollingComponent, \ + Action, Component, Trigger from esphomeyaml.util import ServiceRegistry @@ -28,8 +31,9 @@ def validate_recursive_condition(value): def validate_recursive_action(value): - value = cv.ensure_list(value) + value = cv.ensure_list(value)[:] for i, item in enumerate(value): + item = copy.deepcopy(item) if not isinstance(item, dict): raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item)) key = next((x for x in item if x != CONF_ACTION_ID), None) @@ -41,11 +45,12 @@ def validate_recursive_action(value): item.setdefault(CONF_ACTION_ID, None) key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None) if key2 is not None: - raise vol.Invalid(u"Cannot have two actions in one item. Key {} overrides {}!" + raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! " + u"Did you forget to indent the action?" u"".format(key, key2)) validator = ACTION_REGISTRY[key][0] value[i] = { - CONF_ACTION_ID: cv.declare_variable_id(None)(item[CONF_ACTION_ID]), + CONF_ACTION_ID: cv.declare_variable_id(Action)(item[CONF_ACTION_ID]), key: validator(item[key]) } return value @@ -54,14 +59,20 @@ def validate_recursive_action(value): ACTION_REGISTRY = ServiceRegistry() # pylint: disable=invalid-name -DelayAction = esphomelib_ns.DelayAction -LambdaAction = esphomelib_ns.LambdaAction -IfAction = esphomelib_ns.IfAction -UpdateComponentAction = esphomelib_ns.UpdateComponentAction -Automation = esphomelib_ns.Automation +DelayAction = esphomelib_ns.class_('DelayAction', Action, Component) +LambdaAction = esphomelib_ns.class_('LambdaAction', Action) +IfAction = esphomelib_ns.class_('IfAction', Action) +UpdateComponentAction = esphomelib_ns.class_('UpdateComponentAction', Action) +Automation = esphomelib_ns.class_('Automation') + +Condition = esphomelib_ns.class_('Condition') +AndCondition = esphomelib_ns.class_('AndCondition', Condition) +OrCondition = esphomelib_ns.class_('OrCondition', Condition) +RangeCondition = esphomelib_ns.class_('RangeCondition', Condition) +LambdaCondition = esphomelib_ns.class_('LambdaCondition', Condition) CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({ - cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(None), + cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(Condition), vol.Optional(CONF_AND): validate_recursive_condition, vol.Optional(CONF_OR): validate_recursive_condition, vol.Optional(CONF_RANGE): vol.All(vol.Schema({ @@ -71,12 +82,6 @@ CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({ vol.Optional(CONF_LAMBDA): cv.lambda_, })]) -# pylint: disable=invalid-name -AndCondition = esphomelib_ns.AndCondition -OrCondition = esphomelib_ns.OrCondition -RangeCondition = esphomelib_ns.RangeCondition -LambdaCondition = esphomelib_ns.LambdaCondition - def validate_automation(extra_schema=None, extra_validators=None, single=False): schema = AUTOMATION_SCHEMA.extend(extra_schema or {}) @@ -91,6 +96,8 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): try: return vol.Schema([schema])(value) except vol.Invalid as err2: + if 'Unable to find action' in str(err): + raise err2 raise vol.MultipleInvalid([err, err2]) elif isinstance(value, dict): if CONF_THEN in value: @@ -113,8 +120,8 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): AUTOMATION_SCHEMA = vol.Schema({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(None), - cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(None), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger), + cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation), vol.Optional(CONF_IF): CONDITIONS_SCHEMA, vol.Required(CONF_THEN): validate_recursive_action, }) @@ -222,7 +229,7 @@ def lambda_action_to_code(config, action_id, arg_type): CONF_COMPONENT_UPDATE = 'component.update' COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(PollingComponent), }) diff --git a/esphomeyaml/components/ads1115.py b/esphomeyaml/components/ads1115.py index 3acb95a049..d8d1d9b7cd 100644 --- a/esphomeyaml/components/ads1115.py +++ b/esphomeyaml/components/ads1115.py @@ -1,22 +1,18 @@ import voluptuous as vol +from esphomeyaml.components import sensor, i2c import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.const import CONF_ADDRESS, CONF_ID +from esphomeyaml.helpers import App, Pvariable, setup_component, Component DEPENDENCIES = ['i2c'] -ADS1115Component = sensor.sensor_ns.ADS1115Component - -RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required.""" +ADS1115Component = sensor.sensor_ns.class_('ADS1115Component', Component, i2c.I2CDevice) ADS1115_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(ADS1115Component), vol.Required(CONF_ADDRESS): cv.i2c_address, - - vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE) -}) +}).extend(cv.COMPONENT_SCHEMA.schema) CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA]) @@ -24,7 +20,8 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA]) def to_code(config): for conf in config: rhs = App.make_ads1115_component(conf[CONF_ADDRESS]) - Pvariable(conf[CONF_ID], rhs) + var = Pvariable(conf[CONF_ID], rhs) + setup_component(var, conf) BUILD_FLAGS = '-DUSE_ADS1115_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/__init__.py b/esphomeyaml/components/binary_sensor/__init__.py index 32731a08ce..e4cf8a264b 100644 --- a/esphomeyaml/components/binary_sensor/__init__.py +++ b/esphomeyaml/components/binary_sensor/__init__.py @@ -1,13 +1,16 @@ import voluptuous as vol +from esphomeyaml import automation, core +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv -from esphomeyaml import automation -from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INTERNAL, CONF_INVERTED, \ - CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, CONF_ON_DOUBLE_CLICK, \ - CONF_ON_PRESS, CONF_ON_RELEASE, CONF_TRIGGER_ID, CONF_FILTERS, CONF_INVERT, CONF_DELAYED_ON, \ - CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT -from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns, \ - setup_mqtt_component, bool_, process_lambda, ArrayInitializer +from esphomeyaml.const import CONF_DELAYED_OFF, CONF_DELAYED_ON, CONF_DEVICE_CLASS, CONF_FILTERS, \ + CONF_HEARTBEAT, CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERT, CONF_INVERTED, \ + CONF_LAMBDA, CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, \ + CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_STATE, \ + CONF_TIMING, CONF_TRIGGER_ID +from esphomeyaml.helpers import App, ArrayInitializer, NoArg, Pvariable, StructInitializer, add, \ + add_job, bool_, esphomelib_ns, process_lambda, setup_mqtt_component, Nameable, Trigger, \ + Component DEVICE_CLASSES = [ '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', @@ -21,17 +24,25 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) binary_sensor_ns = esphomelib_ns.namespace('binary_sensor') -PressTrigger = binary_sensor_ns.PressTrigger -ReleaseTrigger = binary_sensor_ns.ReleaseTrigger -ClickTrigger = binary_sensor_ns.ClickTrigger -DoubleClickTrigger = binary_sensor_ns.DoubleClickTrigger -BinarySensor = binary_sensor_ns.BinarySensor -InvertFilter = binary_sensor_ns.InvertFilter -LambdaFilter = binary_sensor_ns.LambdaFilter -DelayedOnFilter = binary_sensor_ns.DelayedOnFilter -DelayedOffFilter = binary_sensor_ns.DelayedOffFilter -HeartbeatFilter = binary_sensor_ns.HeartbeatFilter -MQTTBinarySensorComponent = binary_sensor_ns.MQTTBinarySensorComponent +BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable) +MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent) + +# Triggers +PressTrigger = binary_sensor_ns.class_('PressTrigger', Trigger.template(NoArg)) +ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', Trigger.template(NoArg)) +ClickTrigger = binary_sensor_ns.class_('ClickTrigger', Trigger.template(NoArg)) +DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', Trigger.template(NoArg)) +MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', Trigger.template(NoArg), Component) +MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent') + +# Filters +Filter = binary_sensor_ns.class_('Filter') +DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, Component) +DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, Component) +HeartbeatFilter = binary_sensor_ns.class_('HeartbeatFilter', Filter, Component) +InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter) +LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) + FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT] @@ -43,9 +54,101 @@ FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_LAMBDA): cv.lambda_, }, cv.has_exactly_one_key(*FILTER_KEYS))]) +MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ + vol.Optional(CONF_STATE): cv.boolean, + vol.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds, + vol.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds, +}) + + +def parse_multi_click_timing_str(value): + if not isinstance(value, basestring): + return value + + parts = value.lower().split(' ') + if len(parts) != 5: + raise vol.Invalid("Multi click timing grammar consists of exactly 5 words, not {}" + "".format(len(parts))) + try: + state = cv.boolean(parts[0]) + except vol.Invalid: + raise vol.Invalid(u"First word must either be ON or OFF, not {}".format(parts[0])) + + if parts[1] != 'for': + raise vol.Invalid(u"Second word must be 'for', got {}".format(parts[1])) + + if parts[2] == 'at': + if parts[3] == 'least': + key = CONF_MIN_LENGTH + elif parts[3] == 'most': + key = CONF_MAX_LENGTH + else: + raise vol.Invalid(u"Third word after at must either be 'least' or 'most', got {}" + u"".format(parts[3])) + try: + length = cv.positive_time_period_milliseconds(parts[4]) + except vol.Invalid as err: + raise vol.Invalid(u"Multi Click Grammar Parsing length failed: {}".format(err)) + return { + CONF_STATE: state, + key: str(length) + } + + if parts[3] != 'to': + raise vol.Invalid("Multi click grammar: 4th word must be 'to'") + + try: + min_length = cv.positive_time_period_milliseconds(parts[2]) + except vol.Invalid as err: + raise vol.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err)) + + try: + max_length = cv.positive_time_period_milliseconds(parts[4]) + except vol.Invalid as err: + raise vol.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err)) + + return { + CONF_STATE: state, + CONF_MIN_LENGTH: str(min_length), + CONF_MAX_LENGTH: str(max_length) + } + + +def validate_multi_click_timing(value): + if not isinstance(value, list): + raise vol.Invalid("Timing option must be a *list* of times!") + timings = [] + state = None + for i, v_ in enumerate(value): + v_ = MULTI_CLICK_TIMING_SCHEMA(v_) + min_length = v_.get(CONF_MIN_LENGTH) + max_length = v_.get(CONF_MAX_LENGTH) + if min_length is None and max_length is None: + raise vol.Invalid("At least one of min_length and max_length is required!") + if min_length is None and max_length is not None: + min_length = core.TimePeriodMilliseconds(milliseconds=0) + + new_state = v_.get(CONF_STATE, not state) + if new_state == state: + raise vol.Invalid("Timings must have alternating state. Indices {} and {} have " + "the same state {}".format(i, i + 1, state)) + if max_length is not None and max_length < min_length: + raise vol.Invalid("Max length ({}) must be larger than min length ({})." + "".format(max_length, min_length)) + + state = new_state + tim = { + CONF_STATE: new_state, + CONF_MIN_LENGTH: min_length, + } + if max_length is not None: + tim[CONF_MAX_LENGTH] = max_length + timings.append(tim) + return timings + + BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent), - cv.GenerateID(): cv.declare_variable_id(BinarySensor), vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)), vol.Optional(CONF_FILTERS): FILTERS_SCHEMA, @@ -65,6 +168,12 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, }), + vol.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MultiClickTrigger), + vol.Required(CONF_TIMING): vol.All([parse_multi_click_timing_str], + validate_multi_click_timing), + vol.Optional(CONF_INVALID_COOLDOWN): cv.positive_time_period_milliseconds, + }), vol.Optional(CONF_INVERTED): cv.invalid( "The inverted binary_sensor property has been replaced by the " @@ -136,6 +245,22 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config): trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) automation.build_automation(trigger, NoArg, conf) + for conf in config.get(CONF_ON_MULTI_CLICK, []): + timings = [] + for tim in conf[CONF_TIMING]: + timings.append(StructInitializer( + MultiClickTriggerEvent, + ('state', tim[CONF_STATE]), + ('min_length', tim[CONF_MIN_LENGTH]), + ('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)), + )) + timings = ArrayInitializer(*timings, multiline=False) + rhs = App.register_component(binary_sensor_var.make_multi_click_trigger(timings)) + trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + if CONF_INVALID_COOLDOWN in conf: + add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) + automation.build_automation(trigger, NoArg, conf) + setup_mqtt_component(mqtt_var, config) @@ -154,4 +279,14 @@ def register_binary_sensor(var, config): add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'binary_sensor', config, + include_state=True, include_command=False) + if ret is None: + return None + if CONF_DEVICE_CLASS in config: + ret['device_class'] = config[CONF_DEVICE_CLASS] + return ret + + BUILD_FLAGS = '-DUSE_BINARY_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py index 8e4737602c..63e08a0baf 100644 --- a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py +++ b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py @@ -1,15 +1,17 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ make_address_array +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME -from esphomeyaml.helpers import get_variable +from esphomeyaml.helpers import esphomelib_ns, get_variable DEPENDENCIES = ['esp32_ble_tracker'] +ESP32BLEPresenceDevice = esphomelib_ns.class_('ESP32BLEPresenceDevice', binary_sensor.BinarySensor) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ESP32BLEPresenceDevice), vol.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker) })) @@ -21,3 +23,7 @@ def to_code(config): yield rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/esp32_touch.py b/esphomeyaml/components/binary_sensor/esp32_touch.py index c22f6150da..fcf01e664c 100644 --- a/esphomeyaml/components/binary_sensor/esp32_touch.py +++ b/esphomeyaml/components/binary_sensor/esp32_touch.py @@ -34,7 +34,11 @@ def validate_touch_pad(value): return value +ESP32TouchBinarySensor = binary_sensor.binary_sensor_ns.class_('ESP32TouchBinarySensor', + binary_sensor.BinarySensor) + PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ESP32TouchBinarySensor), vol.Required(CONF_PIN): validate_touch_pad, vol.Required(CONF_THRESHOLD): cv.uint16_t, cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent), @@ -51,3 +55,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/gpio.py b/esphomeyaml/components/binary_sensor/gpio.py index 4bca5fd329..80a6d2b487 100644 --- a/esphomeyaml/components/binary_sensor/gpio.py +++ b/esphomeyaml/components/binary_sensor/gpio.py @@ -4,14 +4,19 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import binary_sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN -from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application +from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application, \ + setup_component, Component -MakeGPIOBinarySensor = Application.MakeGPIOBinarySensor +MakeGPIOBinarySensor = Application.struct('MakeGPIOBinarySensor') +GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent', + binary_sensor.BinarySensor, + Component) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(GPIOBinarySensorComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor), vol.Required(CONF_PIN): pins.gpio_input_pin_schema -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): @@ -21,6 +26,11 @@ def to_code(config): rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin) gpio = variable(config[CONF_MAKE_ID], rhs) binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config) + setup_component(gpio.Pgpio, config) BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/nextion.py b/esphomeyaml/components/binary_sensor/nextion.py index 5f6b1529b5..162392d223 100644 --- a/esphomeyaml/components/binary_sensor/nextion.py +++ b/esphomeyaml/components/binary_sensor/nextion.py @@ -1,8 +1,8 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml.components import binary_sensor +from esphomeyaml.components import binary_sensor, display from esphomeyaml.components.display.nextion import Nextion +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID from esphomeyaml.helpers import get_variable @@ -10,7 +10,11 @@ DEPENDENCIES = ['display'] CONF_NEXTION_ID = 'nextion_id' +NextionTouchComponent = display.display_ns.class_('NextionTouchComponent', + binary_sensor.BinarySensor) + PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(NextionTouchComponent), vol.Required(CONF_PAGE_ID): cv.uint8_t, vol.Required(CONF_COMPONENT_ID): cv.uint8_t, cv.GenerateID(CONF_NEXTION_ID): cv.use_variable_id(Nextion) @@ -24,3 +28,7 @@ def to_code(config): rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID], config[CONF_COMPONENT_ID]) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/pn532.py b/esphomeyaml/components/binary_sensor/pn532.py index 6ae202a1e7..38031b650c 100644 --- a/esphomeyaml/components/binary_sensor/pn532.py +++ b/esphomeyaml/components/binary_sensor/pn532.py @@ -27,16 +27,23 @@ def validate_uid(value): return value +PN532BinarySensor = binary_sensor.binary_sensor_ns.class_('PN532BinarySensor', + binary_sensor.BinarySensor) + PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(PN532BinarySensor), vol.Required(CONF_UID): validate_uid, cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532Component) })) def to_code(config): - hub = None for hub in get_variable(config[CONF_PN532_ID]): yield addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False)) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/rdm6300.py b/esphomeyaml/components/binary_sensor/rdm6300.py index e1e4214011..c4a11e4f31 100644 --- a/esphomeyaml/components/binary_sensor/rdm6300.py +++ b/esphomeyaml/components/binary_sensor/rdm6300.py @@ -9,15 +9,22 @@ DEPENDENCIES = ['rdm6300'] CONF_RDM6300_ID = 'rdm6300_id' +RDM6300BinarySensor = binary_sensor.binary_sensor_ns.class_('RDM6300BinarySensor', + binary_sensor.BinarySensor) + PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(RDM6300BinarySensor), vol.Required(CONF_UID): cv.uint32_t, cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(rdm6300.RDM6300Component) })) def to_code(config): - hub = None for hub in get_variable(config[CONF_RDM6300_ID]): yield rhs = hub.make_card(config[CONF_NAME], config[CONF_UID]) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/remote_receiver.py b/esphomeyaml/components/binary_sensor/remote_receiver.py index 9e1cf50d99..4d66caca44 100644 --- a/esphomeyaml/components/binary_sensor/remote_receiver.py +++ b/esphomeyaml/components/binary_sensor/remote_receiver.py @@ -22,20 +22,21 @@ REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_SAMSUNG, CONF_ CONF_REMOTE_RECEIVER_ID = 'remote_receiver_id' CONF_RECEIVER_ID = 'receiver_id' -RemoteReceiver = remote_ns.RemoteReceiver -LGReceiver = remote_ns.LGReceiver -NECReceiver = remote_ns.NECReceiver -PanasonicReceiver = remote_ns.PanasonicReceiver -RawReceiver = remote_ns.RawReceiver -SamsungReceiver = remote_ns.SamsungReceiver -SonyReceiver = remote_ns.SonyReceiver -RCSwitchRawReceiver = remote_ns.RCSwitchRawReceiver -RCSwitchTypeAReceiver = remote_ns.RCSwitchTypeAReceiver -RCSwitchTypeBReceiver = remote_ns.RCSwitchTypeBReceiver -RCSwitchTypeCReceiver = remote_ns.RCSwitchTypeCReceiver -RCSwitchTypeDReceiver = remote_ns.RCSwitchTypeDReceiver +RemoteReceiver = remote_ns.class_('RemoteReceiver', binary_sensor.BinarySensor) +LGReceiver = remote_ns.class_('LGReceiver', RemoteReceiver) +NECReceiver = remote_ns.class_('NECReceiver', RemoteReceiver) +PanasonicReceiver = remote_ns.class_('PanasonicReceiver', RemoteReceiver) +RawReceiver = remote_ns.class_('RawReceiver', RemoteReceiver) +SamsungReceiver = remote_ns.class_('SamsungReceiver', RemoteReceiver) +SonyReceiver = remote_ns.class_('SonyReceiver', RemoteReceiver) +RCSwitchRawReceiver = remote_ns.class_('RCSwitchRawReceiver', RemoteReceiver) +RCSwitchTypeAReceiver = remote_ns.class_('RCSwitchTypeAReceiver', RCSwitchRawReceiver) +RCSwitchTypeBReceiver = remote_ns.class_('RCSwitchTypeBReceiver', RCSwitchRawReceiver) +RCSwitchTypeCReceiver = remote_ns.class_('RCSwitchTypeCReceiver', RCSwitchRawReceiver) +RCSwitchTypeDReceiver = remote_ns.class_('RCSwitchTypeDReceiver', RCSwitchRawReceiver) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(RemoteReceiver), vol.Optional(CONF_LG): vol.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)), @@ -109,7 +110,6 @@ def receiver_base(full_config): def to_code(config): - remote = None for remote in get_variable(config[CONF_REMOTE_RECEIVER_ID]): yield rhs = receiver_base(config) @@ -119,3 +119,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/status.py b/esphomeyaml/components/binary_sensor/status.py index fb3169217a..5fca06cf93 100644 --- a/esphomeyaml/components/binary_sensor/status.py +++ b/esphomeyaml/components/binary_sensor/status.py @@ -1,21 +1,30 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.helpers import App, Application, variable, setup_component, Component DEPENDENCIES = ['mqtt'] -MakeStatusBinarySensor = Application.MakeStatusBinarySensor +MakeStatusBinarySensor = Application.struct('MakeStatusBinarySensor') +StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor', + binary_sensor.BinarySensor, + Component) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor), -})) + cv.GenerateID(): cv.declare_variable_id(StatusBinarySensor), +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_status_binary_sensor(config[CONF_NAME]) status = variable(config[CONF_MAKE_ID], rhs) binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config) + setup_component(status.Pstatus, config) BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/template.py b/esphomeyaml/components/binary_sensor/template.py index 43beaeb752..3369b50aab 100644 --- a/esphomeyaml/components/binary_sensor/template.py +++ b/esphomeyaml/components/binary_sensor/template.py @@ -1,22 +1,28 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, process_lambda, variable, optional, bool_, add +from esphomeyaml.helpers import App, Application, add, bool_, optional, process_lambda, variable, \ + setup_component, Component -MakeTemplateBinarySensor = Application.MakeTemplateBinarySensor +MakeTemplateBinarySensor = Application.struct('MakeTemplateBinarySensor') +TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor', + binary_sensor.BinarySensor, + Component) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateBinarySensor), vol.Required(CONF_LAMBDA): cv.lambda_, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_template_binary_sensor(config[CONF_NAME]) make = variable(config[CONF_MAKE_ID], rhs) binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config) + setup_component(make.Ptemplate_, config) template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], @@ -26,3 +32,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/cover/__init__.py b/esphomeyaml/components/cover/__init__.py index 650542963e..a121ce9b08 100644 --- a/esphomeyaml/components/cover/__init__.py +++ b/esphomeyaml/components/cover/__init__.py @@ -1,24 +1,29 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \ - TemplateArguments, get_variable + TemplateArguments, get_variable, Action, Nameable PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) cover_ns = esphomelib_ns.namespace('cover') -Cover = cover_ns.Cover -MQTTCoverComponent = cover_ns.MQTTCoverComponent -CoverState = cover_ns.CoverState + +Cover = cover_ns.class_('Cover', Nameable) +MQTTCoverComponent = cover_ns.class_('MQTTCoverComponent', mqtt.MQTTComponent) + +CoverState = cover_ns.class_('CoverState') COVER_OPEN = cover_ns.COVER_OPEN COVER_CLOSED = cover_ns.COVER_CLOSED -OpenAction = cover_ns.OpenAction -CloseAction = cover_ns.CloseAction -StopAction = cover_ns.StopAction + +# Actions +OpenAction = cover_ns.class_('OpenAction', Action) +CloseAction = cover_ns.class_('CloseAction', Action) +StopAction = cover_ns.class_('StopAction', Action) COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(Cover), @@ -44,7 +49,7 @@ BUILD_FLAGS = '-DUSE_COVER' CONF_COVER_OPEN = 'cover.open' COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Cover), }) @@ -60,7 +65,7 @@ def cover_open_to_code(config, action_id, arg_type): CONF_COVER_CLOSE = 'cover.close' COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Cover), }) @@ -76,7 +81,7 @@ def cover_close_to_code(config, action_id, arg_type): CONF_COVER_STOP = 'cover.stop' COVER_STOP_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Cover), }) @@ -88,3 +93,10 @@ def cover_stop_to_code(config, action_id, arg_type): rhs = var.make_stop_action(template_arg) type = StopAction.template(arg_type) yield Pvariable(action_id, rhs, type=type) + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'cover', config, include_state=True, include_command=True) + if ret is None: + return None + return ret diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index df9c0f30b5..564c13ceb4 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -5,18 +5,21 @@ from esphomeyaml import automation from esphomeyaml.components import cover from esphomeyaml.const import CONF_CLOSE_ACTION, CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, \ CONF_OPEN_ACTION, CONF_STOP_ACTION, CONF_OPTIMISTIC -from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable, optional +from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable, optional, \ + setup_component -MakeTemplateCover = Application.MakeTemplateCover +MakeTemplateCover = Application.struct('MakeTemplateCover') +TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover) PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateCover), + cv.GenerateID(): cv.declare_variable_id(TemplateCover), vol.Optional(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_OPTIMISTIC): cv.boolean, vol.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), vol.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), -}), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) def to_code(config): @@ -24,9 +27,9 @@ def to_code(config): make = variable(config[CONF_MAKE_ID], rhs) cover.setup_cover(make.Ptemplate_, make.Pmqtt, config) + setup_component(make.Ptemplate_, config) if CONF_LAMBDA in config: - template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(cover.CoverState)): yield @@ -45,3 +48,12 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_COVER' + + +def to_hass_config(data, config): + ret = cover.core_to_hass_config(data, config) + if ret is None: + return None + if CONF_OPTIMISTIC in config: + ret['optimistic'] = config[CONF_OPTIMISTIC] + return ret diff --git a/esphomeyaml/components/dallas.py b/esphomeyaml/components/dallas.py index 22c4ac3ee3..eae9fa9c82 100644 --- a/esphomeyaml/components/dallas.py +++ b/esphomeyaml/components/dallas.py @@ -4,21 +4,22 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.helpers import App, Pvariable, setup_component, PollingComponent -DallasComponent = sensor.sensor_ns.DallasComponent +DallasComponent = sensor.sensor_ns.class_('DallasComponent', PollingComponent) CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ cv.GenerateID(): cv.declare_variable_id(DallasComponent), vol.Required(CONF_PIN): pins.input_output_pin, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})]) +}).extend(cv.COMPONENT_SCHEMA.schema)]) def to_code(config): for conf in config: rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL)) - Pvariable(conf[CONF_ID], rhs) + var = Pvariable(conf[CONF_ID], rhs) + setup_component(var, conf) BUILD_FLAGS = '-DUSE_DALLAS_SENSOR' diff --git a/esphomeyaml/components/deep_sleep.py b/esphomeyaml/components/deep_sleep.py index 7dc3ecdf42..4b5f57c2f5 100644 --- a/esphomeyaml/components/deep_sleep.py +++ b/esphomeyaml/components/deep_sleep.py @@ -1,11 +1,12 @@ import voluptuous as vol from esphomeyaml import config_validation as cv, pins -from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \ - CONF_SLEEP_DURATION, CONF_WAKEUP_PIN -from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns, \ - TemplateArguments, get_variable + CONF_SLEEP_DURATION, CONF_WAKEUP_PIN, CONF_MODE, CONF_PINS +from esphomeyaml.helpers import Action, App, Component, Pvariable, TemplateArguments, add, \ + esphomelib_ns, get_variable, gpio_input_pin_expression, setup_component, global_ns, \ + StructInitializer def validate_pin_number(value): @@ -16,17 +17,26 @@ def validate_pin_number(value): return value -DeepSleepComponent = esphomelib_ns.DeepSleepComponent -EnterDeepSleepAction = esphomelib_ns.EnterDeepSleepAction -PreventDeepSleepAction = esphomelib_ns.PreventDeepSleepAction +DeepSleepComponent = esphomelib_ns.class_('DeepSleepComponent', Component) +EnterDeepSleepAction = esphomelib_ns.class_('EnterDeepSleepAction', Action) +PreventDeepSleepAction = esphomelib_ns.class_('PreventDeepSleepAction', Action) +WakeupPinMode = esphomelib_ns.enum('WakeupPinMode') WAKEUP_PIN_MODES = { - 'IGNORE': esphomelib_ns.WAKEUP_PIN_MODE_IGNORE, - 'KEEP_AWAKE': esphomelib_ns.WAKEUP_PIN_MODE_KEEP_AWAKE, - 'INVERT_WAKEUP': esphomelib_ns.WAKEUP_PIN_MODE_INVERT_WAKEUP, + 'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE, + 'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE, + 'INVERT_WAKEUP': WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP, +} + +esp_sleep_ext1_wakeup_mode_t = global_ns.enum('esp_sleep_ext1_wakeup_mode_t') +Ext1Wakeup = esphomelib_ns.struct('Ext1Wakeup') +EXT1_WAKEUP_MODES = { + 'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW, + 'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH, } CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode' +CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup' CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent), @@ -35,9 +45,14 @@ CONFIG_SCHEMA = vol.Schema({ validate_pin_number), vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, vol.Upper, cv.one_of(*WAKEUP_PIN_MODES)), + vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, vol.Schema({ + vol.Required(CONF_PINS): vol.All(cv.ensure_list, [pins.shorthand_input_pin], + [validate_pin_number]), + vol.Required(CONF_MODE): vol.All(vol.Upper, cv.one_of(*EXT1_WAKEUP_MODES)), + })), vol.Optional(CONF_RUN_CYCLES): cv.positive_int, vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -46,7 +61,6 @@ def to_code(config): if CONF_SLEEP_DURATION in config: add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION])) if CONF_WAKEUP_PIN in config: - pin = None for pin in gpio_input_pin_expression(config[CONF_WAKEUP_PIN]): yield add(deep_sleep.set_wakeup_pin(pin)) @@ -57,10 +71,23 @@ def to_code(config): if CONF_RUN_DURATION in config: add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION])) + if CONF_ESP32_EXT1_WAKEUP in config: + conf = config[CONF_ESP32_EXT1_WAKEUP] + mask = 0 + for pin in conf[CONF_PINS]: + mask |= 1 << pin[CONF_NUMBER] + struct = StructInitializer( + Ext1Wakeup, + ('mask', mask), + ('wakeup_mode', EXT1_WAKEUP_MODES[conf[CONF_MODE]]) + ) + add(deep_sleep.set_ext1_wakeup(struct)) + + setup_component(deep_sleep, config) + BUILD_FLAGS = '-DUSE_DEEP_SLEEP' - CONF_DEEP_SLEEP_ENTER = 'deep_sleep.enter' DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent), diff --git a/esphomeyaml/components/display/__init__.py b/esphomeyaml/components/display/__init__.py index cada701f87..ffcaabecb6 100644 --- a/esphomeyaml/components/display/__init__.py +++ b/esphomeyaml/components/display/__init__.py @@ -10,7 +10,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) display_ns = esphomelib_ns.namespace('display') -DisplayBuffer = display_ns.DisplayBuffer +DisplayBuffer = display_ns.class_('DisplayBuffer') DisplayBufferRef = DisplayBuffer.operator('ref') DISPLAY_ROTATIONS = { diff --git a/esphomeyaml/components/display/lcd_gpio.py b/esphomeyaml/components/display/lcd_gpio.py index 66cc021c5f..d36ac780e8 100644 --- a/esphomeyaml/components/display/lcd_gpio.py +++ b/esphomeyaml/components/display/lcd_gpio.py @@ -5,11 +5,12 @@ from esphomeyaml import pins from esphomeyaml.components import display from esphomeyaml.const import CONF_DATA_PINS, CONF_DIMENSIONS, CONF_ENABLE_PIN, CONF_ID, \ CONF_LAMBDA, CONF_RS_PIN, CONF_RW_PIN -from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda +from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda, \ + setup_component, PollingComponent -GPIOLCDDisplay = display.display_ns.GPIOLCDDisplay -LCDDisplay = display.display_ns.LCDDisplay +LCDDisplay = display.display_ns.class_('LCDDisplay', PollingComponent) LCDDisplayRef = LCDDisplay.operator('ref') +GPIOLCDDisplay = display.display_ns.class_('GPIOLCDDisplay', LCDDisplay) def validate_lcd_dimensions(value): @@ -36,7 +37,7 @@ PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({ vol.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_RS_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -67,6 +68,7 @@ def to_code(config): add(lcd.set_writer(lambda_)) display.setup_display(lcd, config) + setup_component(lcd, config) BUILD_FLAGS = '-DUSE_LCD_DISPLAY' diff --git a/esphomeyaml/components/display/lcd_pcf8574.py b/esphomeyaml/components/display/lcd_pcf8574.py index a2e9e28eac..761203c212 100644 --- a/esphomeyaml/components/display/lcd_pcf8574.py +++ b/esphomeyaml/components/display/lcd_pcf8574.py @@ -1,20 +1,21 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import display -from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions +from esphomeyaml.components import display, i2c +from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions, \ + LCDDisplay from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA -from esphomeyaml.helpers import App, Pvariable, add, process_lambda +from esphomeyaml.helpers import App, Pvariable, add, process_lambda, setup_component DEPENDENCIES = ['i2c'] -PCF8574LCDDisplay = display.display_ns.PCF8574LCDDisplay +PCF8574LCDDisplay = display.display_ns.class_('PCF8574LCDDisplay', LCDDisplay, i2c.I2CDevice) PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(PCF8574LCDDisplay), vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions, vol.Optional(CONF_ADDRESS): cv.i2c_address, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -30,6 +31,7 @@ def to_code(config): add(lcd.set_writer(lambda_)) display.setup_display(lcd, config) + setup_component(lcd, config) BUILD_FLAGS = ['-DUSE_LCD_DISPLAY', '-DUSE_LCD_DISPLAY_PCF8574'] diff --git a/esphomeyaml/components/display/max7219.py b/esphomeyaml/components/display/max7219.py index 24256bfb32..b5bf380c4d 100644 --- a/esphomeyaml/components/display/max7219.py +++ b/esphomeyaml/components/display/max7219.py @@ -2,16 +2,16 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins -from esphomeyaml.components import display +from esphomeyaml.components import display, spi from esphomeyaml.components.spi import SPIComponent from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, \ CONF_SPI_ID from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression, \ - process_lambda + process_lambda, setup_component, PollingComponent DEPENDENCIES = ['spi'] -MAX7219Component = display.display_ns.MAX7219Component +MAX7219Component = display.display_ns.class_('MAX7219Component', PollingComponent, spi.SPIDevice) MAX7219ComponentRef = MAX7219Component.operator('ref') PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({ @@ -21,15 +21,15 @@ PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NUM_CHIPS): vol.All(cv.uint8_t, vol.Range(min=1)), vol.Optional(CONF_INTENSITY): vol.All(cv.uint8_t, vol.Range(min=0, max=15)), -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for spi in get_variable(config[CONF_SPI_ID]): + for spi_ in get_variable(config[CONF_SPI_ID]): yield for cs in gpio_output_pin_expression(config[CONF_CS_PIN]): yield - rhs = App.make_max7219(spi, cs) + rhs = App.make_max7219(spi_, cs) max7219 = Pvariable(config[CONF_ID], rhs) if CONF_NUM_CHIPS in config: @@ -43,6 +43,7 @@ def to_code(config): add(max7219.set_writer(lambda_)) display.setup_display(max7219, config) + setup_component(max7219, config) BUILD_FLAGS = '-DUSE_MAX7219' diff --git a/esphomeyaml/components/display/nextion.py b/esphomeyaml/components/display/nextion.py index 44e69c74cf..9f26647f96 100644 --- a/esphomeyaml/components/display/nextion.py +++ b/esphomeyaml/components/display/nextion.py @@ -1,24 +1,26 @@ -import esphomeyaml.config_validation as cv -from esphomeyaml.components import display +from esphomeyaml.components import display, uart from esphomeyaml.components.uart import UARTComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_UART_ID -from esphomeyaml.helpers import App, Pvariable, add, get_variable, process_lambda +from esphomeyaml.helpers import App, PollingComponent, Pvariable, add, get_variable, \ + process_lambda, \ + setup_component DEPENDENCIES = ['uart'] -Nextion = display.display_ns.Nextion +Nextion = display.display_ns.class_('Nextion', PollingComponent, uart.UARTDevice) NextionRef = Nextion.operator('ref') PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(Nextion), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for uart in get_variable(config[CONF_UART_ID]): + for uart_ in get_variable(config[CONF_UART_ID]): yield - rhs = App.make_nextion(uart) + rhs = App.make_nextion(uart_) nextion = Pvariable(config[CONF_ID], rhs) if CONF_LAMBDA in config: @@ -27,6 +29,7 @@ def to_code(config): add(nextion.set_writer(lambda_)) display.setup_display(nextion, config) + setup_component(nextion, config) BUILD_FLAGS = '-DUSE_NEXTION' diff --git a/esphomeyaml/components/display/ssd1306_i2c.py b/esphomeyaml/components/display/ssd1306_i2c.py index be19b119c0..a80ba8d5b5 100644 --- a/esphomeyaml/components/display/ssd1306_i2c.py +++ b/esphomeyaml/components/display/ssd1306_i2c.py @@ -7,11 +7,11 @@ from esphomeyaml.components.display import ssd1306_spi from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, \ CONF_MODEL, CONF_RESET_PIN, CONF_LAMBDA from esphomeyaml.helpers import App, Pvariable, add, \ - gpio_output_pin_expression, process_lambda + gpio_output_pin_expression, process_lambda, setup_component DEPENDENCIES = ['i2c'] -I2CSSD1306 = display.display_ns.I2CSSD1306 +I2CSSD1306 = display.display_ns.class_('I2CSSD1306', ssd1306_spi.SSD1306) PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(I2CSSD1306), @@ -19,7 +19,7 @@ PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_EXTERNAL_VCC): cv.boolean, vol.Optional(CONF_ADDRESS): cv.i2c_address, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -41,6 +41,7 @@ def to_code(config): add(ssd.set_writer(lambda_)) display.setup_display(ssd, config) + setup_component(ssd, config) BUILD_FLAGS = '-DUSE_SSD1306' diff --git a/esphomeyaml/components/display/ssd1306_spi.py b/esphomeyaml/components/display/ssd1306_spi.py index b08e0a3b00..bf47a5e92a 100644 --- a/esphomeyaml/components/display/ssd1306_spi.py +++ b/esphomeyaml/components/display/ssd1306_spi.py @@ -2,27 +2,29 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins -from esphomeyaml.components import display +from esphomeyaml.components import display, spi from esphomeyaml.components.spi import SPIComponent from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, \ CONF_ID, CONF_MODEL, \ CONF_RESET_PIN, CONF_SPI_ID, CONF_LAMBDA from esphomeyaml.helpers import App, Pvariable, add, get_variable, \ - gpio_output_pin_expression, process_lambda + gpio_output_pin_expression, process_lambda, setup_component, PollingComponent DEPENDENCIES = ['spi'] -SPISSD1306 = display.display_ns.SPISSD1306 +SSD1306 = display.display_ns.class_('SSD1306', PollingComponent, display.DisplayBuffer) +SPISSD1306 = display.display_ns.class_('SPISSD1306', SSD1306, spi.SPIDevice) +SSD1306Model = display.display_ns.enum('SSD1306Model') MODELS = { - 'SSD1306_128X32': display.display_ns.SSD1306_MODEL_128_32, - 'SSD1306_128X64': display.display_ns.SSD1306_MODEL_128_64, - 'SSD1306_96X16': display.display_ns.SSD1306_MODEL_96_16, - 'SSD1306_64X48': display.display_ns.SSD1306_MODEL_64_48, - 'SH1106_128X32': display.display_ns.SH1106_MODEL_128_32, - 'SH1106_128X64': display.display_ns.SH1106_MODEL_128_64, - 'SH1106_96X16': display.display_ns.SH1106_MODEL_96_16, - 'SH1106_64X48': display.display_ns.SH1106_MODEL_64_48, + 'SSD1306_128X32': SSD1306Model.SSD1306_MODEL_128_32, + 'SSD1306_128X64': SSD1306Model.SSD1306_MODEL_128_64, + 'SSD1306_96X16': SSD1306Model.SSD1306_MODEL_96_16, + 'SSD1306_64X48': SSD1306Model.SSD1306_MODEL_64_48, + 'SH1106_128X32': SSD1306Model.SH1106_MODEL_128_32, + 'SH1106_128X64': SSD1306Model.SH1106_MODEL_128_64, + 'SH1106_96X16': SSD1306Model.SH1106_MODEL_96_16, + 'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48, } SSD1306_MODEL = vol.All(vol.Upper, vol.Replace(' ', '_'), cv.one_of(*MODELS)) @@ -35,18 +37,18 @@ PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ vol.Required(CONF_MODEL): SSD1306_MODEL, vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_EXTERNAL_VCC): cv.boolean, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for spi in get_variable(config[CONF_SPI_ID]): + for spi_ in get_variable(config[CONF_SPI_ID]): yield for cs in gpio_output_pin_expression(config[CONF_CS_PIN]): yield for dc in gpio_output_pin_expression(config[CONF_DC_PIN]): yield - rhs = App.make_spi_ssd1306(spi, cs, dc) + rhs = App.make_spi_ssd1306(spi_, cs, dc) ssd = Pvariable(config[CONF_ID], rhs) add(ssd.set_model(MODELS[config[CONF_MODEL]])) @@ -63,6 +65,7 @@ def to_code(config): add(ssd.set_writer(lambda_)) display.setup_display(ssd, config) + setup_component(ssd, config) BUILD_FLAGS = '-DUSE_SSD1306' diff --git a/esphomeyaml/components/display/waveshare_epaper.py b/esphomeyaml/components/display/waveshare_epaper.py index 823f268349..51723440cf 100644 --- a/esphomeyaml/components/display/waveshare_epaper.py +++ b/esphomeyaml/components/display/waveshare_epaper.py @@ -2,25 +2,30 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins -from esphomeyaml.components import display +from esphomeyaml.components import display, spi from esphomeyaml.components.spi import SPIComponent from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \ CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \ - gpio_output_pin_expression, process_lambda + gpio_output_pin_expression, process_lambda, setup_component, PollingComponent DEPENDENCIES = ['spi'] WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA -WaveshareEPaper = display.display_ns.WaveshareEPaper +WaveshareEPaper = display.display_ns.class_('WaveshareEPaper', + PollingComponent, spi.SPIDevice, display.DisplayBuffer) + + +WaveshareEPaperTypeAModel = display.display_ns.enum('WaveshareEPaperTypeAModel') +WaveshareEPaperTypeBModel = display.display_ns.enum('WaveshareEPaperTypeBModel') MODELS = { - '1.54in': ('a', display.display_ns.WAVESHARE_EPAPER_1_54_IN), - '2.13in': ('a', display.display_ns.WAVESHARE_EPAPER_2_13_IN), - '2.90in': ('a', display.display_ns.WAVESHARE_EPAPER_2_9_IN), - '2.70in': ('b', display.display_ns.WAVESHARE_EPAPER_2_7_IN), - '4.20in': ('b', display.display_ns.WAVESHARE_EPAPER_4_2_IN), - '7.50in': ('b', display.display_ns.WAVESHARE_EPAPER_7_5_IN), + '1.54in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), + '2.13in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), + '2.90in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), + '2.70in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_2_7_IN), + '4.20in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_4_2_IN), + '7.50in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_7_5_IN), } @@ -34,7 +39,7 @@ def validate_full_update_every_only_type_a(value): PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(None), + cv.GenerateID(): cv.declare_variable_id(WaveshareEPaper), cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, @@ -42,11 +47,11 @@ PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, -}), validate_full_update_every_only_type_a) +}).extend(cv.COMPONENT_SCHEMA.schema), validate_full_update_every_only_type_a) def to_code(config): - for spi in get_variable(config[CONF_SPI_ID]): + for spi_ in get_variable(config[CONF_SPI_ID]): yield for cs in gpio_output_pin_expression(config[CONF_CS_PIN]): yield @@ -55,10 +60,10 @@ def to_code(config): model_type, model = MODELS[config[CONF_MODEL]] if model_type == 'a': - rhs = App.make_waveshare_epaper_type_a(spi, cs, dc, model) + rhs = App.make_waveshare_epaper_type_a(spi_, cs, dc, model) epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaperTypeA) elif model_type == 'b': - rhs = App.make_waveshare_epaper_type_b(spi, cs, dc, model) + rhs = App.make_waveshare_epaper_type_b(spi_, cs, dc, model) epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaper) else: raise NotImplementedError() @@ -79,6 +84,7 @@ def to_code(config): add(epaper.set_full_update_every(config[CONF_FULL_UPDATE_EVERY])) display.setup_display(epaper, config) + setup_component(epaper, config) BUILD_FLAGS = '-DUSE_WAVESHARE_EPAPER' diff --git a/esphomeyaml/components/esp32_ble.py b/esphomeyaml/components/esp32_ble.py deleted file mode 100644 index adf60a7290..0000000000 --- a/esphomeyaml/components/esp32_ble.py +++ /dev/null @@ -1,5 +0,0 @@ -from esphomeyaml import config_validation as cv - -CONFIG_SCHEMA = cv.invalid("The 'esp32_ble' component has been renamed to the 'esp32_ble_tracker' " - "component in order to avoid confusion with the new 'esp32_ble_beacon' " - "component.") diff --git a/esphomeyaml/components/esp32_ble_beacon.py b/esphomeyaml/components/esp32_ble_beacon.py index c10b0a0fc7..2d9e8d37a4 100644 --- a/esphomeyaml/components/esp32_ble_beacon.py +++ b/esphomeyaml/components/esp32_ble_beacon.py @@ -1,12 +1,13 @@ import voluptuous as vol from esphomeyaml import config_validation as cv -from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32, CONF_UUID, CONF_TYPE -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, RawExpression, ArrayInitializer +from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32 +from esphomeyaml.helpers import App, ArrayInitializer, Component, Pvariable, RawExpression, add, \ + esphomelib_ns, setup_component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -ESP32BLEBeacon = esphomelib_ns.ESP32BLEBeacon +ESP32BLEBeacon = esphomelib_ns.class_('ESP32BLEBeacon', Component) CONF_MAJOR = 'major' CONF_MINOR = 'minor' @@ -18,12 +19,12 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_MAJOR): cv.uint16_t, vol.Optional(CONF_MINOR): cv.uint16_t, vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): uuid = config[CONF_UUID].hex - uuid_arr = [RawExpression('0x{}'.format(uuid[i:i+2])) for i in range(0, len(uuid), 2)] + uuid_arr = [RawExpression('0x{}'.format(uuid[i:i + 2])) for i in range(0, len(uuid), 2)] rhs = App.make_esp32_ble_beacon(ArrayInitializer(*uuid_arr, multiline=False)) ble = Pvariable(config[CONF_ID], rhs) if CONF_MAJOR in config: @@ -31,5 +32,7 @@ def to_code(config): if CONF_MINOR in config: add(ble.set_minor(config[CONF_MINOR])) + setup_component(ble, config) + BUILD_FLAGS = '-DUSE_ESP32_BLE_BEACON' diff --git a/esphomeyaml/components/esp32_ble_tracker.py b/esphomeyaml/components/esp32_ble_tracker.py index 600d31bd97..40be12590a 100644 --- a/esphomeyaml/components/esp32_ble_tracker.py +++ b/esphomeyaml/components/esp32_ble_tracker.py @@ -1,19 +1,26 @@ import voluptuous as vol from esphomeyaml import config_validation as cv +from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32 from esphomeyaml.core import HexInt -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer +from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer, \ + setup_component, Component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] CONF_ESP32_BLE_ID = 'esp32_ble_id' -ESP32BLETracker = esphomelib_ns.ESP32BLETracker +ESP32BLETracker = esphomelib_ns.class_('ESP32BLETracker', Component) +XiaomiSensor = esphomelib_ns.class_('XiaomiSensor', sensor.Sensor) +XiaomiDevice = esphomelib_ns.class_('XiaomiDevice') +XIAOMI_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(XiaomiSensor) +}) CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker), vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def make_address_array(address): @@ -27,5 +34,7 @@ def to_code(config): if CONF_SCAN_INTERVAL in config: add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL])) + setup_component(ble, config) + BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER' diff --git a/esphomeyaml/components/esp32_touch.py b/esphomeyaml/components/esp32_touch.py index 5804a0863e..0591e1e92f 100644 --- a/esphomeyaml/components/esp32_touch.py +++ b/esphomeyaml/components/esp32_touch.py @@ -6,7 +6,7 @@ from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \ CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \ CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32 from esphomeyaml.core import TimePeriod -from esphomeyaml.helpers import App, Pvariable, add, global_ns +from esphomeyaml.helpers import App, Pvariable, add, global_ns, setup_component, Component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] @@ -41,7 +41,7 @@ VOLTAGE_ATTENUATION = { '0V': global_ns.TOUCH_HVOLT_ATTEN_0V, } -ESP32TouchComponent = binary_sensor.binary_sensor_ns.ESP32TouchComponent +ESP32TouchComponent = binary_sensor.binary_sensor_ns.class_('ESP32TouchComponent', Component) CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent), @@ -54,7 +54,7 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_LOW_VOLTAGE_REFERENCE): validate_voltage(LOW_VOLTAGE_REFERENCE), vol.Optional(CONF_HIGH_VOLTAGE_REFERENCE): validate_voltage(HIGH_VOLTAGE_REFERENCE), vol.Optional(CONF_VOLTAGE_ATTENUATION): validate_voltage(VOLTAGE_ATTENUATION), -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -80,5 +80,7 @@ def to_code(config): value = VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]] add(touch.set_voltage_attenuation(value)) + setup_component(touch, config) + BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR' diff --git a/esphomeyaml/components/fan/__init__.py b/esphomeyaml/components/fan/__init__.py index efdbff4087..9874c4114f 100644 --- a/esphomeyaml/components/fan/__init__.py +++ b/esphomeyaml/components/fan/__init__.py @@ -1,29 +1,33 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \ CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \ - CONF_SPEED, CONF_OSCILLATING + CONF_SPEED, CONF_OSCILLATING, CONF_OSCILLATION_OUTPUT, CONF_NAME from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ - TemplateArguments, get_variable, templatable, bool_ + TemplateArguments, get_variable, templatable, bool_, Action, Nameable, Component PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) fan_ns = esphomelib_ns.namespace('fan') -FanState = fan_ns.FanState -MQTTFanComponent = fan_ns.MQTTFanComponent -MakeFan = Application.MakeFan -TurnOnAction = fan_ns.TurnOnAction -TurnOffAction = fan_ns.TurnOffAction -ToggleAction = fan_ns.ToggleAction -FanSpeed = fan_ns.FanSpeed -FAN_SPEED_OFF = fan_ns.FAN_SPEED_OFF -FAN_SPEED_LOW = fan_ns.FAN_SPEED_LOW -FAN_SPEED_MEDIUM = fan_ns.FAN_SPEED_MEDIUM -FAN_SPEED_HIGH = fan_ns.FAN_SPEED_HIGH +FanState = fan_ns.class_('FanState', Nameable, Component) +MQTTFanComponent = fan_ns.class_('MQTTFanComponent', mqtt.MQTTComponent) +MakeFan = Application.struct('MakeFan') + +# Actions +TurnOnAction = fan_ns.class_('TurnOnAction', Action) +TurnOffAction = fan_ns.class_('TurnOffAction', Action) +ToggleAction = fan_ns.class_('ToggleAction', Action) + +FanSpeed = fan_ns.enum('FanSpeed') +FAN_SPEED_OFF = FanSpeed.FAN_SPEED_OFF +FAN_SPEED_LOW = FanSpeed.FAN_SPEED_LOW +FAN_SPEED_MEDIUM = FanSpeed.FAN_SPEED_MEDIUM +FAN_SPEED_HIGH = FanSpeed.FAN_SPEED_HIGH FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(FanState), @@ -73,7 +77,7 @@ BUILD_FLAGS = '-DUSE_FAN' CONF_FAN_TOGGLE = 'fan.toggle' FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(FanState), }) @@ -89,7 +93,7 @@ def fan_toggle_to_code(config, action_id, arg_type): CONF_FAN_TURN_OFF = 'fan.turn_off' FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(FanState), }) @@ -105,7 +109,7 @@ def fan_turn_off_to_code(config, action_id, arg_type): CONF_FAN_TURN_ON = 'fan.turn_on' FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(FanState), vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), vol.Optional(CONF_SPEED): cv.templatable(validate_fan_speed), }) @@ -128,3 +132,15 @@ def fan_turn_on_to_code(config, action_id, arg_type): yield None add(action.set_speed(template_)) yield action + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'fan', config, include_state=True, include_command=True) + if ret is None: + return None + if CONF_OSCILLATION_OUTPUT in config: + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/state') + ret['oscillation_state_topic'] = config.get(CONF_OSCILLATION_STATE_TOPIC, default) + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/command') + ret['oscillation_command__topic'] = config.get(CONF_OSCILLATION_COMMAND_TOPIC, default) + return ret diff --git a/esphomeyaml/components/fan/binary.py b/esphomeyaml/components/fan/binary.py index 05c833dbdd..1ce51c2a0b 100644 --- a/esphomeyaml/components/fan/binary.py +++ b/esphomeyaml/components/fan/binary.py @@ -1,29 +1,32 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import fan +from esphomeyaml.components import fan, output from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT -from esphomeyaml.helpers import App, add, get_variable, variable +from esphomeyaml.helpers import App, add, get_variable, variable, setup_component PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), - vol.Required(CONF_OUTPUT): cv.use_variable_id(None), - vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None), -})) + vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), + vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput), +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - output = None - for output in get_variable(config[CONF_OUTPUT]): + for output_ in get_variable(config[CONF_OUTPUT]): yield rhs = App.make_fan(config[CONF_NAME]) fan_struct = variable(config[CONF_MAKE_ID], rhs) - add(fan_struct.Poutput.set_binary(output)) + add(fan_struct.Poutput.set_binary(output_)) if CONF_OSCILLATION_OUTPUT in config: - oscillation_output = None for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]): yield add(fan_struct.Poutput.set_oscillation(oscillation_output)) fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) + setup_component(fan_struct.Poutput, config) + + +def to_hass_config(data, config): + return fan.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/fan/speed.py b/esphomeyaml/components/fan/speed.py index f0bee8258a..d1259d3ae1 100644 --- a/esphomeyaml/components/fan/speed.py +++ b/esphomeyaml/components/fan/speed.py @@ -1,7 +1,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import fan +from esphomeyaml.components import fan, mqtt, output from esphomeyaml.const import CONF_HIGH, CONF_LOW, CONF_MAKE_ID, CONF_MEDIUM, CONF_NAME, \ CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, \ CONF_SPEED_STATE_TOPIC @@ -9,37 +9,46 @@ from esphomeyaml.helpers import App, add, get_variable, variable PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), - vol.Required(CONF_OUTPUT): cv.use_variable_id(None), + vol.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput), vol.Optional(CONF_SPEED_STATE_TOPIC): cv.publish_topic, vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic, - vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None), + vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput), vol.Optional(CONF_SPEED): vol.Schema({ vol.Required(CONF_LOW): cv.percentage, vol.Required(CONF_MEDIUM): cv.percentage, vol.Required(CONF_HIGH): cv.percentage, }), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - output = None - for output in get_variable(config[CONF_OUTPUT]): + for output_ in get_variable(config[CONF_OUTPUT]): yield rhs = App.make_fan(config[CONF_NAME]) fan_struct = variable(config[CONF_MAKE_ID], rhs) if CONF_SPEED in config: speeds = config[CONF_SPEED] - add(fan_struct.Poutput.set_speed(output, + add(fan_struct.Poutput.set_speed(output_, speeds[CONF_LOW], speeds[CONF_MEDIUM], speeds[CONF_HIGH])) else: - add(fan_struct.Poutput.set_speed(output)) + add(fan_struct.Poutput.set_speed(output_)) if CONF_OSCILLATION_OUTPUT in config: - oscillation_output = None for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]): yield add(fan_struct.Poutput.set_oscillation(oscillation_output)) fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) + + +def to_hass_config(data, config): + ret = fan.core_to_hass_config(data, config) + if ret is None: + return None + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/state') + ret['speed_state_topic'] = config.get(CONF_SPEED_STATE_TOPIC, default) + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/command') + ret['speed_command__topic'] = config.get(CONF_SPEED_COMMAND_TOPIC, default) + return ret diff --git a/esphomeyaml/components/font.py b/esphomeyaml/components/font.py index f18336a0a0..1456c3e717 100644 --- a/esphomeyaml/components/font.py +++ b/esphomeyaml/components/font.py @@ -11,8 +11,8 @@ from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawEx DEPENDENCIES = ['display'] -Font = display.display_ns.Font -Glyph = display.display_ns.Glyph +Font = display.display_ns.class_('Font') +Glyph = display.display_ns.class_('Glyph') def validate_glyphs(value): diff --git a/esphomeyaml/components/globals.py b/esphomeyaml/components/globals.py new file mode 100644 index 0000000000..d9a6cf0978 --- /dev/null +++ b/esphomeyaml/components/globals.py @@ -0,0 +1,35 @@ +import voluptuous as vol + +from esphomeyaml import config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_INITIAL_VALUE, CONF_RESTORE_VALUE, CONF_TYPE +from esphomeyaml.helpers import App, Component, Pvariable, RawExpression, TemplateArguments, add, \ + esphomelib_ns, setup_component + +GlobalVariableComponent = esphomelib_ns.class_('GlobalVariableComponent', Component) + +GLOBAL_VAR_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.declare_variable_id(GlobalVariableComponent), + vol.Required(CONF_TYPE): cv.string_strict, + vol.Optional(CONF_INITIAL_VALUE): cv.string_strict, + vol.Optional(CONF_RESTORE_VALUE): cv.boolean, +}).extend(cv.COMPONENT_SCHEMA.schema) + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [GLOBAL_VAR_SCHEMA]) + + +def to_code(config): + for conf in config: + type_ = RawExpression(conf[CONF_TYPE]) + template_args = TemplateArguments(type_) + res_type = GlobalVariableComponent.template(template_args) + initial_value = None + if CONF_INITIAL_VALUE in conf: + initial_value = RawExpression(conf[CONF_INITIAL_VALUE]) + rhs = App.Pmake_global_variable(template_args, initial_value) + glob = Pvariable(conf[CONF_ID], rhs, type=res_type) + + if conf.get(CONF_RESTORE_VALUE, False): + hash_ = hash(conf[CONF_ID].id) % 2**32 + add(glob.set_restore_value(hash_)) + + setup_component(glob, conf) diff --git a/esphomeyaml/components/i2c.py b/esphomeyaml/components/i2c.py index 647530b25b..133b3031c6 100644 --- a/esphomeyaml/components/i2c.py +++ b/esphomeyaml/components/i2c.py @@ -4,9 +4,10 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \ CONF_RECEIVE_TIMEOUT -from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns +from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns, setup_component, Component -I2CComponent = esphomelib_ns.I2CComponent +I2CComponent = esphomelib_ns.class_('I2CComponent', Component) +I2CDevice = pins.I2CDevice CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(I2CComponent), @@ -18,7 +19,7 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_RECEIVE_TIMEOUT): cv.invalid("The receive_timeout option has been removed " "because timeouts are already handled by the " "low-level i2c interface.") -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -27,6 +28,8 @@ def to_code(config): if CONF_FREQUENCY in config: add(i2c.set_frequency(config[CONF_FREQUENCY])) + setup_component(i2c, config) + BUILD_FLAGS = '-DUSE_I2C' diff --git a/esphomeyaml/components/image.py b/esphomeyaml/components/image.py index acb99bbb19..ac0c95be2d 100644 --- a/esphomeyaml/components/image.py +++ b/esphomeyaml/components/image.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['display'] -Image_ = display.display_ns.Image +Image_ = display.display_ns.class_('Image') CONF_RAW_DATA_ID = 'raw_data_id' diff --git a/esphomeyaml/components/ir_transmitter.py b/esphomeyaml/components/ir_transmitter.py deleted file mode 100644 index 6e50f78870..0000000000 --- a/esphomeyaml/components/ir_transmitter.py +++ /dev/null @@ -1,6 +0,0 @@ -import voluptuous as vol - - -def CONFIG_SCHEMA(config): - raise vol.Invalid("The ir_transmitter component has been renamed to " - "remote_transmitter because of 433MHz signal support.") diff --git a/esphomeyaml/components/light/__init__.py b/esphomeyaml/components/light/__init__.py index ad427b643c..c74376b2fb 100644 --- a/esphomeyaml/components/light/__init__.py +++ b/esphomeyaml/components/light/__init__.py @@ -1,6 +1,7 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \ @@ -10,36 +11,48 @@ from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLOR CONF_EFFECT from esphomeyaml.helpers import Application, ArrayInitializer, Pvariable, RawExpression, \ StructInitializer, add, add_job, esphomelib_ns, process_lambda, setup_mqtt_component, \ - get_variable, TemplateArguments, templatable, uint32, float_, std_string + get_variable, TemplateArguments, templatable, uint32, float_, std_string, Nameable, Component, \ + Action PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) +# Base light_ns = esphomelib_ns.namespace('light') -LightState = light_ns.LightState -LightColorValues = light_ns.LightColorValues -MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent -ToggleAction = light_ns.ToggleAction -TurnOffAction = light_ns.TurnOffAction -TurnOnAction = light_ns.TurnOnAction -MakeLight = Application.MakeLight -RandomLightEffect = light_ns.RandomLightEffect -LambdaLightEffect = light_ns.LambdaLightEffect -StrobeLightEffect = light_ns.StrobeLightEffect -StrobeLightEffectColor = light_ns.StrobeLightEffectColor -FlickerLightEffect = light_ns.FlickerLightEffect -FastLEDLambdaLightEffect = light_ns.FastLEDLambdaLightEffect -FastLEDRainbowLightEffect = light_ns.FastLEDRainbowLightEffect -FastLEDColorWipeEffect = light_ns.FastLEDColorWipeEffect -FastLEDColorWipeEffectColor = light_ns.FastLEDColorWipeEffectColor -FastLEDScanEffect = light_ns.FastLEDScanEffect -FastLEDScanEffectColor = light_ns.FastLEDScanEffectColor -FastLEDTwinkleEffect = light_ns.FastLEDTwinkleEffect -FastLEDRandomTwinkleEffect = light_ns.FastLEDRandomTwinkleEffect -FastLEDFireworksEffect = light_ns.FastLEDFireworksEffect -FastLEDFlickerEffect = light_ns.FastLEDFlickerEffect -FastLEDLightOutputComponent = light_ns.FastLEDLightOutputComponent +LightState = light_ns.class_('LightState', Nameable, Component) +MakeLight = Application.struct('MakeLight') +LightOutput = light_ns.class_('LightOutput') +FastLEDLightOutputComponent = light_ns.class_('FastLEDLightOutputComponent', LightOutput) + +# Actions +ToggleAction = light_ns.class_('ToggleAction', Action) +TurnOffAction = light_ns.class_('TurnOffAction', Action) +TurnOnAction = light_ns.class_('TurnOnAction', Action) + +LightColorValues = light_ns.class_('LightColorValues') + + +MQTTJSONLightComponent = light_ns.class_('MQTTJSONLightComponent', mqtt.MQTTComponent) + +# Effects +LightEffect = light_ns.class_('LightEffect') +RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect) +LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect) +StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect) +StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect) +FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect) +BaseFastLEDLightEffect = light_ns.class_('BaseFastLEDLightEffect', LightEffect) +FastLEDLambdaLightEffect = light_ns.class_('FastLEDLambdaLightEffect', BaseFastLEDLightEffect) +FastLEDRainbowLightEffect = light_ns.class_('FastLEDRainbowLightEffect', BaseFastLEDLightEffect) +FastLEDColorWipeEffect = light_ns.class_('FastLEDColorWipeEffect', BaseFastLEDLightEffect) +FastLEDColorWipeEffectColor = light_ns.class_('FastLEDColorWipeEffectColor', BaseFastLEDLightEffect) +FastLEDScanEffect = light_ns.class_('FastLEDScanEffect', BaseFastLEDLightEffect) +FastLEDScanEffectColor = light_ns.class_('FastLEDScanEffectColor', BaseFastLEDLightEffect) +FastLEDTwinkleEffect = light_ns.class_('FastLEDTwinkleEffect', BaseFastLEDLightEffect) +FastLEDRandomTwinkleEffect = light_ns.class_('FastLEDRandomTwinkleEffect', BaseFastLEDLightEffect) +FastLEDFireworksEffect = light_ns.class_('FastLEDFireworksEffect', BaseFastLEDLightEffect) +FastLEDFlickerEffect = light_ns.class_('FastLEDFlickerEffect', BaseFastLEDLightEffect) CONF_STROBE = 'strobe' CONF_FLICKER = 'flicker' @@ -344,7 +357,7 @@ BUILD_FLAGS = '-DUSE_LIGHT' CONF_LIGHT_TOGGLE = 'light.toggle' LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(LightState), vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), }) @@ -366,7 +379,7 @@ def light_toggle_to_code(config, action_id, arg_type): CONF_LIGHT_TURN_OFF = 'light.turn_off' LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(LightState), vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), }) @@ -388,7 +401,7 @@ def light_turn_off_to_code(config, action_id, arg_type): CONF_LIGHT_TURN_ON = 'light.turn_on' LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(LightState), vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'): cv.templatable(cv.positive_time_period_milliseconds), vol.Exclusive(CONF_FLASH_LENGTH, 'transformer'): @@ -448,3 +461,24 @@ def light_turn_on_to_code(config, action_id, arg_type): yield None add(action.set_effect(template_)) yield action + + +def core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True, + white_value=True): + ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True, + platform='mqtt_json') + if ret is None: + return None + if brightness: + ret['brightness'] = True + if rgb: + ret['rgb'] = True + if color_temp: + ret['color_temp'] = True + if white_value: + ret['white_value'] = True + for effect in config.get(CONF_EFFECTS, []): + ret["effect"] = True + effects = ret.setdefault("effect_list", []) + effects.append(next(x for x in effect.values())[CONF_NAME]) + return ret diff --git a/esphomeyaml/components/light/binary.py b/esphomeyaml/components/light/binary.py index 5468d54d32..55beec807a 100644 --- a/esphomeyaml/components/light/binary.py +++ b/esphomeyaml/components/light/binary.py @@ -1,21 +1,26 @@ import voluptuous as vol +from esphomeyaml.components import light, output import esphomeyaml.config_validation as cv -from esphomeyaml.components import light -from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT, CONF_EFFECTS -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT +from esphomeyaml.helpers import App, get_variable, setup_component, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_OUTPUT): cv.use_variable_id(None), + vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), vol.Optional(CONF_EFFECTS): light.validate_effects(light.BINARY_EFFECTS), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - output = None - for output in get_variable(config[CONF_OUTPUT]): + for output_ in get_variable(config[CONF_OUTPUT]): yield - rhs = App.make_binary_light(config[CONF_NAME], output) + rhs = App.make_binary_light(config[CONF_NAME], output_) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=False, rgb=False, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/cwww.py b/esphomeyaml/components/light/cwww.py index f14557c4bc..d21139b5a0 100644 --- a/esphomeyaml/components/light/cwww.py +++ b/esphomeyaml/components/light/cwww.py @@ -1,25 +1,25 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import light +from esphomeyaml.components import light, output from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \ validate_color_temperature from esphomeyaml.const import CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \ CONF_NAME, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.helpers import App, get_variable, variable, setup_component PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_COLD_WHITE): cv.use_variable_id(None), - vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None), + vol.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput), vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature, vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS), -}), validate_cold_white_colder) +}).extend(cv.COMPONENT_SCHEMA.schema), validate_cold_white_colder) def to_code(config): @@ -32,3 +32,9 @@ def to_code(config): cold_white, warm_white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=True, + white_value=False) diff --git a/esphomeyaml/components/light/fastled_clockless.py b/esphomeyaml/components/light/fastled_clockless.py index 767703c152..c779f4ceed 100644 --- a/esphomeyaml/components/light/fastled_clockless.py +++ b/esphomeyaml/components/light/fastled_clockless.py @@ -8,7 +8,7 @@ from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \ CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ - get_variable, variable + get_variable, variable, setup_component TYPES = [ 'NEOPIXEL', @@ -53,7 +53,7 @@ def validate(value): return value -MakeFastLEDLight = Application.MakeFastLEDLight +MakeFastLEDLight = Application.struct('MakeFastLEDLight') PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), @@ -70,7 +70,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), -}), validate) +}).extend(cv.COMPONENT_SCHEMA.schema), validate) def to_code(config): @@ -89,7 +89,6 @@ def to_code(config): add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE])) if CONF_POWER_SUPPLY in config: - power_supply = None for power_supply in get_variable(config[CONF_POWER_SUPPLY]): yield add(fast_led.set_power_supply(power_supply)) @@ -99,6 +98,12 @@ def to_code(config): add(fast_led.set_correction(r, g, b)) light.setup_light(make.Pstate, make.Pmqtt, config) + setup_component(fast_led, config) BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/fastled_spi.py b/esphomeyaml/components/light/fastled_spi.py index a74a435d5b..bd4df644e3 100644 --- a/esphomeyaml/components/light/fastled_spi.py +++ b/esphomeyaml/components/light/fastled_spi.py @@ -8,7 +8,7 @@ from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \ CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ - get_variable, variable + get_variable, variable, setup_component CHIPSETS = [ 'LPD8806', @@ -30,7 +30,7 @@ RGB_ORDERS = [ 'BGR', ] -MakeFastLEDLight = Application.MakeFastLEDLight +MakeFastLEDLight = Application.struct('MakeFastLEDLight') PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), @@ -48,7 +48,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): @@ -69,7 +69,6 @@ def to_code(config): add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE])) if CONF_POWER_SUPPLY in config: - power_supply = None for power_supply in get_variable(config[CONF_POWER_SUPPLY]): yield add(fast_led.set_power_supply(power_supply)) @@ -79,6 +78,12 @@ def to_code(config): add(fast_led.set_correction(r, g, b)) light.setup_light(make.Pstate, make.Pmqtt, config) + setup_component(fast_led, config) BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/monochromatic.py b/esphomeyaml/components/light/monochromatic.py index 9ab8b3d53a..bed9f65d37 100644 --- a/esphomeyaml/components/light/monochromatic.py +++ b/esphomeyaml/components/light/monochromatic.py @@ -1,24 +1,29 @@ import voluptuous as vol +from esphomeyaml.components import light, output import esphomeyaml.config_validation as cv -from esphomeyaml.components import light -from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \ - CONF_NAME, CONF_OUTPUT, CONF_EFFECTS -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, \ + CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT +from esphomeyaml.helpers import App, get_variable, setup_component, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_OUTPUT): cv.use_variable_id(None), + vol.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - output = None - for output in get_variable(config[CONF_OUTPUT]): + for output_ in get_variable(config[CONF_OUTPUT]): yield - rhs = App.make_monochromatic_light(config[CONF_NAME], output) + rhs = App.make_monochromatic_light(config[CONF_NAME], output_) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/rgb.py b/esphomeyaml/components/light/rgb.py index 7f2f360f5d..556ba9b241 100644 --- a/esphomeyaml/components/light/rgb.py +++ b/esphomeyaml/components/light/rgb.py @@ -1,32 +1,35 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import light +from esphomeyaml.components import light, output from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.helpers import App, get_variable, variable, setup_component PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_RED): cv.use_variable_id(None), - vol.Required(CONF_GREEN): cv.use_variable_id(None), - vol.Required(CONF_BLUE): cv.use_variable_id(None), + vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - red = None for red in get_variable(config[CONF_RED]): yield - green = None for green in get_variable(config[CONF_GREEN]): yield - blue = None for blue in get_variable(config[CONF_BLUE]): yield rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/rgbw.py b/esphomeyaml/components/light/rgbw.py index edd8919eb8..07631ca71b 100644 --- a/esphomeyaml/components/light/rgbw.py +++ b/esphomeyaml/components/light/rgbw.py @@ -1,36 +1,38 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import light +from esphomeyaml.components import light, output from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.helpers import App, get_variable, variable, setup_component PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_RED): cv.use_variable_id(None), - vol.Required(CONF_GREEN): cv.use_variable_id(None), - vol.Required(CONF_BLUE): cv.use_variable_id(None), - vol.Required(CONF_WHITE): cv.use_variable_id(None), + vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_WHITE): cv.use_variable_id(output.FloatOutput), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - red = None for red in get_variable(config[CONF_RED]): yield - green = None for green in get_variable(config[CONF_GREEN]): yield - blue = None for blue in get_variable(config[CONF_BLUE]): yield - white = None for white in get_variable(config[CONF_WHITE]): yield rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=True) diff --git a/esphomeyaml/components/light/rgbww.py b/esphomeyaml/components/light/rgbww.py index fe69993698..fa7a92d6ff 100644 --- a/esphomeyaml/components/light/rgbww.py +++ b/esphomeyaml/components/light/rgbww.py @@ -1,11 +1,11 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import light +from esphomeyaml.components import light, output from esphomeyaml.const import CONF_BLUE, CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, \ CONF_NAME, CONF_RED, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.helpers import App, get_variable, variable, setup_component def validate_color_temperature(value): @@ -30,18 +30,18 @@ def validate_cold_white_colder(value): PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), - vol.Required(CONF_RED): cv.use_variable_id(None), - vol.Required(CONF_GREEN): cv.use_variable_id(None), - vol.Required(CONF_BLUE): cv.use_variable_id(None), - vol.Required(CONF_COLD_WHITE): cv.use_variable_id(None), - vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None), + vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput), + vol.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput), vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature, vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS), -}), validate_cold_white_colder) +}).extend(cv.COMPONENT_SCHEMA.schema), validate_cold_white_colder) def to_code(config): @@ -60,3 +60,9 @@ def to_code(config): red, green, blue, cold_white, warm_white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + setup_component(light_struct.Pstate, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True, + white_value=True) diff --git a/esphomeyaml/components/logger.py b/esphomeyaml/components/logger.py index 996732df4c..839d371d56 100644 --- a/esphomeyaml/components/logger.py +++ b/esphomeyaml/components/logger.py @@ -7,8 +7,8 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_ID, CONF_LEVEL, \ CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE from esphomeyaml.core import ESPHomeYAMLError, Lambda -from esphomeyaml.helpers import App, Pvariable, TemplateArguments, add, esphomelib_ns, global_ns, \ - process_lambda, RawExpression, statement +from esphomeyaml.helpers import App, Pvariable, RawExpression, TemplateArguments, add, \ + esphomelib_ns, global_ns, process_lambda, statement, Component LOG_LEVELS = { 'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE, @@ -44,7 +44,7 @@ def validate_local_no_higher_than_global(value): return value -LogComponent = esphomelib_ns.LogComponent +LogComponent = esphomelib_ns.class_('LogComponent', Component) CONFIG_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(LogComponent), @@ -79,6 +79,7 @@ def maybe_simple_message(schema): if isinstance(value, dict): return vol.Schema(schema)(value) return vol.Schema(schema)({CONF_FORMAT: value}) + return validator diff --git a/esphomeyaml/components/mqtt.py b/esphomeyaml/components/mqtt.py index 143f0aa4ad..646ddc0e73 100644 --- a/esphomeyaml/components/mqtt.py +++ b/esphomeyaml/components/mqtt.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import re import voluptuous as vol @@ -10,10 +11,13 @@ from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, C CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \ CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \ CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \ - CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE + CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE, \ + CONF_STATE_TOPIC, CONF_MQTT, CONF_ESPHOMEYAML, CONF_NAME, CONF_AVAILABILITY, \ + CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_INTERNAL +from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \ StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \ - uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef + uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef, Component, Action, Trigger def validate_message_just_topic(value): @@ -34,12 +38,14 @@ MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({ })) mqtt_ns = esphomelib_ns.namespace('mqtt') -MQTTMessage = mqtt_ns.MQTTMessage -MQTTClientComponent = mqtt_ns.MQTTClientComponent -MQTTPublishAction = mqtt_ns.MQTTPublishAction -MQTTPublishJsonAction = mqtt_ns.MQTTPublishJsonAction -MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger -MQTTJsonMessageTrigger = mqtt_ns.MQTTJsonMessageTrigger +MQTTMessage = mqtt_ns.struct('MQTTMessage') +MQTTClientComponent = mqtt_ns.class_('MQTTClientComponent', Component) +MQTTPublishAction = mqtt_ns.class_('MQTTPublishAction', Action) +MQTTPublishJsonAction = mqtt_ns.class_('MQTTPublishJsonAction', Action) +MQTTMessageTrigger = mqtt_ns.class_('MQTTMessageTrigger', Trigger.template(std_string)) +MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger', + Trigger.template(JsonObjectConstRef)) +MQTTComponent = mqtt_ns.class_('MQTTComponent', Component) def validate_broker(value): @@ -239,3 +245,66 @@ def required_build_flags(config): if CONF_SSL_FINGERPRINTS in config: return '-DASYNC_TCP_SSL_ENABLED=1' return None + + +def get_default_topic_for(data, component_type, name, suffix): + whitelist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' + sanitized_name = ''.join(x for x in name.lower().replace(' ', '_') if x in whitelist) + return '{}/{}/{}/{}'.format(data.topic_prefix, component_type, + sanitized_name, suffix) + + +def build_hass_config(data, component_type, config, include_state=True, include_command=True, + platform='mqtt'): + if config.get(CONF_INTERNAL, False): + return None + ret = OrderedDict() + ret['platform'] = platform + ret['name'] = config[CONF_NAME] + if include_state: + default = get_default_topic_for(data, component_type, config[CONF_NAME], 'state') + ret['state_topic'] = config.get(CONF_STATE_TOPIC, default) + if include_command: + default = get_default_topic_for(data, component_type, config[CONF_NAME], 'command') + ret['command_topic'] = config.get(CONF_STATE_TOPIC, default) + avail = config.get(CONF_AVAILABILITY, data.availability) + if avail: + ret['availability_topic'] = avail[CONF_TOPIC] + payload_available = avail[CONF_PAYLOAD_AVAILABLE] + if payload_available != 'online': + ret['payload_available'] = payload_available + payload_not_available = avail[CONF_PAYLOAD_NOT_AVAILABLE] + if payload_not_available != 'offline': + ret['payload_not_available'] = payload_not_available + return ret + + +class GenerateHassConfigData(object): + def __init__(self, config): + if 'mqtt' not in config: + raise ESPHomeYAMLError("Cannot generate Home Assistant MQTT config if MQTT is not " + "used!") + mqtt = config[CONF_MQTT] + self.topic_prefix = mqtt.get(CONF_TOPIC_PREFIX, config[CONF_ESPHOMEYAML][CONF_NAME]) + birth_message = mqtt.get(CONF_BIRTH_MESSAGE) + if CONF_BIRTH_MESSAGE not in mqtt: + birth_message = { + CONF_TOPIC: self.topic_prefix + '/status', + CONF_PAYLOAD: 'online', + } + will_message = mqtt.get(CONF_WILL_MESSAGE) + if CONF_WILL_MESSAGE not in mqtt: + will_message = { + CONF_TOPIC: self.topic_prefix + '/status', + CONF_PAYLOAD: 'offline' + } + if not birth_message or not will_message: + self.availability = None + elif birth_message[CONF_TOPIC] != will_message[CONF_TOPIC]: + self.availability = None + else: + self.availability = { + CONF_TOPIC: birth_message[CONF_TOPIC], + CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD], + CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD], + } diff --git a/esphomeyaml/components/my9231.py b/esphomeyaml/components/my9231.py new file mode 100644 index 0000000000..a52e1b1033 --- /dev/null +++ b/esphomeyaml/components/my9231.py @@ -0,0 +1,52 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.components import output +from esphomeyaml.const import (CONF_DATA_PIN, CONF_CLOCK_PIN, CONF_NUM_CHANNELS, + CONF_NUM_CHIPS, CONF_BIT_DEPTH, CONF_ID, + CONF_UPDATE_ON_BOOT) +from esphomeyaml.helpers import (gpio_output_pin_expression, App, Pvariable, + add, setup_component, Component) + +MY9231OutputComponent = output.output_ns.class_('MY9231OutputComponent', Component) + + +MY9231_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(MY9231OutputComponent), + vol.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + vol.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + vol.Optional(CONF_NUM_CHANNELS): vol.All(vol.Coerce(int), + vol.Range(3, 1020)), + vol.Optional(CONF_NUM_CHIPS): vol.All(vol.Coerce(int), + vol.Range(1, 255)), + vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), + cv.one_of(8, 12, 14, 16)), + vol.Optional(CONF_UPDATE_ON_BOOT): vol.Coerce(bool), +}).extend(cv.COMPONENT_SCHEMA.schema) + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [MY9231_SCHEMA]) + + +def to_code(config): + for conf in config: + di = None + for di in gpio_output_pin_expression(conf[CONF_DATA_PIN]): + yield + dcki = None + for dcki in gpio_output_pin_expression(conf[CONF_CLOCK_PIN]): + yield + rhs = App.make_my9231_component(di, dcki) + my9231 = Pvariable(conf[CONF_ID], rhs) + if CONF_NUM_CHANNELS in conf: + add(my9231.set_num_channels(conf[CONF_NUM_CHANNELS])) + if CONF_NUM_CHIPS in conf: + add(my9231.set_num_chips(conf[CONF_NUM_CHIPS])) + if CONF_BIT_DEPTH in conf: + add(my9231.set_bit_depth(conf[CONF_BIT_DEPTH])) + if CONF_UPDATE_ON_BOOT in conf: + add(my9231.set_update(conf[CONF_UPDATE_ON_BOOT])) + setup_component(my9231, conf) + + +BUILD_FLAGS = '-DUSE_MY9231_OUTPUT' diff --git a/esphomeyaml/components/ota.py b/esphomeyaml/components/ota.py index e7052d60e0..83b224cbed 100644 --- a/esphomeyaml/components/ota.py +++ b/esphomeyaml/components/ota.py @@ -7,16 +7,15 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \ ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns +from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, Component _LOGGER = logging.getLogger(__name__) -OTAComponent = esphomelib_ns.OTAComponent +OTAComponent = esphomelib_ns.class_('OTAComponent', Component) CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(OTAComponent), vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean, - # TODO Num attempts + wait time vol.Optional(CONF_PORT): cv.port, vol.Optional(CONF_PASSWORD): cv.string, }) diff --git a/esphomeyaml/components/output/__init__.py b/esphomeyaml/components/output/__init__.py index 33e275dd34..6456bd0991 100644 --- a/esphomeyaml/components/output/__init__.py +++ b/esphomeyaml/components/output/__init__.py @@ -5,7 +5,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components.power_supply import PowerSupplyComponent from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL from esphomeyaml.helpers import add, esphomelib_ns, get_variable, TemplateArguments, Pvariable, \ - templatable, bool_ + templatable, float_, add_job, Action PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -25,9 +25,13 @@ FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({ FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema) output_ns = esphomelib_ns.namespace('output') -TurnOffAction = output_ns.TurnOffAction -TurnOnAction = output_ns.TurnOnAction -SetLevelAction = output_ns.SetLevelAction +BinaryOutput = output_ns.class_('BinaryOutput') +FloatOutput = output_ns.class_('FloatOutput', BinaryOutput) + +# Actions +TurnOffAction = output_ns.class_('TurnOffAction', Action) +TurnOnAction = output_ns.class_('TurnOnAction', Action) +SetLevelAction = output_ns.class_('SetLevelAction', Action) def setup_output_platform_(obj, config, skip_power_supply=False): @@ -43,37 +47,20 @@ def setup_output_platform_(obj, config, skip_power_supply=False): def setup_output_platform(obj, config, skip_power_supply=False): - for _ in setup_output_platform_(obj, config, skip_power_supply): - yield + add_job(setup_output_platform_, obj, config, skip_power_supply) BUILD_FLAGS = '-DUSE_OUTPUT' CONF_OUTPUT_TURN_ON = 'output.turn_on' -OUTPUT_TURN_OFF_ACTION = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), -}) - - -@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_OFF_ACTION) -def output_turn_on_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) - for var in get_variable(config[CONF_ID]): - yield None - rhs = var.make_turn_off_action(template_arg) - type = TurnOffAction.template(arg_type) - yield Pvariable(action_id, rhs, type=type) - - -CONF_OUTPUT_TURN_OFF = 'output.turn_off' OUTPUT_TURN_ON_ACTION = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None) + vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput), }) -@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_ON_ACTION) -def output_turn_off_to_code(config, action_id, arg_type): +@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_ON_ACTION) +def output_turn_on_to_code(config, action_id, arg_type): template_arg = TemplateArguments(arg_type) for var in get_variable(config[CONF_ID]): yield None @@ -82,10 +69,26 @@ def output_turn_off_to_code(config, action_id, arg_type): yield Pvariable(action_id, rhs, type=type) +CONF_OUTPUT_TURN_OFF = 'output.turn_off' +OUTPUT_TURN_OFF_ACTION = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput) +}) + + +@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_OFF_ACTION) +def output_turn_off_to_code(config, action_id, arg_type): + template_arg = TemplateArguments(arg_type) + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_turn_off_action(template_arg) + type = TurnOffAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) + + CONF_OUTPUT_SET_LEVEL = 'output.set_level' OUTPUT_SET_LEVEL_ACTION = vol.Schema({ - vol.Required(CONF_ID): cv.use_variable_id(None), - vol.Required(CONF_LEVEL): cv.percentage, + vol.Required(CONF_ID): cv.use_variable_id(FloatOutput), + vol.Required(CONF_LEVEL): cv.templatable(cv.percentage), }) @@ -97,7 +100,7 @@ def output_set_level_to_code(config, action_id, arg_type): rhs = var.make_set_level_action(template_arg) type = SetLevelAction.template(arg_type) action = Pvariable(action_id, rhs, type=type) - for template_ in templatable(config[CONF_LEVEL], arg_type, bool_): + for template_ in templatable(config[CONF_LEVEL], arg_type, float_): yield None add(action.set_level(template_)) yield action diff --git a/esphomeyaml/components/output/esp8266_pwm.py b/esphomeyaml/components/output/esp8266_pwm.py index 84eae94241..c28673742a 100644 --- a/esphomeyaml/components/output/esp8266_pwm.py +++ b/esphomeyaml/components/output/esp8266_pwm.py @@ -1,36 +1,41 @@ import voluptuous as vol from esphomeyaml import pins -import esphomeyaml.config_validation as cv from esphomeyaml.components import output -from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266 -from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_FREQUENCY +from esphomeyaml.helpers import App, Component, Pvariable, gpio_output_pin_expression, \ + setup_component, add ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] def valid_pwm_pin(value): - if value[CONF_NUMBER] > 16: - raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.") + num = value[CONF_NUMBER] + cv.one_of(0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16)(num) return value -ESP8266PWMOutput = output.output_ns.ESP8266PWMOutput +ESP8266PWMOutput = output.output_ns.class_('ESP8266PWMOutput', output.FloatOutput, Component) PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_ID): cv.declare_variable_id(ESP8266PWMOutput), vol.Required(CONF_PIN): vol.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin), -}) + vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=1.0e-6)), +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - pin = None for pin in gpio_output_pin_expression(config[CONF_PIN]): yield rhs = App.make_esp8266_pwm_output(pin) gpio = Pvariable(config[CONF_ID], rhs) + + if CONF_FREQUENCY in config: + add(gpio.set_frequency(config[CONF_FREQUENCY])) + output.setup_output_platform(gpio, config) + setup_component(gpio, config) BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT' diff --git a/esphomeyaml/components/output/gpio.py b/esphomeyaml/components/output/gpio.py index 3f471e74b5..51e3ce27ef 100644 --- a/esphomeyaml/components/output/gpio.py +++ b/esphomeyaml/components/output/gpio.py @@ -4,23 +4,25 @@ from esphomeyaml import pins import esphomeyaml.config_validation as cv from esphomeyaml.components import output from esphomeyaml.const import CONF_ID, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression +from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression, setup_component, \ + Component -GPIOBinaryOutputComponent = output.output_ns.GPIOBinaryOutputComponent +GPIOBinaryOutputComponent = output.output_ns.class_('GPIOBinaryOutputComponent', + output.BinaryOutput, Component) PLATFORM_SCHEMA = output.BINARY_OUTPUT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutputComponent), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - pin = None for pin in gpio_output_pin_expression(config[CONF_PIN]): yield rhs = App.make_gpio_output(pin) gpio = Pvariable(config[CONF_ID], rhs) output.setup_output_platform(gpio, config) + setup_component(gpio, config) BUILD_FLAGS = '-DUSE_GPIO_OUTPUT' diff --git a/esphomeyaml/components/output/ledc.py b/esphomeyaml/components/output/ledc.py index 5db2a207c2..987d44a5be 100644 --- a/esphomeyaml/components/output/ledc.py +++ b/esphomeyaml/components/output/ledc.py @@ -5,7 +5,7 @@ from esphomeyaml import pins from esphomeyaml.components import output from esphomeyaml.const import APB_CLOCK_FREQ, CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \ CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import App, Pvariable, add +from esphomeyaml.helpers import App, Pvariable, add, setup_component, Component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] @@ -19,7 +19,7 @@ def validate_frequency_bit_depth(obj): return obj -LEDCOutputComponent = output.output_ns.LEDCOutputComponent +LEDCOutputComponent = output.output_ns.class_('LEDCOutputComponent', output.FloatOutput, Component) PLATFORM_SCHEMA = vol.All(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_ID): cv.declare_variable_id(LEDCOutputComponent), @@ -27,7 +27,7 @@ PLATFORM_SCHEMA = vol.All(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_FREQUENCY): cv.frequency, vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), vol.Range(min=1, max=15)), vol.Optional(CONF_CHANNEL): vol.All(vol.Coerce(int), vol.Range(min=0, max=15)) -}), validate_frequency_bit_depth) +}).extend(cv.COMPONENT_SCHEMA.schema), validate_frequency_bit_depth) def to_code(config): @@ -39,6 +39,7 @@ def to_code(config): if CONF_CHANNEL in config: add(ledc.set_channel(config[CONF_CHANNEL])) output.setup_output_platform(ledc, config) + setup_component(ledc, config) BUILD_FLAGS = '-DUSE_LEDC_OUTPUT' diff --git a/esphomeyaml/components/output/my9231.py b/esphomeyaml/components/output/my9231.py new file mode 100644 index 0000000000..4669ed2400 --- /dev/null +++ b/esphomeyaml/components/output/my9231.py @@ -0,0 +1,35 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import output +from esphomeyaml.components.my9231 import MY9231OutputComponent +from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY +from esphomeyaml.helpers import Pvariable, get_variable, setup_component + +DEPENDENCIES = ['my9231'] + +Channel = MY9231OutputComponent.class_('Channel', output.FloatOutput) + +PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_ID): cv.declare_variable_id(Channel), + vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int), + vol.Range(min=0, max=65535)), + cv.GenerateID(CONF_MY9231_ID): cv.use_variable_id(MY9231OutputComponent), +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + power_supply = None + if CONF_POWER_SUPPLY in config: + for power_supply in get_variable(config[CONF_POWER_SUPPLY]): + yield + my9231 = None + for my9231 in get_variable(config[CONF_MY9231_ID]): + yield + rhs = my9231.create_channel(config[CONF_CHANNEL], power_supply) + out = Pvariable(config[CONF_ID], rhs) + output.setup_output_platform(out, config, skip_power_supply=True) + setup_component(out, config) + + +BUILD_FLAGS = '-DUSE_MY9231_OUTPUT' diff --git a/esphomeyaml/components/output/pca9685.py b/esphomeyaml/components/output/pca9685.py index 55c57377b9..ba2f57ce6d 100644 --- a/esphomeyaml/components/output/pca9685.py +++ b/esphomeyaml/components/output/pca9685.py @@ -8,7 +8,7 @@ from esphomeyaml.helpers import Pvariable, get_variable DEPENDENCIES = ['pca9685'] -Channel = PCA9685OutputComponent.Channel +Channel = PCA9685OutputComponent.class_('Channel', output.FloatOutput) PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_ID): cv.declare_variable_id(Channel), @@ -23,7 +23,6 @@ def to_code(config): if CONF_POWER_SUPPLY in config: for power_supply in get_variable(config[CONF_POWER_SUPPLY]): yield - pca9685 = None for pca9685 in get_variable(config[CONF_PCA9685_ID]): yield rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply) diff --git a/esphomeyaml/components/pca9685.py b/esphomeyaml/components/pca9685.py index b2a3f2b096..ac3094abaf 100644 --- a/esphomeyaml/components/pca9685.py +++ b/esphomeyaml/components/pca9685.py @@ -1,13 +1,14 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import output +from esphomeyaml.components import output, i2c from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID, CONF_PHASE_BALANCER -from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add +from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add, setup_component, Component DEPENDENCIES = ['i2c'] -PCA9685OutputComponent = output.output_ns.namespace('PCA9685OutputComponent') +PCA9685OutputComponent = output.output_ns.class_('PCA9685OutputComponent', + Component, i2c.I2CDevice) PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. " "esphomelib will now automatically choose a suitable phase balancer.") @@ -19,7 +20,7 @@ PCA9685_SCHEMA = vol.Schema({ vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_PHASE_BALANCER): cv.invalid(PHASE_BALANCER_MESSAGE), -}) +}).extend(cv.COMPONENT_SCHEMA.schema) CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA]) @@ -30,6 +31,7 @@ def to_code(config): pca9685 = Pvariable(conf[CONF_ID], rhs) if CONF_ADDRESS in conf: add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS]))) + setup_component(pca9685, conf) BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT' diff --git a/esphomeyaml/components/pcf8574.py b/esphomeyaml/components/pcf8574.py index 9a1d40f843..e2f92d763f 100644 --- a/esphomeyaml/components/pcf8574.py +++ b/esphomeyaml/components/pcf8574.py @@ -1,19 +1,27 @@ import voluptuous as vol +from esphomeyaml import pins import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575 -from esphomeyaml.helpers import App, Pvariable, esphomelib_ns +from esphomeyaml.helpers import App, GPIOInputPin, GPIOOutputPin, Pvariable, io_ns, setup_component DEPENDENCIES = ['i2c'] -io_ns = esphomelib_ns.namespace('io') -PCF8574Component = io_ns.PCF8574Component +PCF8574GPIOMode = io_ns.enum('PCF8574GPIOMode') +PCF8675_GPIO_MODES = { + 'INPUT': PCF8574GPIOMode.PCF8574_INPUT, + 'INPUT_PULLUP': PCF8574GPIOMode.PCF8574_INPUT_PULLUP, + 'OUTPUT': PCF8574GPIOMode.PCF8574_OUTPUT, +} + +PCF8574GPIOInputPin = io_ns.class_('PCF8574GPIOInputPin', GPIOInputPin) +PCF8574GPIOOutputPin = io_ns.class_('PCF8574GPIOOutputPin', GPIOOutputPin) PCF8574_SCHEMA = vol.Schema({ - vol.Required(CONF_ID): cv.declare_variable_id(PCF8574Component), + vol.Required(CONF_ID): cv.declare_variable_id(pins.PCF8574Component), vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address, vol.Optional(CONF_PCF8575, default=False): cv.boolean, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA]) @@ -21,7 +29,8 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA]) def to_code(config): for conf in config: rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575]) - Pvariable(conf[CONF_ID], rhs) + var = Pvariable(conf[CONF_ID], rhs) + setup_component(var, conf) BUILD_FLAGS = '-DUSE_PCF8574' diff --git a/esphomeyaml/components/pn532.py b/esphomeyaml/components/pn532.py index 6dda13b0c4..a33ac791bd 100644 --- a/esphomeyaml/components/pn532.py +++ b/esphomeyaml/components/pn532.py @@ -2,16 +2,18 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins, automation -from esphomeyaml.components import binary_sensor +from esphomeyaml.components import binary_sensor, spi from esphomeyaml.components.spi import SPIComponent from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL, \ CONF_ON_TAG, CONF_TRIGGER_ID -from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression, std_string +from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression, \ + std_string, setup_component, PollingComponent, Trigger DEPENDENCIES = ['spi'] -PN532Component = binary_sensor.binary_sensor_ns.PN532Component -PN532Trigger = binary_sensor.binary_sensor_ns.PN532Trigger +PN532Component = binary_sensor.binary_sensor_ns.class_('PN532Component', PollingComponent, + spi.SPIDevice) +PN532Trigger = binary_sensor.binary_sensor_ns.class_('PN532Trigger', Trigger.template(std_string)) CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ cv.GenerateID(): cv.declare_variable_id(PN532Component), @@ -21,23 +23,23 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ vol.Optional(CONF_ON_TAG): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PN532Trigger), }), -})]) +}).extend(cv.COMPONENT_SCHEMA.schema)]) def to_code(config): for conf in config: - spi = None - for spi in get_variable(conf[CONF_SPI_ID]): + for spi_ in get_variable(conf[CONF_SPI_ID]): yield - cs = None for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]): yield - rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL)) + rhs = App.make_pn532_component(spi_, cs, conf.get(CONF_UPDATE_INTERVAL)) pn532 = Pvariable(conf[CONF_ID], rhs) for conf_ in conf.get(CONF_ON_TAG, []): trigger = Pvariable(conf_[CONF_TRIGGER_ID], pn532.make_trigger()) automation.build_automation(trigger, std_string, conf_) + setup_component(pn532, conf) + BUILD_FLAGS = '-DUSE_PN532' diff --git a/esphomeyaml/components/power_supply.py b/esphomeyaml/components/power_supply.py index 209b729be7..97efda4f1e 100644 --- a/esphomeyaml/components/power_supply.py +++ b/esphomeyaml/components/power_supply.py @@ -3,25 +3,26 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression +from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression, \ + setup_component, Component -PowerSupplyComponent = esphomelib_ns.PowerSupplyComponent +PowerSupplyComponent = esphomelib_ns.class_('PowerSupplyComponent', Component) POWER_SUPPLY_SCHEMA = vol.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds, vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA]) def to_code(config): for conf in config: - pin = None for pin in gpio_output_pin_expression(conf[CONF_PIN]): yield + rhs = App.make_power_supply(pin) psu = Pvariable(conf[CONF_ID], rhs) if CONF_ENABLE_TIME in conf: @@ -29,5 +30,7 @@ def to_code(config): if CONF_KEEP_ON_TIME in conf: add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME])) + setup_component(psu, conf) + BUILD_FLAGS = '-DUSE_OUTPUT' diff --git a/esphomeyaml/components/rdm6300.py b/esphomeyaml/components/rdm6300.py index 7cc05e4f2c..0ede8e290f 100644 --- a/esphomeyaml/components/rdm6300.py +++ b/esphomeyaml/components/rdm6300.py @@ -1,28 +1,28 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import binary_sensor -from esphomeyaml.components.uart import UARTComponent +from esphomeyaml.components import binary_sensor, uart from esphomeyaml.const import CONF_ID, CONF_UART_ID -from esphomeyaml.helpers import App, Pvariable, get_variable +from esphomeyaml.helpers import App, Pvariable, get_variable, setup_component, Component DEPENDENCIES = ['uart'] -RDM6300Component = binary_sensor.binary_sensor_ns.RDM6300Component +RDM6300Component = binary_sensor.binary_sensor_ns.class_('RDM6300Component', Component, + uart.UARTDevice) CONFIG_SCHEMA = vol.All(cv.ensure_list_not_empty, [vol.Schema({ cv.GenerateID(): cv.declare_variable_id(RDM6300Component), - cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), -})]) + cv.GenerateID(CONF_UART_ID): cv.use_variable_id(uart.UARTComponent), +}).extend(cv.COMPONENT_SCHEMA.schema)]) def to_code(config): for conf in config: - uart = None - for uart in get_variable(conf[CONF_UART_ID]): + for uart_ in get_variable(conf[CONF_UART_ID]): yield - rhs = App.make_rdm6300_component(uart) - Pvariable(conf[CONF_ID], rhs) + rhs = App.make_rdm6300_component(uart_) + var = Pvariable(conf[CONF_ID], rhs) + setup_component(var, conf) BUILD_FLAGS = '-DUSE_RDM6300' diff --git a/esphomeyaml/components/remote_receiver.py b/esphomeyaml/components/remote_receiver.py index 8cb6be5a93..878e5a6fc5 100644 --- a/esphomeyaml/components/remote_receiver.py +++ b/esphomeyaml/components/remote_receiver.py @@ -4,20 +4,26 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, \ CONF_PIN, CONF_TOLERANCE -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_input_pin_expression +from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_input_pin_expression, \ + setup_component, Component remote_ns = esphomelib_ns.namespace('remote') -RemoteReceiverComponent = remote_ns.RemoteReceiverComponent +RemoteControlComponentBase = remote_ns.class_('RemoteControlComponentBase') +RemoteReceiverComponent = remote_ns.class_('RemoteReceiverComponent', + RemoteControlComponentBase, + Component) + +RemoteReceiveDumper = remote_ns.class_('RemoteReceiveDumper') DUMPERS = { - 'lg': remote_ns.LGDumper, - 'nec': remote_ns.NECDumper, - 'panasonic': remote_ns.PanasonicDumper, - 'raw': remote_ns.RawDumper, - 'samsung': remote_ns.SamsungDumper, - 'sony': remote_ns.SonyDumper, - 'rc_switch': remote_ns.RCSwitchDumper, + 'lg': remote_ns.class_('LGDumper', RemoteReceiveDumper), + 'nec': remote_ns.class_('NECDumper', RemoteReceiveDumper), + 'panasonic': remote_ns.class_('PanasonicDumper', RemoteReceiveDumper), + 'raw': remote_ns.class_('RawDumper', RemoteReceiveDumper), + 'samsung': remote_ns.class_('SamsungDumper', RemoteReceiveDumper), + 'sony': remote_ns.class_('SonyDumper', RemoteReceiveDumper), + 'rc_switch': remote_ns.class_('RCSwitchDumper', RemoteReceiveDumper), } @@ -39,16 +45,16 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ vol.Optional(CONF_BUFFER_SIZE): cv.validate_bytes, vol.Optional(CONF_FILTER): cv.positive_time_period_microseconds, vol.Optional(CONF_IDLE): cv.positive_time_period_microseconds, -})]) +}).extend(cv.COMPONENT_SCHEMA.schema)]) def to_code(config): for conf in config: - pin = None for pin in gpio_input_pin_expression(conf[CONF_PIN]): yield rhs = App.make_remote_receiver_component(pin) receiver = Pvariable(conf[CONF_ID], rhs) + for dumper in conf[CONF_DUMP]: add(receiver.add_dumper(DUMPERS[dumper].new())) if CONF_TOLERANCE in conf: @@ -60,5 +66,7 @@ def to_code(config): if CONF_IDLE in conf: add(receiver.set_idle_us(conf[CONF_IDLE])) + setup_component(receiver, conf) + BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER' diff --git a/esphomeyaml/components/remote_transmitter.py b/esphomeyaml/components/remote_transmitter.py index 7eb8ed3db1..144ac2bfdf 100644 --- a/esphomeyaml/components/remote_transmitter.py +++ b/esphomeyaml/components/remote_transmitter.py @@ -1,17 +1,18 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins +from esphomeyaml.components.remote_receiver import RemoteControlComponentBase, remote_ns +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_DUTY_PERCENT, CONF_CHANNEL, CONF_CODE, \ CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_ID, CONF_INVERTED, CONF_ONE, CONF_PIN, \ CONF_PROTOCOL, CONF_PULSE_LENGTH, CONF_STATE, CONF_SYNC, CONF_ZERO from esphomeyaml.core import HexInt -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression +from esphomeyaml.helpers import App, Component, Pvariable, add, gpio_output_pin_expression, \ + setup_component -remote_ns = esphomelib_ns.namespace('remote') - -RemoteTransmitterComponent = remote_ns.RemoteTransmitterComponent -RCSwitchProtocol = remote_ns.RCSwitchProtocol +RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent', + RemoteControlComponentBase, Component) +RCSwitchProtocol = remote_ns.class_('RCSwitchProtocol') rc_switch_protocols = remote_ns.rc_switch_protocols @@ -81,7 +82,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ vol.Required(CONF_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(cv.percentage_int, vol.Range(min=1, max=100)), -})]) +}).extend(cv.COMPONENT_SCHEMA.schema)]) def build_rc_switch_protocol(config): @@ -104,13 +105,15 @@ def binary_code(value): def to_code(config): for conf in config: - pin = None for pin in gpio_output_pin_expression(conf[CONF_PIN]): yield rhs = App.make_remote_transmitter_component(pin) transmitter = Pvariable(conf[CONF_ID], rhs) + if CONF_CARRIER_DUTY_PERCENT in conf: add(transmitter.set_carrier_duty_percent(conf[CONF_CARRIER_DUTY_PERCENT])) + setup_component(transmitter, conf) + BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER' diff --git a/esphomeyaml/components/script.py b/esphomeyaml/components/script.py index 6c8c52469e..5aab19c3c8 100644 --- a/esphomeyaml/components/script.py +++ b/esphomeyaml/components/script.py @@ -4,10 +4,11 @@ from esphomeyaml import automation from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID -from esphomeyaml.helpers import NoArg, Pvariable, TemplateArguments, esphomelib_ns, get_variable +from esphomeyaml.helpers import NoArg, Pvariable, TemplateArguments, esphomelib_ns, get_variable, \ + Trigger, Action -Script = esphomelib_ns.Script -ScriptExecuteAction = esphomelib_ns.ScriptExecuteAction +Script = esphomelib_ns.class_('Script', Trigger.template(NoArg)) +ScriptExecuteAction = esphomelib_ns.class_('ScriptExecuteAction', Action) CONFIG_SCHEMA = automation.validate_automation({ vol.Required(CONF_ID): cv.declare_variable_id(Script), diff --git a/esphomeyaml/components/sensor/__init__.py b/esphomeyaml/components/sensor/__init__.py index 01732ea1c6..846c08ea81 100644 --- a/esphomeyaml/components/sensor/__init__.py +++ b/esphomeyaml/components/sensor/__init__.py @@ -1,15 +1,18 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import automation +from esphomeyaml.components import mqtt +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \ CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \ CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_INTERNAL, \ CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE, \ - CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \ - CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE -from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, add_job, esphomelib_ns, \ - float_, process_lambda, setup_mqtt_component, templatable + CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, \ + CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, \ + CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE +from esphomeyaml.helpers import App, ArrayInitializer, Component, Nameable, PollingComponent, \ + Pvariable, Trigger, add, add_job, esphomelib_ns, float_, process_lambda, setup_mqtt_component, \ + templatable PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -20,6 +23,15 @@ def validate_recursive_filter(value): return FILTERS_SCHEMA(value) +def validate_send_first_at(value): + send_first_at = value.get(CONF_SEND_FIRST_AT) + send_every = value[CONF_SEND_EVERY] + if send_first_at is not None and send_first_at > send_every: + raise vol.Invalid("send_first_at must be smaller than or equal to send_every! {} <= {}" + "".format(send_first_at, send_every)) + return value + + FILTER_KEYS = [CONF_OFFSET, CONF_MULTIPLY, CONF_FILTER_OUT, CONF_FILTER_NAN, CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_LAMBDA, CONF_THROTTLE, CONF_DELTA, CONF_UNIQUE, CONF_HEARTBEAT, CONF_DEBOUNCE, CONF_OR] @@ -29,10 +41,11 @@ FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_MULTIPLY): vol.Coerce(float), vol.Optional(CONF_FILTER_OUT): vol.Coerce(float), vol.Optional(CONF_FILTER_NAN): None, - vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({ + vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.All(vol.Schema({ vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int, vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int, - }), + vol.Optional(CONF_SEND_FIRST_AT): cv.positive_not_null_int, + }), validate_send_first_at), vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({ vol.Required(CONF_ALPHA): cv.positive_float, vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int, @@ -46,31 +59,38 @@ FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_OR): validate_recursive_filter, }, cv.has_exactly_one_key(*FILTER_KEYS))]) -# pylint: disable=invalid-name +# Base sensor_ns = esphomelib_ns.namespace('sensor') -Sensor = sensor_ns.Sensor -MQTTSensorComponent = sensor_ns.MQTTSensorComponent -OffsetFilter = sensor_ns.OffsetFilter -MultiplyFilter = sensor_ns.MultiplyFilter -FilterOutValueFilter = sensor_ns.FilterOutValueFilter -FilterOutNANFilter = sensor_ns.FilterOutNANFilter -SlidingWindowMovingAverageFilter = sensor_ns.SlidingWindowMovingAverageFilter -ExponentialMovingAverageFilter = sensor_ns.ExponentialMovingAverageFilter -LambdaFilter = sensor_ns.LambdaFilter -ThrottleFilter = sensor_ns.ThrottleFilter -DeltaFilter = sensor_ns.DeltaFilter -OrFilter = sensor_ns.OrFilter -HeartbeatFilter = sensor_ns.HeartbeatFilter -DebounceFilter = sensor_ns.DebounceFilter -UniqueFilter = sensor_ns.UniqueFilter +Sensor = sensor_ns.class_('Sensor', Nameable) +MQTTSensorComponent = sensor_ns.class_('MQTTSensorComponent', mqtt.MQTTComponent) -SensorStateTrigger = sensor_ns.SensorStateTrigger -SensorRawStateTrigger = sensor_ns.SensorRawStateTrigger -ValueRangeTrigger = sensor_ns.ValueRangeTrigger +PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', PollingComponent, Sensor) +EmptySensor = sensor_ns.class_('EmptySensor', Sensor) +EmptyPollingParentSensor = sensor_ns.class_('EmptyPollingParentSensor', EmptySensor) + +# Triggers +SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', Trigger.template(float_)) +SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', Trigger.template(float_)) +ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', Trigger.template(float_)) + +# Filters +Filter = sensor_ns.class_('Filter') +SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter) +ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter) +LambdaFilter = sensor_ns.class_('LambdaFilter', Filter) +OffsetFilter = sensor_ns.class_('OffsetFilter', Filter) +MultiplyFilter = sensor_ns.class_('MultiplyFilter', Filter) +FilterOutValueFilter = sensor_ns.class_('FilterOutValueFilter', Filter) +FilterOutNANFilter = sensor_ns.class_('FilterOutNANFilter', Filter) +ThrottleFilter = sensor_ns.class_('ThrottleFilter', Filter) +DebounceFilter = sensor_ns.class_('DebounceFilter', Filter, Component) +HeartbeatFilter = sensor_ns.class_('HeartbeatFilter', Filter, Component) +DeltaFilter = sensor_ns.class_('DeltaFilter', Filter) +OrFilter = sensor_ns.class_('OrFilter', Filter) +UniqueFilter = sensor_ns.class_('UniqueFilter', Filter) SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent), - cv.GenerateID(): cv.declare_variable_id(Sensor), vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict, vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int), @@ -103,7 +123,8 @@ def setup_filter(config): yield FilterOutNANFilter.new() elif CONF_SLIDING_WINDOW_MOVING_AVERAGE in config: conf = config[CONF_SLIDING_WINDOW_MOVING_AVERAGE] - yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY]) + yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY], + conf.get(CONF_SEND_FIRST_AT)) elif CONF_EXPONENTIAL_MOVING_AVERAGE in config: conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE] yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY]) @@ -199,3 +220,18 @@ def register_sensor(var, config): BUILD_FLAGS = '-DUSE_SENSOR' + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False) + if ret is None: + return None + if CONF_UNIT_OF_MEASUREMENT in config: + ret['unit_of_measurement'] = config[CONF_UNIT_OF_MEASUREMENT] + if CONF_EXPIRE_AFTER in config: + expire = config[CONF_EXPIRE_AFTER] + if expire is not None: + ret['expire_after'] = expire.total_seconds + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + return ret diff --git a/esphomeyaml/components/sensor/adc.py b/esphomeyaml/components/sensor/adc.py index 6d75251c69..f629ee3d4d 100644 --- a/esphomeyaml/components/sensor/adc.py +++ b/esphomeyaml/components/sensor/adc.py @@ -5,7 +5,7 @@ from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, global_ns, variable +from esphomeyaml.helpers import App, Application, add, global_ns, variable, setup_component ATTENUATION_MODES = { '0db': global_ns.ADC_0db, @@ -22,14 +22,16 @@ def validate_adc_pin(value): return pins.analog_pin(value) -MakeADCSensor = Application.MakeADCSensor +MakeADCSensor = Application.struct('MakeADCSensor') +ADCSensorComponent = sensor.sensor_ns.class_('ADCSensorComponent', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ADCSensorComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeADCSensor), vol.Required(CONF_PIN): validate_adc_pin, vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): @@ -42,7 +44,8 @@ def to_code(config): adc = make.Padc if CONF_ATTENUATION in config: add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]])) - sensor.setup_sensor(make.Padc, make.Pmqtt, config) + sensor.setup_sensor(adc, make.Pmqtt, config) + setup_component(adc, config) BUILD_FLAGS = '-DUSE_ADC_SENSOR' @@ -52,3 +55,7 @@ def required_build_flags(config): if config[CONF_PIN] == 'VCC': return '-DUSE_ADC_SENSOR_VCC' return None + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index ab9b594a78..5f6c296789 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -9,24 +9,26 @@ from esphomeyaml.helpers import get_variable DEPENDENCIES = ['ads1115'] +ADS1115Multiplexer = sensor.sensor_ns.enum('ADS1115Multiplexer') MUX = { - 'A0_A1': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N1, - 'A0_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N3, - 'A1_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_N3, - 'A2_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_N3, - 'A0_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_NG, - 'A1_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_NG, - 'A2_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_NG, - 'A3_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P3_NG, + 'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1, + 'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3, + 'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3, + 'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3, + 'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG, + 'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG, + 'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG, + 'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG, } +ADS1115Gain = sensor.sensor_ns.enum('ADS1115Gain') GAIN = { - '6.144': sensor.sensor_ns.ADS1115_GAIN_6P144, - '4.096': sensor.sensor_ns.ADS1115_GAIN_6P096, - '2.048': sensor.sensor_ns.ADS1115_GAIN_2P048, - '1.024': sensor.sensor_ns.ADS1115_GAIN_1P024, - '0.512': sensor.sensor_ns.ADS1115_GAIN_0P512, - '0.256': sensor.sensor_ns.ADS1115_GAIN_0P256, + '6.144': ADS1115Gain.ADS1115_GAIN_6P144, + '4.096': ADS1115Gain.ADS1115_GAIN_6P096, + '2.048': ADS1115Gain.ADS1115_GAIN_2P048, + '1.024': ADS1115Gain.ADS1115_GAIN_1P024, + '0.512': ADS1115Gain.ADS1115_GAIN_0P512, + '0.256': ADS1115Gain.ADS1115_GAIN_0P256, } @@ -45,7 +47,10 @@ def validate_mux(value): return cv.one_of(*MUX)(value) +ADS1115Sensor = sensor.sensor_ns.class_('ADS1115Sensor', sensor.EmptySensor) + PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ADS1115Sensor), vol.Required(CONF_MULTIPLEXER): validate_mux, vol.Required(CONF_GAIN): validate_gain, cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component), @@ -65,3 +70,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ADS1115_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/bh1750.py b/esphomeyaml/components/sensor/bh1750.py index c50a1494ab..09057e5f7f 100644 --- a/esphomeyaml/components/sensor/bh1750.py +++ b/esphomeyaml/components/sensor/bh1750.py @@ -1,27 +1,31 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component DEPENDENCIES = ['i2c'] +BH1750Resolution = sensor.sensor_ns.enum('BH1750Resolution') BH1750_RESOLUTIONS = { - 4.0: sensor.sensor_ns.BH1750_RESOLUTION_4P0_LX, - 1.0: sensor.sensor_ns.BH1750_RESOLUTION_1P0_LX, - 0.5: sensor.sensor_ns.BH1750_RESOLUTION_0P5_LX, + 4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX, + 1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX, + 0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX, } -MakeBH1750Sensor = Application.MakeBH1750Sensor +MakeBH1750Sensor = Application.struct('MakeBH1750Sensor') +BH1750Sensor = sensor.sensor_ns.class_('BH1750Sensor', sensor.PollingSensorComponent, + i2c.I2CDevice) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BH1750Sensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBH1750Sensor), vol.Optional(CONF_ADDRESS, default=0x23): cv.i2c_address, vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, cv.one_of(*BH1750_RESOLUTIONS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): @@ -32,6 +36,11 @@ def to_code(config): if CONF_RESOLUTION in config: add(bh1750.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]])) sensor.setup_sensor(bh1750, make_bh1750.Pmqtt, config) + setup_component(bh1750, config) BUILD_FLAGS = '-DUSE_BH1750' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ble_rssi.py b/esphomeyaml/components/sensor/ble_rssi.py index 2b98f45c44..e209b9af64 100644 --- a/esphomeyaml/components/sensor/ble_rssi.py +++ b/esphomeyaml/components/sensor/ble_rssi.py @@ -5,19 +5,25 @@ from esphomeyaml.components import sensor from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ make_address_array from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME -from esphomeyaml.helpers import get_variable +from esphomeyaml.helpers import get_variable, esphomelib_ns DEPENDENCIES = ['esp32_ble_tracker'] +ESP32BLERSSISensor = esphomelib_ns.class_('ESP32BLERSSISensor', sensor.Sensor) + PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ESP32BLERSSISensor), vol.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker) })) def to_code(config): - hub = None for hub in get_variable(config[CONF_ESP32_BLE_ID]): yield rhs = hub.make_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) sensor.register_sensor(rhs, config) + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/bme280.py b/esphomeyaml/components/sensor/bme280.py index e123134fb7..23e395f6ef 100644 --- a/esphomeyaml/components/sensor/bme280.py +++ b/esphomeyaml/components/sensor/bme280.py @@ -4,42 +4,56 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_IIR_FILTER, CONF_MAKE_ID, \ CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component DEPENDENCIES = ['i2c'] +BME280Oversampling = sensor.sensor_ns.enum('BME280Oversampling') OVERSAMPLING_OPTIONS = { - 'NONE': sensor.sensor_ns.BME280_OVERSAMPLING_NONE, - '1X': sensor.sensor_ns.BME280_OVERSAMPLING_1X, - '2X': sensor.sensor_ns.BME280_OVERSAMPLING_2X, - '4X': sensor.sensor_ns.BME280_OVERSAMPLING_4X, - '8X': sensor.sensor_ns.BME280_OVERSAMPLING_8X, - '16X': sensor.sensor_ns.BME280_OVERSAMPLING_16X, + 'NONE': BME280Oversampling.BME280_OVERSAMPLING_NONE, + '1X': BME280Oversampling.BME280_OVERSAMPLING_1X, + '2X': BME280Oversampling.BME280_OVERSAMPLING_2X, + '4X': BME280Oversampling.BME280_OVERSAMPLING_4X, + '8X': BME280Oversampling.BME280_OVERSAMPLING_8X, + '16X': BME280Oversampling.BME280_OVERSAMPLING_16X, } +BME280IIRFilter = sensor.sensor_ns.enum('BME280IIRFilter') IIR_FILTER_OPTIONS = { - 'OFF': sensor.sensor_ns.BME280_IIR_FILTER_OFF, - '2X': sensor.sensor_ns.BME280_IIR_FILTER_2X, - '4X': sensor.sensor_ns.BME280_IIR_FILTER_4X, - '8X': sensor.sensor_ns.BME280_IIR_FILTER_8X, - '16X': sensor.sensor_ns.BME280_IIR_FILTER_16X, + 'OFF': BME280IIRFilter.BME280_IIR_FILTER_OFF, + '2X': BME280IIRFilter.BME280_IIR_FILTER_2X, + '4X': BME280IIRFilter.BME280_IIR_FILTER_4X, + '8X': BME280IIRFilter.BME280_IIR_FILTER_8X, + '16X': BME280IIRFilter.BME280_IIR_FILTER_16X, } BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), }) -MakeBME280Sensor = Application.MakeBME280Sensor +MakeBME280Sensor = Application.struct('MakeBME280Sensor') +BME280TemperatureSensor = sensor.sensor_ns.class_('BME280TemperatureSensor', + sensor.EmptyPollingParentSensor) +BME280PressureSensor = sensor.sensor_ns.class_('BME280PressureSensor', + sensor.EmptyPollingParentSensor) +BME280HumiditySensor = sensor.sensor_ns.class_('BME280HumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME280Sensor), vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address, - vol.Required(CONF_TEMPERATURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_PRESSURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME280TemperatureSensor), + })), + vol.Required(CONF_PRESSURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME280PressureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME280HumiditySensor), + })), vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -69,6 +83,13 @@ def to_code(config): config[CONF_PRESSURE]) sensor.setup_sensor(bme280.Pget_humidity_sensor(), make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(bme280, config) BUILD_FLAGS = '-DUSE_BME280' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/bme680.py b/esphomeyaml/components/sensor/bme680.py index 43cebc5d49..71778574a1 100644 --- a/esphomeyaml/components/sensor/bme680.py +++ b/esphomeyaml/components/sensor/bme680.py @@ -6,43 +6,61 @@ from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_GAS_RESISTANCE, CONF_HUMIDITY, CONF_IIR_FILTER, \ CONF_MAKE_ID, CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, \ CONF_UPDATE_INTERVAL, CONF_HEATER, CONF_DURATION -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component DEPENDENCIES = ['i2c'] +BME680Oversampling = sensor.sensor_ns.enum('BME680Oversampling') OVERSAMPLING_OPTIONS = { - 'NONE': sensor.sensor_ns.BME680_OVERSAMPLING_NONE, - '1X': sensor.sensor_ns.BME680_OVERSAMPLING_1X, - '2X': sensor.sensor_ns.BME680_OVERSAMPLING_2X, - '4X': sensor.sensor_ns.BME680_OVERSAMPLING_4X, - '8X': sensor.sensor_ns.BME680_OVERSAMPLING_8X, - '16X': sensor.sensor_ns.BME680_OVERSAMPLING_16X, + 'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE, + '1X': BME680Oversampling.BME680_OVERSAMPLING_1X, + '2X': BME680Oversampling.BME680_OVERSAMPLING_2X, + '4X': BME680Oversampling.BME680_OVERSAMPLING_4X, + '8X': BME680Oversampling.BME680_OVERSAMPLING_8X, + '16X': BME680Oversampling.BME680_OVERSAMPLING_16X, } +BME680IIRFilter = sensor.sensor_ns.enum('BME680IIRFilter') IIR_FILTER_OPTIONS = { - 'OFF': sensor.sensor_ns.BME680_IIR_FILTER_OFF, - '1X': sensor.sensor_ns.BME680_IIR_FILTER_1X, - '3X': sensor.sensor_ns.BME680_IIR_FILTER_3X, - '7X': sensor.sensor_ns.BME680_IIR_FILTER_7X, - '15X': sensor.sensor_ns.BME680_IIR_FILTER_15X, - '31X': sensor.sensor_ns.BME680_IIR_FILTER_31X, - '63X': sensor.sensor_ns.BME680_IIR_FILTER_63X, - '127X': sensor.sensor_ns.BME680_IIR_FILTER_127X, + 'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF, + '1X': BME680IIRFilter.BME680_IIR_FILTER_1X, + '3X': BME680IIRFilter.BME680_IIR_FILTER_3X, + '7X': BME680IIRFilter.BME680_IIR_FILTER_7X, + '15X': BME680IIRFilter.BME680_IIR_FILTER_15X, + '31X': BME680IIRFilter.BME680_IIR_FILTER_31X, + '63X': BME680IIRFilter.BME680_IIR_FILTER_63X, + '127X': BME680IIRFilter.BME680_IIR_FILTER_127X, } BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), }) -MakeBME680Sensor = Application.MakeBME680Sensor +MakeBME680Sensor = Application.struct('MakeBME680Sensor') +BME680TemperatureSensor = sensor.sensor_ns.class_('BME680TemperatureSensor', + sensor.EmptyPollingParentSensor) +BME680PressureSensor = sensor.sensor_ns.class_('BME680PressureSensor', + sensor.EmptyPollingParentSensor) +BME680HumiditySensor = sensor.sensor_ns.class_('BME680HumiditySensor', + sensor.EmptyPollingParentSensor) +BME680GasResistanceSensor = sensor.sensor_ns.class_('BME680GasResistanceSensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME680Sensor), vol.Optional(CONF_ADDRESS, default=0x76): cv.i2c_address, - vol.Required(CONF_TEMPERATURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_PRESSURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_GAS_RESISTANCE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME680TemperatureSensor), + })), + vol.Required(CONF_PRESSURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME680PressureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME680HumiditySensor), + })), + vol.Required(CONF_GAS_RESISTANCE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BME680GasResistanceSensor), + })), vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)), vol.Optional(CONF_HEATER): vol.Any(None, vol.All(vol.Schema({ vol.Optional(CONF_TEMPERATURE, default=320): vol.All(vol.Coerce(int), vol.Range(200, 400)), @@ -50,7 +68,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.positive_time_period_milliseconds, vol.Range(max=core.TimePeriod(milliseconds=4032))) }, cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION)))), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -89,6 +107,14 @@ def to_code(config): config[CONF_HUMIDITY]) sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), make.Pmqtt_gas_resistance, config[CONF_GAS_RESISTANCE]) + setup_component(bme680, config) BUILD_FLAGS = '-DUSE_BME680' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY]), + sensor.core_to_hass_config(data, config[CONF_GAS_RESISTANCE])] diff --git a/esphomeyaml/components/sensor/bmp085.py b/esphomeyaml/components/sensor/bmp085.py index 01659d9852..24b7199a10 100644 --- a/esphomeyaml/components/sensor/bmp085.py +++ b/esphomeyaml/components/sensor/bmp085.py @@ -4,19 +4,27 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \ CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable +from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable, setup_component DEPENDENCIES = ['i2c'] -MakeBMP085Sensor = Application.MakeBMP085Sensor +MakeBMP085Sensor = Application.struct('MakeBMP085Sensor') +BMP085TemperatureSensor = sensor.sensor_ns.class_('BMP085TemperatureSensor', + sensor.EmptyPollingParentSensor) +BMP085PressureSensor = sensor.sensor_ns.class_('BMP085PressureSensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP085Sensor), - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BMP085TemperatureSensor), + })), + vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BMP085PressureSensor), + })), vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -31,6 +39,12 @@ def to_code(config): config[CONF_TEMPERATURE]) sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), bmp.Pmqtt_pressure, config[CONF_PRESSURE]) + setup_component(bmp.Pbmp, config) BUILD_FLAGS = '-DUSE_BMP085_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/bmp280.py b/esphomeyaml/components/sensor/bmp280.py index 53d4a34e48..35d30403d8 100644 --- a/esphomeyaml/components/sensor/bmp280.py +++ b/esphomeyaml/components/sensor/bmp280.py @@ -4,41 +4,51 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_IIR_FILTER, CONF_MAKE_ID, \ CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component DEPENDENCIES = ['i2c'] +BMP280Oversampling = sensor.sensor_ns.enum('BMP280Oversampling') OVERSAMPLING_OPTIONS = { - 'NONE': sensor.sensor_ns.BMP280_OVERSAMPLING_NONE, - '1X': sensor.sensor_ns.BMP280_OVERSAMPLING_1X, - '2X': sensor.sensor_ns.BMP280_OVERSAMPLING_2X, - '4X': sensor.sensor_ns.BMP280_OVERSAMPLING_4X, - '8X': sensor.sensor_ns.BMP280_OVERSAMPLING_8X, - '16X': sensor.sensor_ns.BMP280_OVERSAMPLING_16X, + 'NONE': BMP280Oversampling.BMP280_OVERSAMPLING_NONE, + '1X': BMP280Oversampling.BMP280_OVERSAMPLING_1X, + '2X': BMP280Oversampling.BMP280_OVERSAMPLING_2X, + '4X': BMP280Oversampling.BMP280_OVERSAMPLING_4X, + '8X': BMP280Oversampling.BMP280_OVERSAMPLING_8X, + '16X': BMP280Oversampling.BMP280_OVERSAMPLING_16X, } +BMP280IIRFilter = sensor.sensor_ns.enum('BMP280IIRFilter') IIR_FILTER_OPTIONS = { - 'OFF': sensor.sensor_ns.BMP280_IIR_FILTER_OFF, - '2X': sensor.sensor_ns.BMP280_IIR_FILTER_2X, - '4X': sensor.sensor_ns.BMP280_IIR_FILTER_4X, - '8X': sensor.sensor_ns.BMP280_IIR_FILTER_8X, - '16X': sensor.sensor_ns.BMP280_IIR_FILTER_16X, + 'OFF': BMP280IIRFilter.BMP280_IIR_FILTER_OFF, + '2X': BMP280IIRFilter.BMP280_IIR_FILTER_2X, + '4X': BMP280IIRFilter.BMP280_IIR_FILTER_4X, + '8X': BMP280IIRFilter.BMP280_IIR_FILTER_8X, + '16X': BMP280IIRFilter.BMP280_IIR_FILTER_16X, } BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), }) -MakeBMP280Sensor = Application.MakeBMP280Sensor +MakeBMP280Sensor = Application.struct('MakeBMP280Sensor') +BMP280TemperatureSensor = sensor.sensor_ns.class_('BMP280TemperatureSensor', + sensor.EmptyPollingParentSensor) +BMP280PressureSensor = sensor.sensor_ns.class_('BMP280PressureSensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP280Sensor), vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address, - vol.Required(CONF_TEMPERATURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA), - vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BMP280TemperatureSensor), + })), + vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(BMP280PressureSensor), + })), vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -62,6 +72,12 @@ def to_code(config): config[CONF_TEMPERATURE]) sensor.setup_sensor(bmp280.Pget_pressure_sensor(), make.Pmqtt_pressure, config[CONF_PRESSURE]) + setup_component(bmp280, config) BUILD_FLAGS = '-DUSE_BMP280' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/cse7766.py b/esphomeyaml/components/sensor/cse7766.py index 01dc894db8..091c146632 100644 --- a/esphomeyaml/components/sensor/cse7766.py +++ b/esphomeyaml/components/sensor/cse7766.py @@ -1,31 +1,44 @@ import voluptuous as vol -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, uart from esphomeyaml.components.uart import UARTComponent import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_CURRENT, CONF_ID, CONF_NAME, CONF_POWER, CONF_UART_ID, \ CONF_VOLTAGE -from esphomeyaml.helpers import App, Pvariable, get_variable +from esphomeyaml.helpers import App, Pvariable, get_variable, setup_component, Component DEPENDENCIES = ['uart'] -CSE7766Component = sensor.sensor_ns.CSE7766Component +CSE7766Component = sensor.sensor_ns.class_('CSE7766Component', Component, uart.UARTDevice) +CSE7766VoltageSensor = sensor.sensor_ns.class_('CSE7766VoltageSensor', + sensor.EmptySensor) +CSE7766CurrentSensor = sensor.sensor_ns.class_('CSE7766CurrentSensor', + sensor.EmptySensor) +CSE7766PowerSensor = sensor.sensor_ns.class_('CSE7766PowerSensor', + sensor.EmptySensor) PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(CSE7766Component), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), - vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), -}), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, CONF_POWER)) + vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CSE7766VoltageSensor), + })), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CSE7766CurrentSensor), + })), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CSE7766PowerSensor), + })), +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, + CONF_POWER)) def to_code(config): - for uart in get_variable(config[CONF_UART_ID]): + for uart_ in get_variable(config[CONF_UART_ID]): yield - rhs = App.make_cse7766(uart) + rhs = App.make_cse7766(uart_) cse = Pvariable(config[CONF_ID], rhs) if CONF_VOLTAGE in config: @@ -37,6 +50,15 @@ def to_code(config): if CONF_POWER in config: conf = config[CONF_POWER] sensor.register_sensor(cse.make_power_sensor(conf[CONF_NAME]), conf) + setup_component(cse, config) BUILD_FLAGS = '-DUSE_CSE7766' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/dallas.py b/esphomeyaml/components/sensor/dallas.py index bbf1e7429c..b974c8e636 100644 --- a/esphomeyaml/components/sensor/dallas.py +++ b/esphomeyaml/components/sensor/dallas.py @@ -7,7 +7,11 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAM CONF_RESOLUTION from esphomeyaml.helpers import HexIntLiteral, get_variable +DallasTemperatureSensor = sensor.sensor_ns.class_('DallasTemperatureSensor', + sensor.EmptyPollingParentSensor) + PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DallasTemperatureSensor), vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int, vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int, cv.GenerateID(CONF_DALLAS_ID): cv.use_variable_id(DallasComponent), @@ -16,7 +20,6 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ def to_code(config): - hub = None for hub in get_variable(config[CONF_DALLAS_ID]): yield if CONF_ADDRESS in config: @@ -29,3 +32,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_DALLAS_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/dht.py b/esphomeyaml/components/sensor/dht.py index 0b4f013e2a..a13d0f0fdd 100644 --- a/esphomeyaml/components/sensor/dht.py +++ b/esphomeyaml/components/sensor/dht.py @@ -3,46 +3,66 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \ - CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable + CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable, \ + setup_component, PollingComponent, Pvariable from esphomeyaml.pins import gpio_output_pin_schema +DHTModel = sensor.sensor_ns.enum('DHTModel') DHT_MODELS = { - 'AUTO_DETECT': sensor.sensor_ns.DHT_MODEL_AUTO_DETECT, - 'DHT11': sensor.sensor_ns.DHT_MODEL_DHT11, - 'DHT22': sensor.sensor_ns.DHT_MODEL_DHT22, - 'AM2302': sensor.sensor_ns.DHT_MODEL_AM2302, - 'RHT03': sensor.sensor_ns.DHT_MODEL_RHT03, + 'AUTO_DETECT': DHTModel.DHT_MODEL_AUTO_DETECT, + 'DHT11': DHTModel.DHT_MODEL_DHT11, + 'DHT22': DHTModel.DHT_MODEL_DHT22, + 'AM2302': DHTModel.DHT_MODEL_AM2302, + 'RHT03': DHTModel.DHT_MODEL_RHT03, } -MakeDHTSensor = Application.MakeDHTSensor +MakeDHTSensor = Application.struct('MakeDHTSensor') +DHTComponent = sensor.sensor_ns.class_('DHTComponent', PollingComponent) +DHTTemperatureSensor = sensor.sensor_ns.class_('DHTTemperatureSensor', + sensor.EmptyPollingParentSensor) +DHTHumiditySensor = sensor.sensor_ns.class_('DHTHumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHTSensor), + cv.GenerateID(): cv.declare_variable_id(DHTComponent), vol.Required(CONF_PIN): gpio_output_pin_schema, - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DHTTemperatureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DHTHumiditySensor), + })), vol.Optional(CONF_MODEL): vol.All(vol.Upper, cv.one_of(*DHT_MODELS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - pin = None for pin in gpio_output_pin_expression(config[CONF_PIN]): yield rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], pin, config.get(CONF_UPDATE_INTERVAL)) - dht = variable(config[CONF_MAKE_ID], rhs) + make = variable(config[CONF_MAKE_ID], rhs) + dht = make.Pdht + Pvariable(config[CONF_ID], dht) + if CONF_MODEL in config: constant = DHT_MODELS[config[CONF_MODEL]] - add(dht.Pdht.set_dht_model(constant)) + add(dht.set_dht_model(constant)) - sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), - dht.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), - dht.Pmqtt_humidity, config[CONF_HUMIDITY]) + sensor.setup_sensor(dht.Pget_temperature_sensor(), + make.Pmqtt_temperature, config[CONF_TEMPERATURE]) + sensor.setup_sensor(dht.Pget_humidity_sensor(), + make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(dht, config) BUILD_FLAGS = '-DUSE_DHT_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/dht12.py b/esphomeyaml/components/sensor/dht12.py index f6526ada08..7e80ab410e 100644 --- a/esphomeyaml/components/sensor/dht12.py +++ b/esphomeyaml/components/sensor/dht12.py @@ -1,33 +1,52 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable + CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, variable, setup_component, PollingComponent, \ + Pvariable DEPENDENCIES = ['i2c'] -MakeDHT12Sensor = Application.MakeDHT12Sensor +MakeDHT12Sensor = Application.struct('MakeDHT12Sensor') +DHT12Component = sensor.sensor_ns.class_('DHT12Component', PollingComponent, i2c.I2CDevice) +DHT12TemperatureSensor = sensor.sensor_ns.class_('DHT12TemperatureSensor', + sensor.EmptyPollingParentSensor) +DHT12HumiditySensor = sensor.sensor_ns.class_('DHT12HumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHT12Sensor), - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + cv.GenerateID(): cv.declare_variable_id(DHT12Component), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DHT12TemperatureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DHT12HumiditySensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): rhs = App.make_dht12_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - dht = variable(config[CONF_MAKE_ID], rhs) + make = variable(config[CONF_MAKE_ID], rhs) + dht = make.Pdht12 + Pvariable(config[CONF_ID], dht) - sensor.setup_sensor(dht.Pdht12.Pget_temperature_sensor(), dht.Pmqtt_temperature, + sensor.setup_sensor(dht.Pget_temperature_sensor(), make.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(dht.Pdht12.Pget_humidity_sensor(), dht.Pmqtt_humidity, + sensor.setup_sensor(dht.Pget_humidity_sensor(), make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(dht, config) BUILD_FLAGS = '-DUSE_DHT12_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/duty_cycle.py b/esphomeyaml/components/sensor/duty_cycle.py index 0153f12010..85f8ec3bd9 100644 --- a/esphomeyaml/components/sensor/duty_cycle.py +++ b/esphomeyaml/components/sensor/duty_cycle.py @@ -4,25 +4,32 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, variable +from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, variable, \ + setup_component -MakeDutyCycleSensor = Application.MakeDutyCycleSensor +MakeDutyCycleSensor = Application.struct('MakeDutyCycleSensor') +DutyCycleSensor = sensor.sensor_ns.class_('DutyCycleSensor', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(DutyCycleSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDutyCycleSensor), vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - pin = None for pin in gpio_input_pin_expression(config[CONF_PIN]): yield rhs = App.make_duty_cycle_sensor(config[CONF_NAME], pin, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) sensor.setup_sensor(make.Pduty, make.Pmqtt, config) + setup_component(make.Pduty, config) BUILD_FLAGS = '-DUSE_DUTY_CYCLE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/esp32_hall.py b/esphomeyaml/components/sensor/esp32_hall.py index dd6a5fc545..ddf2af9b5e 100644 --- a/esphomeyaml/components/sensor/esp32_hall.py +++ b/esphomeyaml/components/sensor/esp32_hall.py @@ -3,22 +3,29 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.helpers import App, Application, variable, setup_component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -MakeESP32HallSensor = Application.MakeESP32HallSensor +MakeESP32HallSensor = Application.struct('MakeESP32HallSensor') +ESP32HallSensor = sensor.sensor_ns.class_('ESP32HallSensor', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ESP32HallSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeESP32HallSensor), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_esp32_hall_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) sensor.setup_sensor(make.Phall, make.Pmqtt, config) + setup_component(make.Phall, config) BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/hdc1080.py b/esphomeyaml/components/sensor/hdc1080.py index 642dc68f1f..e8decc408a 100644 --- a/esphomeyaml/components/sensor/hdc1080.py +++ b/esphomeyaml/components/sensor/hdc1080.py @@ -1,34 +1,53 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable + CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, variable, setup_component, PollingComponent, \ + Pvariable DEPENDENCIES = ['i2c'] -MakeHDC1080Sensor = Application.MakeHDC1080Sensor +MakeHDC1080Sensor = Application.struct('MakeHDC1080Sensor') +HDC1080Component = sensor.sensor_ns.class_('HDC1080Component', PollingComponent, i2c.I2CDevice) +HDC1080TemperatureSensor = sensor.sensor_ns.class_('HDC1080TemperatureSensor', + sensor.EmptyPollingParentSensor) +HDC1080HumiditySensor = sensor.sensor_ns.class_('HDC1080HumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHDC1080Sensor), - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + cv.GenerateID(): cv.declare_variable_id(HDC1080Component), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HDC1080TemperatureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HDC1080HumiditySensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - hdc1080 = variable(config[CONF_MAKE_ID], rhs) + make = variable(config[CONF_MAKE_ID], rhs) + hdc1080 = make.Phdc1080 + Pvariable(config[CONF_ID], hdc1080) - sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), - hdc1080.Pmqtt_temperature, + sensor.setup_sensor(hdc1080.Pget_temperature_sensor(), + make.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), hdc1080.Pmqtt_humidity, + sensor.setup_sensor(hdc1080.Pget_humidity_sensor(), make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(hdc1080, config) BUILD_FLAGS = '-DUSE_HDC1080_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/hlw8012.py b/esphomeyaml/components/sensor/hlw8012.py index 86ed388ef4..0f2f7d0aa5 100644 --- a/esphomeyaml/components/sensor/hlw8012.py +++ b/esphomeyaml/components/sensor/hlw8012.py @@ -1,17 +1,18 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_CF1_PIN, CONF_CF_PIN, CONF_CHANGE_MODE_EVERY, CONF_CURRENT, \ CONF_CURRENT_RESISTOR, CONF_ID, CONF_NAME, CONF_POWER, CONF_SEL_PIN, CONF_UPDATE_INTERVAL, \ CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER -from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression +from esphomeyaml.helpers import App, PollingComponent, Pvariable, add, gpio_output_pin_expression, \ + setup_component -HLW8012Component = sensor.sensor_ns.HLW8012Component -HLW8012VoltageSensor = sensor.sensor_ns.HLW8012VoltageSensor -HLW8012CurrentSensor = sensor.sensor_ns.HLW8012CurrentSensor -HLW8012PowerSensor = sensor.sensor_ns.HLW8012PowerSensor +HLW8012Component = sensor.sensor_ns.class_('HLW8012Component', PollingComponent) +HLW8012VoltageSensor = sensor.sensor_ns.class_('HLW8012VoltageSensor', sensor.EmptySensor) +HLW8012CurrentSensor = sensor.sensor_ns.class_('HLW8012CurrentSensor', sensor.EmptySensor) +HLW8012PowerSensor = sensor.sensor_ns.class_('HLW8012PowerSensor', sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(HLW8012Component), @@ -19,19 +20,25 @@ PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ vol.Required(CONF_CF_PIN): pins.input_pin, vol.Required(CONF_CF1_PIN): pins.input_pin, - vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HLW8012VoltageSensor), + })), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HLW8012CurrentSensor), + })), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HLW8012PowerSensor), + })), vol.Optional(CONF_CURRENT_RESISTOR): cv.resistance, vol.Optional(CONF_VOLTAGE_DIVIDER): cv.positive_float, vol.Optional(CONF_CHANGE_MODE_EVERY): vol.All(cv.uint32_t, vol.Range(min=1)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, CONF_POWER)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, + CONF_POWER)) def to_code(config): - sel = None for sel in gpio_output_pin_expression(config[CONF_SEL_PIN]): yield @@ -50,8 +57,19 @@ def to_code(config): sensor.register_sensor(hlw.make_power_sensor(conf[CONF_NAME]), conf) if CONF_CURRENT_RESISTOR in config: add(hlw.set_current_resistor(config[CONF_CURRENT_RESISTOR])) + if CONF_VOLTAGE_DIVIDER in config: + add(hlw.set_voltage_divider(config[CONF_VOLTAGE_DIVIDER])) if CONF_CHANGE_MODE_EVERY in config: add(hlw.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY])) + setup_component(hlw, config) BUILD_FLAGS = '-DUSE_HLW8012' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/hmc5883l.py b/esphomeyaml/components/sensor/hmc5883l.py index f82ae5df1b..a4ed04cf0a 100644 --- a/esphomeyaml/components/sensor/hmc5883l.py +++ b/esphomeyaml/components/sensor/hmc5883l.py @@ -2,9 +2,9 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_RANGE -from esphomeyaml.helpers import App, Pvariable, add +from esphomeyaml.helpers import App, Pvariable, add, setup_component, PollingComponent DEPENDENCIES = ['i2c'] @@ -13,19 +13,23 @@ CONF_FIELD_STRENGTH_Y = 'field_strength_y' CONF_FIELD_STRENGTH_Z = 'field_strength_z' CONF_HEADING = 'heading' -HMC5883LComponent = sensor.sensor_ns.HMC5883LComponent -HMC5883LFieldStrengthSensor = sensor.sensor_ns.HMC5883LFieldStrengthSensor -HMC5883LHeadingSensor = sensor.sensor_ns.HMC5883LHeadingSensor +HMC5883LComponent = sensor.sensor_ns.class_('HMC5883LComponent', PollingComponent, i2c.I2CDevice) +HMC5883LFieldStrengthSensor = sensor.sensor_ns.class_('HMC5883LFieldStrengthSensor', + sensor.EmptyPollingParentSensor) +HMC5883LHeadingSensor = sensor.sensor_ns.class_('HMC5883LHeadingSensor', + sensor.EmptyPollingParentSensor) + +HMC5883LRange = sensor.sensor_ns.enum('HMC5883LRange') HMC5883L_RANGES = { - 88: sensor.sensor_ns.HMC5883L_RANGE_88_UT, - 130: sensor.sensor_ns.HMC5883L_RANGE_130_UT, - 190: sensor.sensor_ns.HMC5883L_RANGE_190_UT, - 250: sensor.sensor_ns.HMC5883L_RANGE_250_UT, - 400: sensor.sensor_ns.HMC5883L_RANGE_400_UT, - 470: sensor.sensor_ns.HMC5883L_RANGE_470_UT, - 560: sensor.sensor_ns.HMC5883L_RANGE_560_UT, - 810: sensor.sensor_ns.HMC5883L_RANGE_810_UT, + 88: HMC5883LRange.HMC5883L_RANGE_88_UT, + 130: HMC5883LRange.HMC5883L_RANGE_130_UT, + 190: HMC5883LRange.HMC5883L_RANGE_190_UT, + 250: HMC5883LRange.HMC5883L_RANGE_250_UT, + 400: HMC5883LRange.HMC5883L_RANGE_400_UT, + 470: HMC5883LRange.HMC5883L_RANGE_470_UT, + 560: HMC5883LRange.HMC5883L_RANGE_560_UT, + 810: HMC5883LRange.HMC5883L_RANGE_810_UT, } @@ -36,17 +40,27 @@ def validate_range(value): return cv.one_of(*HMC5883L_RANGES)(int(value)) +SENSOR_KEYS = [CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, + CONF_HEADING] + PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(HMC5883LComponent), vol.Optional(CONF_ADDRESS): cv.i2c_address, - vol.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor), + })), + vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor), + })), + vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor), + })), + vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HMC5883LHeadingSensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, vol.Optional(CONF_RANGE): validate_range, -}), cv.has_at_least_one_key(CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, - CONF_HEADING)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) def to_code(config): @@ -68,6 +82,15 @@ def to_code(config): if CONF_HEADING in config: conf = config[CONF_HEADING] sensor.register_sensor(hmc.Pmake_heading_sensor(conf[CONF_NAME]), conf) + setup_component(hmc, config) BUILD_FLAGS = '-DUSE_HMC5883L' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, CONF_HEADING): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/htu21d.py b/esphomeyaml/components/sensor/htu21d.py index c0a381107d..6c4b077d80 100644 --- a/esphomeyaml/components/sensor/htu21d.py +++ b/esphomeyaml/components/sensor/htu21d.py @@ -1,32 +1,52 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable + CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, PollingComponent, setup_component, variable, \ + Pvariable DEPENDENCIES = ['i2c'] -MakeHTU21DSensor = Application.MakeHTU21DSensor +MakeHTU21DSensor = Application.struct('MakeHTU21DSensor') +HTU21DComponent = sensor.sensor_ns.class_('HTU21DComponent', PollingComponent, i2c.I2CDevice) +HTU21DTemperatureSensor = sensor.sensor_ns.class_('HTU21DTemperatureSensor', + sensor.EmptyPollingParentSensor) +HTU21DHumiditySensor = sensor.sensor_ns.class_('HTU21DHumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HTU21DComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHTU21DSensor), - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HTU21DTemperatureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HTU21DHumiditySensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - htu21d = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), htu21d.Pmqtt_temperature, + make = variable(config[CONF_MAKE_ID], rhs) + htu21d = make.Phtu21d + Pvariable(config[CONF_ID], htu21d) + + sensor.setup_sensor(htu21d.Pget_temperature_sensor(), make.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), htu21d.Pmqtt_humidity, + sensor.setup_sensor(htu21d.Pget_humidity_sensor(), make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(htu21d, config) BUILD_FLAGS = '-DUSE_HTU21D_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/hx711.py b/esphomeyaml/components/sensor/hx711.py index 1a26b0762b..9f35e31cca 100644 --- a/esphomeyaml/components/sensor/hx711.py +++ b/esphomeyaml/components/sensor/hx711.py @@ -4,43 +4,51 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_CLK_PIN -from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable +from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable, \ + setup_component -MakeHX711Sensor = Application.MakeHX711Sensor +MakeHX711Sensor = Application.struct('MakeHX711Sensor') +HX711Sensor = sensor.sensor_ns.class_('HX711Sensor', sensor.PollingSensorComponent) CONF_DOUT_PIN = 'dout_pin' +HX711Gain = sensor.sensor_ns.enum('HX711Gain') GAINS = { - 128: sensor.sensor_ns.HX711_GAIN_128, - 32: sensor.sensor_ns.HX711_GAIN_32, - 64: sensor.sensor_ns.HX711_GAIN_64, + 128: HX711Gain.HX711_GAIN_128, + 32: HX711Gain.HX711_GAIN_32, + 64: HX711Gain.HX711_GAIN_64, } PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HX711Sensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHX711Sensor), vol.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_GAIN): vol.All(cv.int_, cv.one_of(*GAINS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - dout_pin = None for dout_pin in gpio_input_pin_expression(config[CONF_DOUT_PIN]): yield - sck_pin = None for sck_pin in gpio_input_pin_expression(config[CONF_CLK_PIN]): yield rhs = App.make_hx711_sensor(config[CONF_NAME], dout_pin, sck_pin, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) + hx711 = make.Phx711 if CONF_GAIN in config: - add(make.Phx711.set_gain(GAINS[config[CONF_GAIN]])) + add(hx711.set_gain(GAINS[config[CONF_GAIN]])) - sensor.setup_sensor(make.Phx711, make.Pmqtt, config) + sensor.setup_sensor(hx711, make.Pmqtt, config) + setup_component(hx711, config) BUILD_FLAGS = '-DUSE_HX711' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ina219.py b/esphomeyaml/components/sensor/ina219.py index 3aefb945e1..647c099978 100644 --- a/esphomeyaml/components/sensor/ina219.py +++ b/esphomeyaml/components/sensor/ina219.py @@ -1,34 +1,46 @@ # coding=utf-8 import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_ADDRESS, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \ - CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_UPDATE_INTERVAL, CONF_BUS_VOLTAGE, \ - CONF_SHUNT_VOLTAGE, CONF_SHUNT_RESISTANCE -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \ + CONF_MAX_CURRENT, CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_SHUNT_RESISTANCE, \ + CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, PollingComponent, Pvariable, setup_component DEPENDENCIES = ['i2c'] -INA219Component = sensor.sensor_ns.INA219Component -INA219VoltageSensor = sensor.sensor_ns.INA219VoltageSensor -INA219CurrentSensor = sensor.sensor_ns.INA219CurrentSensor -INA219PowerSensor = sensor.sensor_ns.INA219PowerSensor +INA219Component = sensor.sensor_ns.class_('INA219Component', PollingComponent, i2c.I2CDevice) +INA219VoltageSensor = sensor.sensor_ns.class_('INA219VoltageSensor', + sensor.EmptyPollingParentSensor) +INA219CurrentSensor = sensor.sensor_ns.class_('INA219CurrentSensor', + sensor.EmptyPollingParentSensor) +INA219PowerSensor = sensor.sensor_ns.class_('INA219PowerSensor', sensor.EmptyPollingParentSensor) + +SENSOR_KEYS = [CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, + CONF_POWER] PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(INA219Component), vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address, - vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA219VoltageSensor), + })), + vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA219VoltageSensor), + })), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA219CurrentSensor), + })), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA219PowerSensor), + })), vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance, vol.Range(min=0.0, max=32.0)), vol.Optional(CONF_MAX_VOLTAGE, default=32.0): vol.All(cv.voltage, vol.Range(min=0.0, max=32.0)), vol.Optional(CONF_MAX_CURRENT, default=3.2): vol.All(cv.current, vol.Range(min=0.0)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, - CONF_POWER)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) def to_code(config): @@ -48,6 +60,15 @@ def to_code(config): if CONF_POWER in config: conf = config[CONF_POWER] sensor.register_sensor(ina.Pmake_power_sensor(conf[CONF_NAME]), conf) + setup_component(ina, config) BUILD_FLAGS = '-DUSE_INA219' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/ina3221.py b/esphomeyaml/components/sensor/ina3221.py index bd9d574906..0ac8dff1d7 100644 --- a/esphomeyaml/components/sensor/ina3221.py +++ b/esphomeyaml/components/sensor/ina3221.py @@ -1,11 +1,11 @@ # coding=utf-8 import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_NAME, \ CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Pvariable, add +from esphomeyaml.helpers import App, PollingComponent, Pvariable, add, setup_component DEPENDENCIES = ['i2c'] @@ -13,20 +13,31 @@ CONF_CHANNEL_1 = 'channel_1' CONF_CHANNEL_2 = 'channel_2' CONF_CHANNEL_3 = 'channel_3' -INA3221Component = sensor.sensor_ns.INA3221Component -INA3221VoltageSensor = sensor.sensor_ns.INA3221VoltageSensor -INA3221CurrentSensor = sensor.sensor_ns.INA3221CurrentSensor -INA3221PowerSensor = sensor.sensor_ns.INA3221PowerSensor +INA3221Component = sensor.sensor_ns.class_('INA3221Component', PollingComponent, i2c.I2CDevice) +INA3221VoltageSensor = sensor.sensor_ns.class_('INA3221VoltageSensor', + sensor.EmptyPollingParentSensor) +INA3221CurrentSensor = sensor.sensor_ns.class_('INA3221CurrentSensor', + sensor.EmptyPollingParentSensor) +INA3221PowerSensor = sensor.sensor_ns.class_('INA3221PowerSensor', sensor.EmptyPollingParentSensor) + +SENSOR_KEYS = [CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER] INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({ - vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA3221VoltageSensor), + })), + vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA3221VoltageSensor), + })), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA3221CurrentSensor), + })), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA3221PowerSensor), + })), vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance, vol.Range(min=0.0, max=32.0)), -}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, - CONF_POWER)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(INA3221Component), @@ -60,5 +71,19 @@ def to_code(config): c = conf[CONF_POWER] sensor.register_sensor(ina.Pmake_power_sensor(i, c[CONF_NAME]), c) + setup_component(ina, config) + BUILD_FLAGS = '-DUSE_INA3221' + + +def to_hass_config(data, config): + ret = [] + for channel in (CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3): + if channel not in config: + continue + conf = config[channel] + for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in conf: + ret.append(sensor.core_to_hass_config(data, conf[key])) + return ret diff --git a/esphomeyaml/components/sensor/max6675.py b/esphomeyaml/components/sensor/max6675.py index ae9ce0ee16..5bf3907ebb 100644 --- a/esphomeyaml/components/sensor/max6675.py +++ b/esphomeyaml/components/sensor/max6675.py @@ -2,33 +2,41 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, spi from esphomeyaml.components.spi import SPIComponent from esphomeyaml.const import CONF_CS_PIN, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, variable +from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, \ + variable, setup_component -MakeMAX6675Sensor = Application.MakeMAX6675Sensor +MakeMAX6675Sensor = Application.struct('MakeMAX6675Sensor') +MAX6675Sensor = sensor.sensor_ns.class_('MAX6675Sensor', sensor.PollingSensorComponent, + spi.SPIDevice) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MAX6675Sensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor), cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - spi = None - for spi in get_variable(config[CONF_SPI_ID]): + for spi_ in get_variable(config[CONF_SPI_ID]): yield - cs = None for cs in gpio_output_pin_expression(config[CONF_CS_PIN]): yield - rhs = App.make_max6675_sensor(config[CONF_NAME], spi, cs, + rhs = App.make_max6675_sensor(config[CONF_NAME], spi_, cs, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(make.Pmax6675, make.Pmqtt, config) + max6675 = make.Pmax6675 + sensor.setup_sensor(max6675, make.Pmqtt, config) + setup_component(max6675, config) BUILD_FLAGS = '-DUSE_MAX6675_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/mhz19.py b/esphomeyaml/components/sensor/mhz19.py index 7af15aa341..06de766952 100644 --- a/esphomeyaml/components/sensor/mhz19.py +++ b/esphomeyaml/components/sensor/mhz19.py @@ -1,38 +1,58 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, uart from esphomeyaml.components.uart import UARTComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_CO2, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, CONF_UART_ID, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, get_variable, variable + CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, PollingComponent, get_variable, setup_component, \ + variable, Pvariable DEPENDENCIES = ['uart'] -MakeMHZ19Sensor = Application.MakeMHZ19Sensor +MakeMHZ19Sensor = Application.struct('MakeMHZ19Sensor') +MHZ19Component = sensor.sensor_ns.class_('MHZ19Component', PollingComponent, uart.UARTDevice) +MHZ19TemperatureSensor = sensor.sensor_ns.class_('MHZ19TemperatureSensor', + sensor.EmptyPollingParentSensor) +MHZ19CO2Sensor = sensor.sensor_ns.class_('MHZ19CO2Sensor', sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MHZ19Component), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMHZ19Sensor), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), - vol.Required(CONF_CO2): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_CO2): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MHZ19CO2Sensor), + })), + vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MHZ19TemperatureSensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - uart = None - for uart in get_variable(config[CONF_UART_ID]): + for uart_ in get_variable(config[CONF_UART_ID]): yield - rhs = App.make_mhz19_sensor(uart, config[CONF_CO2][CONF_NAME], + rhs = App.make_mhz19_sensor(uart_, config[CONF_CO2][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) mhz19 = make.Pmhz19 + Pvariable(config[CONF_ID], mhz19) sensor.setup_sensor(mhz19.Pget_co2_sensor(), make.Pmqtt, config[CONF_CO2]) if CONF_TEMPERATURE in config: sensor.register_sensor(mhz19.Pmake_temperature_sensor(config[CONF_TEMPERATURE][CONF_NAME]), config[CONF_TEMPERATURE]) + setup_component(mhz19, config) + BUILD_FLAGS = '-DUSE_MHZ19' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_CO2, CONF_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/mpu6050.py b/esphomeyaml/components/sensor/mpu6050.py index 8aa989312e..8fce2d8280 100644 --- a/esphomeyaml/components/sensor/mpu6050.py +++ b/esphomeyaml/components/sensor/mpu6050.py @@ -1,10 +1,10 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.helpers import App, PollingComponent, Pvariable, setup_component DEPENDENCIES = ['i2c'] @@ -15,24 +15,41 @@ CONF_GYRO_X = 'gyro_x' CONF_GYRO_Y = 'gyro_y' CONF_GYRO_Z = 'gyro_z' -MPU6050Component = sensor.sensor_ns.MPU6050Component -MPU6050AccelSensor = sensor.sensor_ns.MPU6050AccelSensor -MPU6050GyroSensor = sensor.sensor_ns.MPU6050GyroSensor -MPU6050TemperatureSensor = sensor.sensor_ns.MPU6050TemperatureSensor +MPU6050Component = sensor.sensor_ns.class_('MPU6050Component', PollingComponent, i2c.I2CDevice) +MPU6050AccelSensor = sensor.sensor_ns.class_('MPU6050AccelSensor', sensor.EmptyPollingParentSensor) +MPU6050GyroSensor = sensor.sensor_ns.class_('MPU6050GyroSensor', sensor.EmptyPollingParentSensor) +MPU6050TemperatureSensor = sensor.sensor_ns.class_('MPU6050TemperatureSensor', + sensor.EmptyPollingParentSensor) + +SENSOR_KEYS = [CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z, + CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z] PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(MPU6050Component), vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address, - vol.Optional(CONF_ACCEL_X): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_ACCEL_Y): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_ACCEL_Z): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_GYRO_X): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_GYRO_Y): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_GYRO_Z): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_ACCEL_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor), + })), + vol.Optional(CONF_ACCEL_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor), + })), + vol.Optional(CONF_ACCEL_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor), + })), + vol.Optional(CONF_GYRO_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor), + })), + vol.Optional(CONF_GYRO_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor), + })), + vol.Optional(CONF_GYRO_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor), + })), + vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MPU6050TemperatureSensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}), cv.has_at_least_one_key(CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z, - CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) def to_code(config): @@ -67,5 +84,16 @@ def to_code(config): rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME]) sensor.register_sensor(rhs, conf) + setup_component(mpu, config) + BUILD_FLAGS = '-DUSE_MPU6050' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z, CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z, + CONF_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/mqtt_subscribe.py b/esphomeyaml/components/sensor/mqtt_subscribe.py index 2bfe71d98d..e06cd456ae 100644 --- a/esphomeyaml/components/sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/sensor/mqtt_subscribe.py @@ -3,27 +3,35 @@ import voluptuous as vol from esphomeyaml.components import sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_QOS, CONF_TOPIC -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component, Component DEPENDENCIES = ['mqtt'] -MakeMQTTSubscribeSensor = Application.MakeMQTTSubscribeSensor +MakeMQTTSubscribeSensor = Application.struct('MakeMQTTSubscribeSensor') +MQTTSubscribeSensor = sensor.sensor_ns.class_('MQTTSubscribeSensor', sensor.Sensor, Component) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeSensor), vol.Required(CONF_TOPIC): cv.subscribe_topic, vol.Optional(CONF_QOS): cv.mqtt_qos, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_mqtt_subscribe_sensor(config[CONF_NAME], config[CONF_TOPIC]) make = variable(config[CONF_MAKE_ID], rhs) + subs = make.Psensor if CONF_QOS in config: - add(make.Psensor.set_qos(config[CONF_QOS])) + add(subs.set_qos(config[CONF_QOS])) - sensor.setup_sensor(make.Psensor, make.Pmqtt, config) + sensor.setup_sensor(subs, make.Pmqtt, config) + setup_component(subs, config) BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ms5611.py b/esphomeyaml/components/sensor/ms5611.py index 92afc930dd..de176b03db 100644 --- a/esphomeyaml/components/sensor/ms5611.py +++ b/esphomeyaml/components/sensor/ms5611.py @@ -1,22 +1,34 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \ +from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \ CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, PollingComponent, Pvariable, add, \ + setup_component, \ + variable DEPENDENCIES = ['i2c'] -MakeMS5611Sensor = Application.MakeMS5611Sensor +MakeMS5611Sensor = Application.struct('MakeMS5611Sensor') +MS5611Component = sensor.sensor_ns.class_('MS5611Component', PollingComponent, i2c.I2CDevice) +MS5611TemperatureSensor = sensor.sensor_ns.class_('MS5611TemperatureSensor', + sensor.EmptyPollingParentSensor) +MS5611PressureSensor = sensor.sensor_ns.class_('MS5611PressureSensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MS5611Component), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMS5611Sensor), vol.Optional(CONF_ADDRESS): cv.i2c_address, - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MS5611TemperatureSensor), + })), + vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MS5611PressureSensor), + })), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -24,14 +36,22 @@ def to_code(config): config[CONF_PRESSURE][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) + ms5611 = make.Pms5611 + Pvariable(config[CONF_ID], ms5611) if CONF_ADDRESS in config: - add(make.Pms5611.set_address(config[CONF_ADDRESS])) + add(ms5611.set_address(config[CONF_ADDRESS])) - sensor.setup_sensor(make.Pms5611.Pget_temperature_sensor(), make.Pmqtt_temperature, + sensor.setup_sensor(ms5611.Pget_temperature_sensor(), make.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(make.Pms5611.Pget_pressure_sensor(), make.Pmqtt_pressure, + sensor.setup_sensor(ms5611.Pget_pressure_sensor(), make.Pmqtt_pressure, config[CONF_PRESSURE]) + setup_component(ms5611, config) BUILD_FLAGS = '-DUSE_MS5611' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/pmsx003.py b/esphomeyaml/components/sensor/pmsx003.py index aa61a00e38..fe4f2bc089 100644 --- a/esphomeyaml/components/sensor/pmsx003.py +++ b/esphomeyaml/components/sensor/pmsx003.py @@ -1,24 +1,26 @@ import voluptuous as vol -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, uart from esphomeyaml.components.uart import UARTComponent import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_FORMALDEHYDE, CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_PM_10_0, \ CONF_PM_1_0, CONF_PM_2_5, CONF_TEMPERATURE, CONF_TYPE, CONF_UART_ID -from esphomeyaml.helpers import App, Pvariable, get_variable +from esphomeyaml.helpers import App, Pvariable, get_variable, setup_component, Component DEPENDENCIES = ['uart'] -PMSX003Component = sensor.sensor_ns.PMSX003Component +PMSX003Component = sensor.sensor_ns.class_('PMSX003Component', uart.UARTDevice, Component) +PMSX003Sensor = sensor.sensor_ns.class_('PMSX003Sensor', sensor.Sensor) CONF_PMSX003 = 'PMSX003' CONF_PMS5003T = 'PMS5003T' CONF_PMS5003ST = 'PMS5003ST' +PMSX003Type = sensor.sensor_ns.enum('PMSX003Type') PMSX003_TYPES = { - CONF_PMSX003: sensor.sensor_ns.PMSX003_TYPE_X003, - CONF_PMS5003T: sensor.sensor_ns.PMSX003_TYPE_5003T, - CONF_PMS5003ST: sensor.sensor_ns.PMSX003_TYPE_5003ST, + CONF_PMSX003: PMSX003Type.PMSX003_TYPE_X003, + CONF_PMS5003T: PMSX003Type.PMSX003_TYPE_5003T, + CONF_PMS5003ST: PMSX003Type.PMSX003_TYPE_5003ST, } SENSORS_TO_TYPE = { @@ -38,25 +40,30 @@ def validate_pmsx003_sensors(value): return value +PMSX003_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(PMSX003Sensor), +}) + + PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(PMSX003Component), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of(*PMSX003_TYPES)), - vol.Optional(CONF_PM_1_0): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_PM_2_5): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_PM_10_0): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_FORMALDEHYDE): cv.nameable(sensor.SENSOR_SCHEMA), -}), cv.has_at_least_one_key(*SENSORS_TO_TYPE)) + vol.Optional(CONF_PM_1_0): cv.nameable(PMSX003_SENSOR_SCHEMA), + vol.Optional(CONF_PM_2_5): cv.nameable(PMSX003_SENSOR_SCHEMA), + vol.Optional(CONF_PM_10_0): cv.nameable(PMSX003_SENSOR_SCHEMA), + vol.Optional(CONF_TEMPERATURE): cv.nameable(PMSX003_SENSOR_SCHEMA), + vol.Optional(CONF_HUMIDITY): cv.nameable(PMSX003_SENSOR_SCHEMA), + vol.Optional(CONF_FORMALDEHYDE): cv.nameable(PMSX003_SENSOR_SCHEMA), +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSORS_TO_TYPE)) def to_code(config): - for uart in get_variable(config[CONF_UART_ID]): + for uart_ in get_variable(config[CONF_UART_ID]): yield - rhs = App.make_pmsx003(uart, PMSX003_TYPES[config[CONF_TYPE]]) + rhs = App.make_pmsx003(uart_, PMSX003_TYPES[config[CONF_TYPE]]) pms = Pvariable(config[CONF_ID], rhs) if CONF_PM_1_0 in config: @@ -83,5 +90,16 @@ def to_code(config): conf = config[CONF_FORMALDEHYDE] sensor.register_sensor(pms.make_formaldehyde_sensor(conf[CONF_NAME]), conf) + setup_component(pms, config) + BUILD_FLAGS = '-DUSE_PMSX003' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_PM_1_0, CONF_PM_2_5, CONF_PM_10_0, CONF_TEMPERATURE, CONF_HUMIDITY, + CONF_FORMALDEHYDE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/pulse_counter.py b/esphomeyaml/components/sensor/pulse_counter.py index 575d1a76c4..b73d62123e 100644 --- a/esphomeyaml/components/sensor/pulse_counter.py +++ b/esphomeyaml/components/sensor/pulse_counter.py @@ -6,17 +6,23 @@ from esphomeyaml.components import sensor from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_INTERNAL_FILTER, \ CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \ ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import App, Application, add, variable, gpio_input_pin_expression +from esphomeyaml.helpers import App, Application, add, variable, gpio_input_pin_expression, \ + setup_component +PulseCounterCountMode = sensor.sensor_ns.enum('PulseCounterCountMode') COUNT_MODES = { - 'DISABLE': sensor.sensor_ns.PULSE_COUNTER_DISABLE, - 'INCREMENT': sensor.sensor_ns.PULSE_COUNTER_INCREMENT, - 'DECREMENT': sensor.sensor_ns.PULSE_COUNTER_DECREMENT, + 'DISABLE': PulseCounterCountMode.PULSE_COUNTER_DISABLE, + 'INCREMENT': PulseCounterCountMode.PULSE_COUNTER_INCREMENT, + 'DECREMENT': PulseCounterCountMode.PULSE_COUNTER_DECREMENT, } COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES)) -MakePulseCounterSensor = Application.MakePulseCounterSensor +PulseCounterBase = sensor.sensor_ns.class_('PulseCounterBase') +MakePulseCounterSensor = Application.struct('MakePulseCounterSensor') +PulseCounterSensorComponent = sensor.sensor_ns.class_('PulseCounterSensorComponent', + sensor.PollingSensorComponent, + PulseCounterBase) def validate_internal_filter(value): @@ -33,6 +39,7 @@ def validate_internal_filter(value): PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(PulseCounterSensorComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePulseCounterSensor), vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, vol.Optional(CONF_COUNT_MODE): vol.Schema({ @@ -44,24 +51,30 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_PULL_MODE): cv.invalid("The pull_mode option has been removed in 1.7.0, " "please use the pin mode schema now.") -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - pin = None for pin in gpio_input_pin_expression(config[CONF_PIN]): yield rhs = App.make_pulse_counter_sensor(config[CONF_NAME], pin, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) pcnt = make.Ppcnt + if CONF_COUNT_MODE in config: rising_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_RISING_EDGE]] falling_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_FALLING_EDGE]] add(pcnt.set_edge_mode(rising_edge, falling_edge)) if CONF_INTERNAL_FILTER in config: add(pcnt.set_filter_us(config[CONF_INTERNAL_FILTER])) - sensor.setup_sensor(make.Ppcnt, make.Pmqtt, config) + + sensor.setup_sensor(pcnt, make.Pmqtt, config) + setup_component(pcnt, config) BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/rotary_encoder.py b/esphomeyaml/components/sensor/rotary_encoder.py index d872ed9c69..1be3d44803 100644 --- a/esphomeyaml/components/sensor/rotary_encoder.py +++ b/esphomeyaml/components/sensor/rotary_encoder.py @@ -4,39 +4,42 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION -from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable +from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable, \ + setup_component, Component +RotaryEncoderResolution = sensor.sensor_ns.enum('RotaryEncoderResolution') RESOLUTIONS = { - '1': sensor.sensor_ns.ROTARY_ENCODER_1_PULSE_PER_CYCLE, - '2': sensor.sensor_ns.ROTARY_ENCODER_2_PULSES_PER_CYCLE, - '4': sensor.sensor_ns.ROTARY_ENCODER_4_PULSES_PER_CYCLE, + '1': RotaryEncoderResolution.ROTARY_ENCODER_1_PULSE_PER_CYCLE, + '2': RotaryEncoderResolution.ROTARY_ENCODER_2_PULSES_PER_CYCLE, + '4': RotaryEncoderResolution.ROTARY_ENCODER_4_PULSES_PER_CYCLE, } CONF_PIN_A = 'pin_a' CONF_PIN_B = 'pin_b' CONF_PIN_RESET = 'pin_reset' -MakeRotaryEncoderSensor = Application.MakeRotaryEncoderSensor +MakeRotaryEncoderSensor = Application.struct('MakeRotaryEncoderSensor') +RotaryEncoderSensor = sensor.sensor_ns.class_('RotaryEncoderSensor', sensor.Sensor, Component) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(RotaryEncoderSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRotaryEncoderSensor), vol.Required(CONF_PIN_A): pins.internal_gpio_input_pin_schema, vol.Required(CONF_PIN_B): pins.internal_gpio_input_pin_schema, vol.Optional(CONF_PIN_RESET): pins.internal_gpio_input_pin_schema, vol.Optional(CONF_RESOLUTION): vol.All(cv.string, cv.one_of(*RESOLUTIONS)), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - pin_a = None for pin_a in gpio_input_pin_expression(config[CONF_PIN_A]): yield - pin_b = None for pin_b in gpio_input_pin_expression(config[CONF_PIN_B]): yield rhs = App.make_rotary_encoder_sensor(config[CONF_NAME], pin_a, pin_b) make = variable(config[CONF_MAKE_ID], rhs) encoder = make.Protary_encoder + if CONF_PIN_RESET in config: pin_i = None for pin_i in gpio_input_pin_expression(config[CONF_PIN_RESET]): @@ -45,7 +48,13 @@ def to_code(config): if CONF_RESOLUTION in config: resolution = RESOLUTIONS[config[CONF_RESOLUTION]] add(encoder.set_resolution(resolution)) + sensor.setup_sensor(encoder, make.Pmqtt, config) + setup_component(encoder, config) BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/sht3xd.py b/esphomeyaml/components/sensor/sht3xd.py index f78d988dc8..ea6b81e75d 100644 --- a/esphomeyaml/components/sensor/sht3xd.py +++ b/esphomeyaml/components/sensor/sht3xd.py @@ -1,25 +1,33 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_ACCURACY, CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \ - CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \ + CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import App, Application, PollingComponent, setup_component, variable, \ + Pvariable DEPENDENCIES = ['i2c'] -MakeSHT3XDSensor = Application.MakeSHT3XDSensor +MakeSHT3XDSensor = Application.struct('MakeSHT3XDSensor') +SHT3XDComponent = sensor.sensor_ns.class_('SHT3XDComponent', PollingComponent, i2c.I2CDevice) +SHT3XDTemperatureSensor = sensor.sensor_ns.class_('SHT3XDTemperatureSensor', + sensor.EmptyPollingParentSensor) +SHT3XDHumiditySensor = sensor.sensor_ns.class_('SHT3XDHumiditySensor', + sensor.EmptyPollingParentSensor) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(SHT3XDComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSHT3XDSensor), - vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(SHT3XDTemperatureSensor), + })), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(SHT3XDHumiditySensor), + })), vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, - - vol.Optional(CONF_ACCURACY): cv.invalid("The accuracy option has been removed and now " - "defaults to HIGH."), -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -27,12 +35,20 @@ def to_code(config): config[CONF_HUMIDITY][CONF_NAME], config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL)) - sht3xd = variable(config[CONF_MAKE_ID], rhs) + make = variable(config[CONF_MAKE_ID], rhs) + sht3xd = make.Psht3xd + Pvariable(config[CONF_ID], sht3xd) - sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), sht3xd.Pmqtt_temperature, + sensor.setup_sensor(sht3xd.Pget_temperature_sensor(), make.Pmqtt_temperature, config[CONF_TEMPERATURE]) - sensor.setup_sensor(sht3xd.Psht3xd.Pget_humidity_sensor(), sht3xd.Pmqtt_humidity, + sensor.setup_sensor(sht3xd.Pget_humidity_sensor(), make.Pmqtt_humidity, config[CONF_HUMIDITY]) + setup_component(sht3xd, config) BUILD_FLAGS = '-DUSE_SHT3XD' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/tcs34725.py b/esphomeyaml/components/sensor/tcs34725.py index 5da00be172..8322e1f054 100644 --- a/esphomeyaml/components/sensor/tcs34725.py +++ b/esphomeyaml/components/sensor/tcs34725.py @@ -1,11 +1,11 @@ # coding=utf-8 import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ADDRESS, CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \ CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Pvariable, add +from esphomeyaml.helpers import App, PollingComponent, Pvariable, add, setup_component DEPENDENCIES = ['i2c'] @@ -14,38 +14,58 @@ CONF_GREEN_CHANNEL = 'green_channel' CONF_BLUE_CHANNEL = 'blue_channel' CONF_CLEAR_CHANNEL = 'clear_channel' -TCS34725Component = sensor.sensor_ns.TCS34725Component +TCS34725Component = sensor.sensor_ns.class_('TCS34725Component', PollingComponent, + i2c.I2CDevice) +TCS34725IntegrationTime = sensor.sensor_ns.enum('TCS34725IntegrationTime') TCS34725_INTEGRATION_TIMES = { - '2.4ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_2_4MS, - '24ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_24MS, - '50ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_50MS, - '101ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_101MS, - '154ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_154MS, - '700ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_700MS, + '2.4ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS, + '24ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS, + '50ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS, + '101ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_101MS, + '154ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_154MS, + '700ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_700MS, } +TCS34725Gain = sensor.sensor_ns.enum('TCS34725Gain') TCS34725_GAINS = { - '1X': sensor.sensor_ns.TCS34725_GAIN_1X, - '4X': sensor.sensor_ns.TCS34725_GAIN_4X, - '16X': sensor.sensor_ns.TCS34725_GAIN_16X, - '60X': sensor.sensor_ns.TCS34725_GAIN_60X, + '1X': TCS34725Gain.TCS34725_GAIN_1X, + '4X': TCS34725Gain.TCS34725_GAIN_4X, + '16X': TCS34725Gain.TCS34725_GAIN_16X, + '60X': TCS34725Gain.TCS34725_GAIN_60X, } +TCS35725IlluminanceSensor = sensor.sensor_ns.class_('TCS35725IlluminanceSensor', + sensor.EmptyPollingParentSensor) +TCS35725ColorTemperatureSensor = sensor.sensor_ns.class_('TCS35725ColorTemperatureSensor', + sensor.EmptyPollingParentSensor) +TCS35725ColorChannelSensor = sensor.sensor_ns.class_('TCS35725ColorChannelSensor', + sensor.EmptyPollingParentSensor) + +COLOR_CHANNEL_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TCS35725ColorChannelSensor), +}) + +SENSOR_KEYS = [CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, + CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE] + PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(TCS34725Component), vol.Optional(CONF_ADDRESS): cv.i2c_address, - vol.Optional(CONF_RED_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_RED_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA), + vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA), + vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA), + vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA), + vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TCS35725IlluminanceSensor), + })), + vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TCS35725ColorTemperatureSensor), + })), vol.Optional(CONF_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES), vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}), cv.has_at_least_one_key(CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, - CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) def to_code(config): @@ -76,5 +96,16 @@ def to_code(config): conf = config[CONF_COLOR_TEMPERATURE] sensor.register_sensor(tcs.Pmake_color_temperature_sensor(conf[CONF_NAME]), conf) + setup_component(tcs, config) + BUILD_FLAGS = '-DUSE_TCS34725' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, CONF_CLEAR_CHANNEL, + CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/template.py b/esphomeyaml/components/sensor/template.py index 6ae6138d5c..d34f96da5d 100644 --- a/esphomeyaml/components/sensor/template.py +++ b/esphomeyaml/components/sensor/template.py @@ -3,27 +3,36 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional, add +from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional, add, \ + setup_component -MakeTemplateSensor = Application.MakeTemplateSensor +MakeTemplateSensor = Application.struct('MakeTemplateSensor') +TemplateSensor = sensor.sensor_ns.class_('TemplateSensor', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TemplateSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSensor), vol.Required(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_template_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config) + template = make.Ptemplate_ + + sensor.setup_sensor(template, make.Pmqtt, config) + setup_component(template, config) - template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(float_)): yield - add(make.Ptemplate_.set_template(template_)) + add(template.set_template(template_)) BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/total_daily_energy.py b/esphomeyaml/components/sensor/total_daily_energy.py new file mode 100644 index 0000000000..86f4678a2b --- /dev/null +++ b/esphomeyaml/components/sensor/total_daily_energy.py @@ -0,0 +1,39 @@ +import voluptuous as vol + +from esphomeyaml.components import sensor, time +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_TIME_ID +from esphomeyaml.helpers import App, Application, Component, get_variable, setup_component, variable + +DEPENDENCIES = ['time'] + +CONF_POWER_ID = 'power_id' +MakeTotalDailyEnergySensor = Application.struct('MakeTotalDailyEnergySensor') +TotalDailyEnergy = sensor.sensor_ns.class_('TotalDailyEnergy', sensor.Sensor, Component) + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TotalDailyEnergy), + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTotalDailyEnergySensor), + cv.GenerateID(CONF_TIME_ID): cv.use_variable_id(time.RealTimeClockComponent), + vol.Required(CONF_POWER_ID): cv.use_variable_id(sensor.Sensor), +}).extend(cv.COMPONENT_SCHEMA.schema)) + + +def to_code(config): + for time_ in get_variable(config[CONF_TIME_ID]): + yield + for sens in get_variable(config[CONF_POWER_ID]): + yield + rhs = App.make_total_daily_energy_sensor(config[CONF_NAME], time_, sens) + make = variable(config[CONF_MAKE_ID], rhs) + total_energy = make.Ptotal_energy + + sensor.setup_sensor(total_energy, make.Pmqtt, config) + setup_component(total_energy, config) + + +BUILD_FLAGS = '-DUSE_TOTAL_DAILY_ENERGY_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/tsl2561.py b/esphomeyaml/components/sensor/tsl2561.py index b97919038d..53112efc46 100644 --- a/esphomeyaml/components/sensor/tsl2561.py +++ b/esphomeyaml/components/sensor/tsl2561.py @@ -1,21 +1,24 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_INTEGRATION_TIME, CONF_MAKE_ID, \ CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component DEPENDENCIES = ['i2c'] +TSL2561IntegrationTime = sensor.sensor_ns.enum('TSL2561IntegrationTime') INTEGRATION_TIMES = { - 14: sensor.sensor_ns.TSL2561_INTEGRATION_14MS, - 101: sensor.sensor_ns.TSL2561_INTEGRATION_101MS, - 402: sensor.sensor_ns.TSL2561_INTEGRATION_402MS, + 14: TSL2561IntegrationTime.TSL2561_INTEGRATION_14MS, + 101: TSL2561IntegrationTime.TSL2561_INTEGRATION_101MS, + 402: TSL2561IntegrationTime.TSL2561_INTEGRATION_402MS, } + +TSL2561Gain = sensor.sensor_ns.enum('TSL2561Gain') GAINS = { - '1X': sensor.sensor_ns.TSL2561_GAIN_1X, - '16X': sensor.sensor_ns.TSL2561_GAIN_16X, + '1X': TSL2561Gain.TSL2561_GAIN_1X, + '16X': TSL2561Gain.TSL2561_GAIN_16X, } CONF_IS_CS_PACKAGE = 'is_cs_package' @@ -28,16 +31,19 @@ def validate_integration_time(value): return value -MakeTSL2561Sensor = Application.MakeTSL2561Sensor +MakeTSL2561Sensor = Application.struct('MakeTSL2561Sensor') +TSL2561Sensor = sensor.sensor_ns.class_('TSL2561Sensor', sensor.PollingSensorComponent, + i2c.I2CDevice) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TSL2561Sensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTSL2561Sensor), vol.Optional(CONF_ADDRESS, default=0x39): cv.i2c_address, vol.Optional(CONF_INTEGRATION_TIME): validate_integration_time, vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*GAINS)), vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): @@ -45,13 +51,20 @@ def to_code(config): config.get(CONF_UPDATE_INTERVAL)) make_tsl = variable(config[CONF_MAKE_ID], rhs) tsl2561 = make_tsl.Ptsl2561 + if CONF_INTEGRATION_TIME in config: add(tsl2561.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]])) if CONF_GAIN in config: add(tsl2561.set_gain(GAINS[config[CONF_GAIN]])) if CONF_IS_CS_PACKAGE in config: add(tsl2561.set_is_cs_package(config[CONF_IS_CS_PACKAGE])) + sensor.setup_sensor(tsl2561, make_tsl.Pmqtt, config) + setup_component(tsl2561, config) BUILD_FLAGS = '-DUSE_TSL2561' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ultrasonic.py b/esphomeyaml/components/sensor/ultrasonic.py index 31ba1f4027..d12c4419fd 100644 --- a/esphomeyaml/components/sensor/ultrasonic.py +++ b/esphomeyaml/components/sensor/ultrasonic.py @@ -6,11 +6,14 @@ from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ECHO_PIN, CONF_MAKE_ID, CONF_NAME, CONF_TIMEOUT_METER, \ CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, \ - gpio_output_pin_expression, variable + gpio_output_pin_expression, variable, setup_component -MakeUltrasonicSensor = Application.MakeUltrasonicSensor +MakeUltrasonicSensor = Application.struct('MakeUltrasonicSensor') +UltrasonicSensorComponent = sensor.sensor_ns.class_('UltrasonicSensorComponent', + sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(UltrasonicSensorComponent), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUltrasonicSensor), vol.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, @@ -21,21 +24,26 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ def to_code(config): - trigger = None for trigger in gpio_output_pin_expression(config[CONF_TRIGGER_PIN]): yield - echo = None for echo in gpio_input_pin_expression(config[CONF_ECHO_PIN]): yield rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) ultrasonic = make.Pultrasonic + if CONF_TIMEOUT_TIME in config: add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME])) elif CONF_TIMEOUT_METER in config: add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER])) + sensor.setup_sensor(ultrasonic, make.Pmqtt, config) + setup_component(ultrasonic, config) BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/uptime.py b/esphomeyaml/components/sensor/uptime.py index 1abb5ecd91..60bf9a990b 100644 --- a/esphomeyaml/components/sensor/uptime.py +++ b/esphomeyaml/components/sensor/uptime.py @@ -3,20 +3,29 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.helpers import App, Application, variable, setup_component -MakeUptimeSensor = Application.MakeUptimeSensor +MakeUptimeSensor = Application.struct('MakeUptimeSensor') +UptimeSensor = sensor.sensor_ns.class_('UptimeSensor', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(UptimeSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUptimeSensor), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_uptime_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(make.Puptime, make.Pmqtt, config) + uptime = make.Puptime + + sensor.setup_sensor(uptime, make.Pmqtt, config) + setup_component(uptime, config) BUILD_FLAGS = '-DUSE_UPTIME_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/wifi_signal.py b/esphomeyaml/components/sensor/wifi_signal.py index 93c6172e08..564824b3aa 100644 --- a/esphomeyaml/components/sensor/wifi_signal.py +++ b/esphomeyaml/components/sensor/wifi_signal.py @@ -3,20 +3,29 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.helpers import App, Application, variable, setup_component -MakeWiFiSignalSensor = Application.MakeWiFiSignalSensor +MakeWiFiSignalSensor = Application.struct('MakeWiFiSignalSensor') +WiFiSignalSensor = sensor.sensor_ns.class_('WiFiSignalSensor', sensor.PollingSensorComponent) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(WiFiSignalSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeWiFiSignalSensor), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_wifi_signal_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(make.Pwifi, make.Pmqtt, config) + wifi = make.Pwifi + + sensor.setup_sensor(wifi, make.Pmqtt, config) + setup_component(wifi, config) BUILD_FLAGS = '-DUSE_WIFI_SIGNAL_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/xiaomi_miflora.py b/esphomeyaml/components/sensor/xiaomi_miflora.py index dd9bd0f68a..6d02fe7e84 100644 --- a/esphomeyaml/components/sensor/xiaomi_miflora.py +++ b/esphomeyaml/components/sensor/xiaomi_miflora.py @@ -1,35 +1,32 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import esp32_ble_tracker, sensor from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ make_address_array -from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ILLUMINANCE, \ - CONF_MAC_ADDRESS, CONF_MAKE_ID, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE -from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ID, CONF_ILLUMINANCE, \ + CONF_MAC_ADDRESS, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE +from esphomeyaml.helpers import Pvariable, get_variable DEPENDENCIES = ['esp32_ble_tracker'] -XiaomiDevice = esphomelib_ns.XiaomiDevice - PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ - cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiDevice), + cv.GenerateID(): cv.declare_variable_id(esp32_ble_tracker.XiaomiDevice), cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker), vol.Required(CONF_MAC_ADDRESS): cv.mac_address, - vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_MOISTURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_CONDUCTIVITY): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_TEMPERATURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_MOISTURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_ILLUMINANCE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_CONDUCTIVITY): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), }) def to_code(config): - hub = None for hub in get_variable(config[CONF_ESP32_BLE_ID]): yield rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS])) - dev = Pvariable(config[CONF_MAKE_ID], rhs) + dev = Pvariable(config[CONF_ID], rhs) if CONF_TEMPERATURE in config: conf = config[CONF_TEMPERATURE] sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf) @@ -45,3 +42,12 @@ def to_code(config): if CONF_BATTERY_LEVEL in config: conf = config[CONF_BATTERY_LEVEL] sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_TEMPERATURE, CONF_MOISTURE, CONF_ILLUMINANCE, CONF_CONDUCTIVITY, + CONF_BATTERY_LEVEL): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/xiaomi_mijia.py b/esphomeyaml/components/sensor/xiaomi_mijia.py index add1010d20..10a6573d6f 100644 --- a/esphomeyaml/components/sensor/xiaomi_mijia.py +++ b/esphomeyaml/components/sensor/xiaomi_mijia.py @@ -1,29 +1,26 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor +from esphomeyaml.components import esp32_ble_tracker, sensor from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ make_address_array +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_MAKE_ID, \ CONF_NAME, CONF_TEMPERATURE -from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable +from esphomeyaml.helpers import Pvariable, get_variable DEPENDENCIES = ['esp32_ble_tracker'] -XiaomiDevice = esphomelib_ns.XiaomiDevice - PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ - cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiDevice), + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(esp32_ble_tracker.XiaomiDevice), cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker), vol.Required(CONF_MAC_ADDRESS): cv.mac_address, - vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), - vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_TEMPERATURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_HUMIDITY): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), + vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA), }) def to_code(config): - hub = None for hub in get_variable(config[CONF_ESP32_BLE_ID]): yield rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS])) @@ -37,3 +34,11 @@ def to_code(config): if CONF_BATTERY_LEVEL in config: conf = config[CONF_BATTERY_LEVEL] sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_TEMPERATURE, CONF_HUMIDITY, CONF_BATTERY_LEVEL): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/spi.py b/esphomeyaml/components/spi.py index 116418e349..c9c98eae09 100644 --- a/esphomeyaml/components/spi.py +++ b/esphomeyaml/components/spi.py @@ -4,9 +4,10 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \ - gpio_output_pin_expression, add + gpio_output_pin_expression, add, setup_component, Component -SPIComponent = esphomelib_ns.SPIComponent +SPIComponent = esphomelib_ns.class_('SPIComponent', Component) +SPIDevice = esphomelib_ns.class_('SPIDevice') SPI_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(SPIComponent), @@ -20,7 +21,6 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA]) def to_code(config): for conf in config: - clk = None for clk in gpio_output_pin_expression(conf[CONF_CLK_PIN]): yield rhs = App.init_spi(clk) @@ -34,5 +34,7 @@ def to_code(config): yield add(spi.set_mosi(mosi)) + setup_component(spi, conf) + BUILD_FLAGS = '-DUSE_SPI' diff --git a/esphomeyaml/components/status_led.py b/esphomeyaml/components/status_led.py index bf24906bb7..e67621c0ee 100644 --- a/esphomeyaml/components/status_led.py +++ b/esphomeyaml/components/status_led.py @@ -2,22 +2,24 @@ import voluptuous as vol from esphomeyaml import config_validation as cv, pins from esphomeyaml.const import CONF_ID, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_output_pin_expression +from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_output_pin_expression, \ + setup_component, Component -StatusLEDComponent = esphomelib_ns.StatusLEDComponent +StatusLEDComponent = esphomelib_ns.class_('StatusLEDComponent', Component) CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent), vol.Optional(CONF_PIN): pins.gpio_output_pin_schema, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - pin = None for pin in gpio_output_pin_expression(config[CONF_PIN]): yield rhs = App.make_status_led(pin) - Pvariable(config[CONF_ID], rhs) + var = Pvariable(config[CONF_ID], rhs) + + setup_component(var, config) BUILD_FLAGS = '-DUSE_STATUS_LED' diff --git a/esphomeyaml/components/stepper/__init__.py b/esphomeyaml/components/stepper/__init__.py new file mode 100644 index 0000000000..ac2ce911ac --- /dev/null +++ b/esphomeyaml/components/stepper/__init__.py @@ -0,0 +1,125 @@ +import voluptuous as vol + +from esphomeyaml.automation import ACTION_REGISTRY +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ACCELERATION, CONF_DECELERATION, CONF_ID, CONF_MAX_SPEED, \ + CONF_POSITION, CONF_TARGET +from esphomeyaml.helpers import Pvariable, TemplateArguments, add, add_job, esphomelib_ns, \ + get_variable, int32, templatable, Action + +PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ + +}) + +# pylint: disable=invalid-name +stepper_ns = esphomelib_ns.namespace('stepper') +Stepper = stepper_ns.class_('Stepper') + +SetTargetAction = stepper_ns.class_('SetTargetAction', Action) +ReportPositionAction = stepper_ns.class_('ReportPositionAction', Action) + + +def validate_acceleration(value): + value = cv.string(value) + for suffix in ('steps/s^2', 'steps/s*s', 'steps/s/s', 'steps/ss', 'steps/(s*s)'): + if value.endswith(suffix): + value = value[:-len(suffix)] + + if value == 'inf': + return 1e6 + + try: + value = float(value) + except ValueError: + raise vol.Invalid("Expected acceleration as floating point number, got {}".format(value)) + + if value <= 0: + raise vol.Invalid("Acceleration must be larger than 0 steps/s^2!") + + return value + + +def validate_speed(value): + value = cv.string(value) + for suffix in ('steps/s', 'steps/s'): + if value.endswith(suffix): + value = value[:-len(suffix)] + + if value == 'inf': + return 1e6 + + try: + value = float(value) + except ValueError: + raise vol.Invalid("Expected speed as floating point number, got {}".format(value)) + + if value <= 0: + raise vol.Invalid("Speed must be larger than 0 steps/s!") + + return value + + +STEPPER_SCHEMA = vol.Schema({ + vol.Required(CONF_MAX_SPEED): validate_speed, + vol.Optional(CONF_ACCELERATION): validate_acceleration, + vol.Optional(CONF_DECELERATION): validate_acceleration, +}) + +STEPPER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(STEPPER_SCHEMA.schema) + + +def setup_stepper_core_(stepper_var, config): + if CONF_ACCELERATION in config: + add(stepper_var.set_acceleration(config[CONF_ACCELERATION])) + if CONF_DECELERATION in config: + add(stepper_var.set_deceleration(config[CONF_DECELERATION])) + if CONF_MAX_SPEED in config: + add(stepper_var.set_max_speed(config[CONF_MAX_SPEED])) + + +def setup_stepper(stepper_var, config): + add_job(setup_stepper_core_, stepper_var, config) + + +BUILD_FLAGS = '-DUSE_STEPPER' + +CONF_STEPPER_SET_TARGET = 'stepper.set_target' +STEPPER_SET_TARGET_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(Stepper), + vol.Required(CONF_TARGET): cv.templatable(cv.int_), +}) + + +@ACTION_REGISTRY.register(CONF_STEPPER_SET_TARGET, STEPPER_SET_TARGET_ACTION_SCHEMA) +def stepper_set_target_to_code(config, action_id, arg_type): + template_arg = TemplateArguments(arg_type) + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_set_target_action(template_arg) + type = SetTargetAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + for template_ in templatable(config[CONF_TARGET], arg_type, int32): + yield None + add(action.set_target(template_)) + yield action + + +CONF_STEPPER_REPORT_POSITION = 'stepper.report_position' +STEPPER_REPORT_POSITION_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(Stepper), + vol.Required(CONF_POSITION): cv.templatable(cv.int_), +}) + + +@ACTION_REGISTRY.register(CONF_STEPPER_REPORT_POSITION, STEPPER_REPORT_POSITION_ACTION_SCHEMA) +def stepper_report_position_to_code(config, action_id, arg_type): + template_arg = TemplateArguments(arg_type) + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_report_position_action(template_arg) + type = ReportPositionAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + for template_ in templatable(config[CONF_POSITION], arg_type, int32): + yield None + add(action.set_target(template_)) + yield action diff --git a/esphomeyaml/components/stepper/a4988.py b/esphomeyaml/components/stepper/a4988.py new file mode 100644 index 0000000000..281ede1110 --- /dev/null +++ b/esphomeyaml/components/stepper/a4988.py @@ -0,0 +1,37 @@ +import voluptuous as vol + +from esphomeyaml import pins +from esphomeyaml.components import stepper +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN +from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, setup_component, \ + Component + +A4988 = stepper.stepper_ns.class_('A4988', stepper.Stepper, Component) + +PLATFORM_SCHEMA = stepper.STEPPER_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_ID): cv.declare_variable_id(A4988), + vol.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema, + vol.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema, + vol.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema, +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + for step_pin in gpio_output_pin_expression(config[CONF_STEP_PIN]): + yield + for dir_pin in gpio_output_pin_expression(config[CONF_DIR_PIN]): + yield + rhs = App.make_a4988(step_pin, dir_pin) + a4988 = Pvariable(config[CONF_ID], rhs) + + if CONF_SLEEP_PIN in config: + for sleep_pin in gpio_output_pin_expression(config[CONF_SLEEP_PIN]): + yield + add(a4988.set_sleep_pin(sleep_pin)) + + stepper.setup_stepper(a4988, config) + setup_component(a4988, config) + + +BUILD_FLAGS = '-DUSE_A4988' diff --git a/esphomeyaml/components/switch/__init__.py b/esphomeyaml/components/switch/__init__.py index b5e18497a1..d1f36cdba8 100644 --- a/esphomeyaml/components/switch/__init__.py +++ b/esphomeyaml/components/switch/__init__.py @@ -1,24 +1,26 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL +from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL, \ + CONF_OPTIMISTIC from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ - TemplateArguments, get_variable + TemplateArguments, get_variable, Nameable, Action PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) switch_ns = esphomelib_ns.namespace('switch_') -Switch = switch_ns.Switch -MQTTSwitchComponent = switch_ns.MQTTSwitchComponent -ToggleAction = switch_ns.ToggleAction -TurnOffAction = switch_ns.TurnOffAction -TurnOnAction = switch_ns.TurnOnAction +Switch = switch_ns.class_('Switch', Nameable) +MQTTSwitchComponent = switch_ns.class_('MQTTSwitchComponent', mqtt.MQTTComponent) + +ToggleAction = switch_ns.class_('ToggleAction', Action) +TurnOffAction = switch_ns.class_('TurnOffAction', Action) +TurnOnAction = switch_ns.class_('TurnOnAction', Action) SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(Switch), cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent), vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_INVERTED): cv.boolean, @@ -56,7 +58,7 @@ BUILD_FLAGS = '-DUSE_SWITCH' CONF_SWITCH_TOGGLE = 'switch.toggle' SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Switch), }) @@ -72,7 +74,7 @@ def switch_toggle_to_code(config, action_id, arg_type): CONF_SWITCH_TURN_OFF = 'switch.turn_off' SWITCH_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Switch), }) @@ -88,7 +90,7 @@ def switch_turn_off_to_code(config, action_id, arg_type): CONF_SWITCH_TURN_ON = 'switch.turn_on' SWITCH_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ - vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_ID): cv.use_variable_id(Switch), }) @@ -100,3 +102,14 @@ def switch_turn_on_to_code(config, action_id, arg_type): rhs = var.make_turn_on_action(template_arg) type = TurnOnAction.template(arg_type) yield Pvariable(action_id, rhs, type=type) + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'switch', config, include_state=True, include_command=True) + if ret is None: + return None + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + if CONF_OPTIMISTIC in config: + ret['optimistic'] = config[CONF_OPTIMISTIC] + return ret diff --git a/esphomeyaml/components/switch/gpio.py b/esphomeyaml/components/switch/gpio.py index 3ee198ec05..039bfcb4cb 100644 --- a/esphomeyaml/components/switch/gpio.py +++ b/esphomeyaml/components/switch/gpio.py @@ -1,31 +1,35 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import switch -from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_POWER_ON_VALUE -from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable, add +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN +from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable, \ + setup_component, Component -MakeGPIOSwitch = Application.MakeGPIOSwitch +MakeGPIOSwitch = Application.struct('MakeGPIOSwitch') +GPIOSwitch = switch.switch_ns.class_('GPIOSwitch', switch.Switch, Component) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(GPIOSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOSwitch), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, - vol.Optional(CONF_POWER_ON_VALUE): cv.boolean, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - pin = None for pin in gpio_output_pin_expression(config[CONF_PIN]): yield rhs = App.make_gpio_switch(config[CONF_NAME], pin) - gpio = variable(config[CONF_MAKE_ID], rhs) + make = variable(config[CONF_MAKE_ID], rhs) + gpio = make.Pswitch_ - if CONF_POWER_ON_VALUE in config: - add(gpio.Pswitch_.set_power_on_value(config[CONF_POWER_ON_VALUE])) - - switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config) + switch.setup_switch(gpio, make.Pmqtt, config) + setup_component(gpio, config) BUILD_FLAGS = '-DUSE_GPIO_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/output.py b/esphomeyaml/components/switch/output.py index 818225f74f..4616c2127e 100644 --- a/esphomeyaml/components/switch/output.py +++ b/esphomeyaml/components/switch/output.py @@ -1,25 +1,33 @@ import voluptuous as vol +from esphomeyaml.components import output, switch import esphomeyaml.config_validation as cv -from esphomeyaml.components import switch from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT -from esphomeyaml.helpers import App, Application, get_variable, variable +from esphomeyaml.helpers import App, Application, Component, get_variable, setup_component, variable -MakeOutputSwitch = Application.MakeOutputSwitch +MakeOutputSwitch = Application.struct('MakeOutputSwitch') +OutputSwitch = switch.switch_ns.class_('OutputSwitch', switch.Switch, Component) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(OutputSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeOutputSwitch), - vol.Required(CONF_OUTPUT): cv.use_variable_id(None), -})) + vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): - output = None - for output in get_variable(config[CONF_OUTPUT]): + for output_ in get_variable(config[CONF_OUTPUT]): yield - rhs = App.make_output_switch(config[CONF_NAME], output) - gpio = variable(config[CONF_MAKE_ID], rhs) - switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config) + rhs = App.make_output_switch(config[CONF_NAME], output_) + make = variable(config[CONF_MAKE_ID], rhs) + switch_ = make.Pswitch_ + + switch.setup_switch(switch_, make.Pmqtt, config) + setup_component(switch, config) BUILD_FLAGS = '-DUSE_OUTPUT_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/remote_transmitter.py b/esphomeyaml/components/switch/remote_transmitter.py index 058e84d44e..effa4121d5 100644 --- a/esphomeyaml/components/switch/remote_transmitter.py +++ b/esphomeyaml/components/switch/remote_transmitter.py @@ -1,18 +1,18 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import switch from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \ RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \ RC_SWITCH_TYPE_D_SCHEMA, RemoteTransmitterComponent, binary_code, build_rc_switch_protocol, \ remote_ns +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL, CONF_CODE, \ CONF_COMMAND, CONF_DATA, CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_INVERTED, CONF_LG, \ CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, \ CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, \ CONF_REPEAT, CONF_SAMSUNG, CONF_SONY, CONF_STATE, CONF_TIMES, \ CONF_WAIT_TIME -from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, get_variable +from esphomeyaml.helpers import ArrayInitializer, Pvariable, add, get_variable DEPENDENCIES = ['remote_transmitter'] @@ -23,22 +23,23 @@ REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SAMSUNG, CONF_SONY, CONF_PANASONIC, CONF_ CONF_REMOTE_TRANSMITTER_ID = 'remote_transmitter_id' CONF_TRANSMITTER_ID = 'transmitter_id' -RemoteTransmitter = remote_ns.RemoteTransmitter -LGTransmitter = remote_ns.LGTransmitter -NECTransmitter = remote_ns.NECTransmitter -PanasonicTransmitter = remote_ns.PanasonicTransmitter -RawTransmitter = remote_ns.RawTransmitter -SamsungTransmitter = remote_ns.SamsungTransmitter -SonyTransmitter = remote_ns.SonyTransmitter -RCSwitchRawTransmitter = remote_ns.RCSwitchRawTransmitter -RCSwitchTypeATransmitter = remote_ns.RCSwitchTypeATransmitter -RCSwitchTypeBTransmitter = remote_ns.RCSwitchTypeBTransmitter -RCSwitchTypeCTransmitter = remote_ns.RCSwitchTypeCTransmitter -RCSwitchTypeDTransmitter = remote_ns.RCSwitchTypeDTransmitter +RemoteTransmitter = remote_ns.class_('RemoteTransmitter', switch.Switch) +LGTransmitter = remote_ns.class_('LGTransmitter', RemoteTransmitter) +NECTransmitter = remote_ns.class_('NECTransmitter', RemoteTransmitter) +PanasonicTransmitter = remote_ns.class_('PanasonicTransmitter', RemoteTransmitter) +RawTransmitter = remote_ns.class_('RawTransmitter', RemoteTransmitter) +SamsungTransmitter = remote_ns.class_('SamsungTransmitter', RemoteTransmitter) +SonyTransmitter = remote_ns.class_('SonyTransmitter', RemoteTransmitter) +RCSwitchRawTransmitter = remote_ns.class_('RCSwitchRawTransmitter', RemoteTransmitter) +RCSwitchTypeATransmitter = remote_ns.class_('RCSwitchTypeATransmitter', RCSwitchRawTransmitter) +RCSwitchTypeBTransmitter = remote_ns.class_('RCSwitchTypeBTransmitter', RCSwitchRawTransmitter) +RCSwitchTypeCTransmitter = remote_ns.class_('RCSwitchTypeCTransmitter', RCSwitchRawTransmitter) +RCSwitchTypeDTransmitter = remote_ns.class_('RCSwitchTypeDTransmitter', RCSwitchRawTransmitter) validate_raw_data = [vol.Any(vol.Coerce(int), cv.time_period_microseconds)] PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(RemoteTransmitter), vol.Optional(CONF_LG): vol.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)), @@ -127,10 +128,9 @@ def transmitter_base(full_config): def to_code(config): - remote = None for remote in get_variable(config[CONF_REMOTE_TRANSMITTER_ID]): yield - rhs = App.register_component(transmitter_base(config)) + rhs = transmitter_base(config) transmitter = Pvariable(config[CONF_TRANSMITTER_ID], rhs) if CONF_REPEAT in config: @@ -146,3 +146,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/restart.py b/esphomeyaml/components/switch/restart.py index 5edd01dab9..c81f486dd2 100644 --- a/esphomeyaml/components/switch/restart.py +++ b/esphomeyaml/components/switch/restart.py @@ -5,9 +5,11 @@ from esphomeyaml.components import switch from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME from esphomeyaml.helpers import App, Application, variable -MakeRestartSwitch = Application.MakeRestartSwitch +MakeRestartSwitch = Application.struct('MakeRestartSwitch') +RestartSwitch = switch.switch_ns.class_('RestartSwitch', switch.Switch) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(RestartSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRestartSwitch), vol.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"), })) @@ -20,3 +22,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_RESTART_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/shutdown.py b/esphomeyaml/components/switch/shutdown.py index 641b84108c..cca5f7184c 100644 --- a/esphomeyaml/components/switch/shutdown.py +++ b/esphomeyaml/components/switch/shutdown.py @@ -5,9 +5,11 @@ from esphomeyaml.components import switch from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME from esphomeyaml.helpers import App, Application, variable -MakeShutdownSwitch = Application.MakeShutdownSwitch +MakeShutdownSwitch = Application.struct('MakeShutdownSwitch') +ShutdownSwitch = switch.switch_ns.class_('ShutdownSwitch', switch.Switch) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(ShutdownSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeShutdownSwitch), vol.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"), })) @@ -20,3 +22,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index a8f110bb6f..464d90feb7 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -4,41 +4,53 @@ import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.components import switch from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_TURN_OFF_ACTION, \ - CONF_TURN_ON_ACTION, CONF_OPTIMISTIC + CONF_TURN_ON_ACTION, CONF_OPTIMISTIC, CONF_RESTORE_STATE from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add, bool_, \ - optional + optional, setup_component, Component -MakeTemplateSwitch = Application.MakeTemplateSwitch +MakeTemplateSwitch = Application.struct('MakeTemplateSwitch') +TemplateSwitch = switch.switch_ns.class_('TemplateSwitch', switch.Switch, Component) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TemplateSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSwitch), vol.Optional(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_OPTIMISTIC): cv.boolean, vol.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True), vol.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True), -}), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) + vol.Optional(CONF_RESTORE_STATE): cv.boolean, +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) def to_code(config): rhs = App.make_template_switch(config[CONF_NAME]) make = variable(config[CONF_MAKE_ID], rhs) + template = make.Ptemplate_ - switch.setup_switch(make.Ptemplate_, make.Pmqtt, config) + switch.setup_switch(template, make.Pmqtt, config) if CONF_LAMBDA in config: - template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(bool_)): yield - add(make.Ptemplate_.set_state_lambda(template_)) + add(template.set_state_lambda(template_)) if CONF_TURN_OFF_ACTION in config: - automation.build_automation(make.Ptemplate_.get_turn_off_trigger(), NoArg, + automation.build_automation(template.get_turn_off_trigger(), NoArg, config[CONF_TURN_OFF_ACTION]) if CONF_TURN_ON_ACTION in config: - automation.build_automation(make.Ptemplate_.get_turn_on_trigger(), NoArg, + automation.build_automation(template.get_turn_on_trigger(), NoArg, config[CONF_TURN_ON_ACTION]) if CONF_OPTIMISTIC in config: - add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC])) + add(template.set_optimistic(config[CONF_OPTIMISTIC])) + + if CONF_RESTORE_STATE in config: + add(template.set_restore_state(config[CONF_RESTORE_STATE])) + + setup_component(template, config) BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/uart.py b/esphomeyaml/components/switch/uart.py index 622b1df0b8..3cbe26dc36 100644 --- a/esphomeyaml/components/switch/uart.py +++ b/esphomeyaml/components/switch/uart.py @@ -1,7 +1,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import switch +from esphomeyaml.components import switch, uart from esphomeyaml.components.uart import UARTComponent from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID from esphomeyaml.core import HexInt @@ -9,7 +9,8 @@ from esphomeyaml.helpers import App, Application, ArrayInitializer, get_variable DEPENDENCIES = ['uart'] -MakeUARTSwitch = Application.MakeUARTSwitch +MakeUARTSwitch = Application.struct('MakeUARTSwitch') +UARTSwitch = switch.switch_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice) def validate_data(value): @@ -23,6 +24,7 @@ def validate_data(value): PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(UARTSwitch), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUARTSwitch), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), vol.Required(CONF_DATA): validate_data, @@ -31,15 +33,18 @@ PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ def to_code(config): - uart = None - for uart in get_variable(config[CONF_UART_ID]): + for uart_ in get_variable(config[CONF_UART_ID]): yield data = config[CONF_DATA] if isinstance(data, str): data = [HexInt(ord(x)) for x in data] - rhs = App.make_uart_switch(uart, config[CONF_NAME], ArrayInitializer(*data, multiline=False)) + rhs = App.make_uart_switch(uart_, config[CONF_NAME], ArrayInitializer(*data, multiline=False)) restart = variable(config[CONF_MAKE_ID], rhs) switch.setup_switch(restart.Puart, restart.Pmqtt, config) BUILD_FLAGS = '-DUSE_UART_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/__init__.py b/esphomeyaml/components/text_sensor/__init__.py index e5fd17760c..0348b07fed 100644 --- a/esphomeyaml/components/text_sensor/__init__.py +++ b/esphomeyaml/components/text_sensor/__init__.py @@ -1,11 +1,12 @@ import voluptuous as vol from esphomeyaml import automation +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_ON_VALUE, \ CONF_TRIGGER_ID from esphomeyaml.helpers import App, Pvariable, add, add_job, esphomelib_ns, setup_mqtt_component, \ - std_string + std_string, Nameable, Trigger PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -13,14 +14,14 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ # pylint: disable=invalid-name text_sensor_ns = esphomelib_ns.namespace('text_sensor') -TextSensor = text_sensor_ns.TextSensor -MQTTTextSensor = text_sensor_ns.MQTTTextSensor +TextSensor = text_sensor_ns.class_('TextSensor', Nameable) +MQTTTextSensor = text_sensor_ns.class_('MQTTTextSensor', mqtt.MQTTComponent) -TextSensorStateTrigger = text_sensor_ns.TextSensorStateTrigger +TextSensorStateTrigger = text_sensor_ns.class_('TextSensorStateTrigger', + Trigger.template(std_string)) TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTTextSensor), - cv.GenerateID(): cv.declare_variable_id(TextSensor), vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ON_VALUE): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(TextSensorStateTrigger), @@ -58,3 +59,12 @@ def register_text_sensor(var, config): BUILD_FLAGS = '-DUSE_TEXT_SENSOR' + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False) + if ret is None: + return None + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + return ret diff --git a/esphomeyaml/components/text_sensor/mqtt_subscribe.py b/esphomeyaml/components/text_sensor/mqtt_subscribe.py index 18082d1f6e..c4dafadee7 100644 --- a/esphomeyaml/components/text_sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/text_sensor/mqtt_subscribe.py @@ -3,25 +3,36 @@ import voluptuous as vol from esphomeyaml.components import text_sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_QOS, CONF_TOPIC -from esphomeyaml.helpers import App, Application, add, variable +from esphomeyaml.helpers import App, Application, add, variable, setup_component, Component DEPENDENCIES = ['mqtt'] -MakeMQTTSubscribeTextSensor = Application.MakeMQTTSubscribeTextSensor +MakeMQTTSubscribeTextSensor = Application.struct('MakeMQTTSubscribeTextSensor') +MQTTSubscribeTextSensor = text_sensor.text_sensor_ns.class_('MQTTSubscribeTextSensor', + text_sensor.TextSensor, Component) PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeTextSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeTextSensor), vol.Required(CONF_TOPIC): cv.subscribe_topic, vol.Optional(CONF_QOS): cv.mqtt_qos, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_mqtt_subscribe_text_sensor(config[CONF_NAME], config[CONF_TOPIC]) make = variable(config[CONF_MAKE_ID], rhs) + sensor_ = make.Psensor + if CONF_QOS in config: - add(make.Psensor.set_qos(config[CONF_QOS])) - text_sensor.setup_text_sensor(make.Psensor, make.Pmqtt, config) + add(sensor_.set_qos(config[CONF_QOS])) + + text_sensor.setup_text_sensor(sensor_, make.Pmqtt, config) + setup_component(sensor_, config) BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/template.py b/esphomeyaml/components/text_sensor/template.py index 8e440b3fa5..264841ea4b 100644 --- a/esphomeyaml/components/text_sensor/template.py +++ b/esphomeyaml/components/text_sensor/template.py @@ -4,27 +4,35 @@ from esphomeyaml.components import text_sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL from esphomeyaml.helpers import App, Application, add, optional, process_lambda, std_string, \ - variable + variable, setup_component, PollingComponent -MakeTemplateTextSensor = Application.MakeTemplateTextSensor +MakeTemplateTextSensor = Application.struct('MakeTemplateTextSensor') +TemplateTextSensor = text_sensor.text_sensor_ns.class_('TemplateTextSensor', + text_sensor.TextSensor, PollingComponent) PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TemplateTextSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateTextSensor), vol.Required(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_template_text_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) - text_sensor.setup_text_sensor(make.Ptemplate_, make.Pmqtt, config) + template = make.Ptemplate_ + text_sensor.setup_text_sensor(template, make.Pmqtt, config) + setup_component(template, config) - template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(std_string)): yield - add(make.Ptemplate_.set_template(template_)) + add(template.set_template(template_)) BUILD_FLAGS = '-DUSE_TEMPLATE_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/version.py b/esphomeyaml/components/text_sensor/version.py index 593211c715..1bfbac7fe6 100644 --- a/esphomeyaml/components/text_sensor/version.py +++ b/esphomeyaml/components/text_sensor/version.py @@ -1,19 +1,27 @@ from esphomeyaml.components import text_sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.helpers import App, Application, variable, setup_component, Component -MakeVersionTextSensor = Application.MakeVersionTextSensor +MakeVersionTextSensor = Application.struct('MakeVersionTextSensor') +VersionTextSensor = text_sensor.text_sensor_ns.class_('VersionTextSensor', + text_sensor.TextSensor, Component) PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(VersionTextSensor), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeVersionTextSensor), -})) +}).extend(cv.COMPONENT_SCHEMA.schema)) def to_code(config): rhs = App.make_version_text_sensor(config[CONF_NAME]) make = variable(config[CONF_MAKE_ID], rhs) text_sensor.setup_text_sensor(make.Psensor, make.Pmqtt, config) + setup_component(make.Psensor, config) BUILD_FLAGS = '-DUSE_VERSION_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/time/__init__.py b/esphomeyaml/components/time/__init__.py index 375c4f6eff..30a4f5e7d7 100644 --- a/esphomeyaml/components/time/__init__.py +++ b/esphomeyaml/components/time/__init__.py @@ -8,7 +8,8 @@ import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \ CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID -from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns +from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns, \ + ArrayInitializer, Component, Trigger _LOGGER = logging.getLogger(__name__) @@ -17,7 +18,9 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) time_ns = esphomelib_ns.namespace('time') -CronTrigger = time_ns.CronTrigger +RealTimeClockComponent = time_ns.class_('RealTimeClockComponent', Component) +CronTrigger = time_ns.class_('CronTrigger', Trigger.template(NoArg), Component) +EsphomelibTime = time_ns.struct('EsphomelibTime') def _tz_timedelta(td): @@ -47,18 +50,8 @@ def _tz_dst_str(dt): _tz_timedelta(td)) -def detect_tz(): - try: - import tzlocal - import pytz - except ImportError: - raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically " - "detect the timezone please install tzlocal (pip2 install tzlocal)") - try: - tz = tzlocal.get_localzone() - except pytz.exceptions.UnknownTimeZoneError: - _LOGGER.warning("Could not auto-detect timezone. Using UTC...") - return 'UTC' +def convert_tz(pytz_obj): + tz = pytz_obj def _dst(dt, is_dst): try: @@ -107,18 +100,34 @@ def detect_tz(): tzbase = '{}{}'.format(norm_tzname, _tz_timedelta(-1 * norm_utcoffset)) if dst_begins is None: # No DST in this timezone - _LOGGER.info("Auto-detected timezone '%s' with UTC offset %s", + _LOGGER.info("Detected timezone '%s' with UTC offset %s", norm_tzname, _tz_timedelta(norm_utcoffset)) return tzbase tzext = '{}{},{},{}'.format(dst_tzname, _tz_timedelta(-1 * dst_utcoffset), _tz_dst_str(dst_begins), _tz_dst_str(dst_ends)) - _LOGGER.info("Auto-detected timezone '%s' with UTC offset %s and daylight savings time from " + _LOGGER.info("Detected timezone '%s' with UTC offset %s and daylight savings time from " "%s to %s", norm_tzname, _tz_timedelta(norm_utcoffset), dst_begins.strftime("%x %X"), dst_ends.strftime("%x %X")) return tzbase + tzext +def detect_tz(): + try: + import tzlocal + import pytz + except ImportError: + raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically " + "detect the timezone please install tzlocal (pip2 install tzlocal)") + try: + tz = tzlocal.get_localzone() + except pytz.exceptions.UnknownTimeZoneError: + _LOGGER.warning("Could not auto-detect timezone. Using UTC...") + return 'UTC' + + return convert_tz(tz) + + def _parse_cron_int(value, special_mapping, message): special_mapping = special_mapping or {} if isinstance(value, (str, unicode)) and value in special_mapping: @@ -233,8 +242,19 @@ def validate_cron_keys(value): return cv.has_at_least_one_key(*CRON_KEYS)(value) +def validate_tz(value): + value = cv.string_strict(value) + + try: + import pytz + + return convert_tz(pytz.timezone(value)) + except Exception: # pylint: disable=broad-except + return value + + TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_TIMEZONE, default=detect_tz): cv.string, + vol.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz, vol.Optional(CONF_ON_TIME): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(CronTrigger), vol.Optional(CONF_SECONDS): validate_cron_seconds, @@ -254,18 +274,25 @@ def setup_time_core_(time_var, config): for conf in config.get(CONF_ON_TIME, []): rhs = App.register_component(time_var.Pmake_cron_trigger()) trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) - for second in conf.get(CONF_SECONDS, [x for x in range(0, 61)]): - add(trigger.add_second(second)) - for minute in conf.get(CONF_MINUTES, [x for x in range(0, 60)]): - add(trigger.add_minute(minute)) - for hour in conf.get(CONF_HOURS, [x for x in range(0, 24)]): - add(trigger.add_hour(hour)) - for day_of_month in conf.get(CONF_DAYS_OF_MONTH, [x for x in range(1, 32)]): - add(trigger.add_day_of_month(day_of_month)) - for month in conf.get(CONF_MONTHS, [x for x in range(1, 13)]): - add(trigger.add_month(month)) - for day_of_week in conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)]): - add(trigger.add_day_of_week(day_of_week)) + + seconds = conf.get(CONF_SECONDS, [x for x in range(0, 61)]) + add(trigger.add_seconds(ArrayInitializer(*seconds, multiline=False))) + + minutes = conf.get(CONF_MINUTES, [x for x in range(0, 60)]) + add(trigger.add_minutes(ArrayInitializer(*minutes, multiline=False))) + + hours = conf.get(CONF_HOURS, [x for x in range(0, 24)]) + add(trigger.add_hours(ArrayInitializer(*hours, multiline=False))) + + days_of_month = conf.get(CONF_DAYS_OF_MONTH, [x for x in range(1, 32)]) + add(trigger.add_days_of_month(ArrayInitializer(*days_of_month, multiline=False))) + + months = conf.get(CONF_MONTHS, [x for x in range(1, 13)]) + add(trigger.add_months(ArrayInitializer(*months, multiline=False))) + + days_of_week = conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)]) + add(trigger.add_days_of_week(ArrayInitializer(*days_of_week, multiline=False))) + automation.build_automation(trigger, NoArg, conf) diff --git a/esphomeyaml/components/time/sntp.py b/esphomeyaml/components/time/sntp.py index 19ba250b10..0422c8b6fa 100644 --- a/esphomeyaml/components/time/sntp.py +++ b/esphomeyaml/components/time/sntp.py @@ -3,22 +3,25 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import time as time_ from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SERVERS -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.helpers import App, Pvariable, add, setup_component -SNTPComponent = time_.time_ns.SNTPComponent +SNTPComponent = time_.time_ns.class_('SNTPComponent', time_.RealTimeClockComponent) PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(SNTPComponent), vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string], vol.Length(max=3)), vol.Optional(CONF_LAMBDA): cv.lambda_, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - rhs = App.make_sntp_component(*config.get(CONF_SERVERS, [])) + rhs = App.make_sntp_component() sntp = Pvariable(config[CONF_ID], rhs) + if CONF_SERVERS in config: + add(sntp.set_servers(*config[CONF_SERVERS])) time_.setup_time(sntp, config) + setup_component(sntp, config) BUILD_FLAGS = '-DUSE_SNTP_COMPONENT' diff --git a/esphomeyaml/components/uart.py b/esphomeyaml/components/uart.py index 549fcb59d5..8f5b9463fb 100644 --- a/esphomeyaml/components/uart.py +++ b/esphomeyaml/components/uart.py @@ -3,18 +3,19 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN -from esphomeyaml.helpers import App, Pvariable, esphomelib_ns +from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, setup_component, Component -UARTComponent = esphomelib_ns.UARTComponent +UARTComponent = esphomelib_ns.class_('UARTComponent', Component) +UARTDevice = esphomelib_ns.class_('UARTDevice') -SPI_SCHEMA = vol.All(vol.Schema({ +UART_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(UARTComponent), vol.Optional(CONF_TX_PIN): pins.output_pin, vol.Optional(CONF_RX_PIN): pins.input_pin, vol.Required(CONF_BAUD_RATE): cv.positive_int, -}), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) +}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) -CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA]) +CONFIG_SCHEMA = vol.All(cv.ensure_list, [UART_SCHEMA]) def to_code(config): @@ -22,7 +23,9 @@ def to_code(config): tx = conf.get(CONF_TX_PIN, -1) rx = conf.get(CONF_RX_PIN, -1) rhs = App.init_uart(tx, rx, conf[CONF_BAUD_RATE]) - Pvariable(conf[CONF_ID], rhs) + var = Pvariable(conf[CONF_ID], rhs) + + setup_component(var, conf) BUILD_FLAGS = '-DUSE_UART' diff --git a/esphomeyaml/components/web_server.py b/esphomeyaml/components/web_server.py index ef4be334db..8a3a06436d 100644 --- a/esphomeyaml/components/web_server.py +++ b/esphomeyaml/components/web_server.py @@ -1,22 +1,19 @@ -import logging - import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import core -from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID, ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CSS_URL, CONF_ID, CONF_JS_URL, CONF_PORT, ESP_PLATFORM_ESP32 +from esphomeyaml.helpers import App, Component, Pvariable, StoringController, add, esphomelib_ns, \ + setup_component -_LOGGER = logging.getLogger(__name__) - -WebServer = esphomelib_ns.WebServer +WebServer = esphomelib_ns.class_('WebServer', Component, StoringController) CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(WebServer), vol.Optional(CONF_PORT): cv.port, vol.Optional(CONF_CSS_URL): cv.string, vol.Optional(CONF_JS_URL): cv.string, -}) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): @@ -27,6 +24,8 @@ def to_code(config): if CONF_JS_URL in config: add(web_server.set_js_url(config[CONF_JS_URL])) + setup_component(web_server, config) + BUILD_FLAGS = '-DUSE_WEB_SERVER' diff --git a/esphomeyaml/components/wifi.py b/esphomeyaml/components/wifi.py index 125ccb6711..a627f9bb73 100644 --- a/esphomeyaml/components/wifi.py +++ b/esphomeyaml/components/wifi.py @@ -5,7 +5,8 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \ CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_POWER_SAVE_MODE,\ CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266 -from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns +from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns, \ + Component def validate_password(value): @@ -64,16 +65,16 @@ def validate(config): return config -# pylint: disable=invalid-name -IPAddress = global_ns.IPAddress -ManualIP = esphomelib_ns.ManualIP -WiFiComponent = esphomelib_ns.WiFiComponent -WiFiAp = esphomelib_ns.WiFiAp +IPAddress = global_ns.class_('IPAddress') +ManualIP = esphomelib_ns.struct('ManualIP') +WiFiComponent = esphomelib_ns.class_('WiFiComponent', Component) +WiFiAp = esphomelib_ns.struct('WiFiAp') +WiFiPowerSaveMode = esphomelib_ns.enum('WiFiPowerSaveMode') WIFI_POWER_SAVE_MODES = { - 'NONE': esphomelib_ns.WIFI_POWER_SAVE_NONE, - 'LIGHT': esphomelib_ns.WIFI_POWER_SAVE_LIGHT, - 'HIGH': esphomelib_ns.WIFI_POWER_SAVE_HIGH, + 'NONE': WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE, + 'LIGHT': WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, + 'HIGH': WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, } CONFIG_SCHEMA = vol.All(vol.Schema({ diff --git a/esphomeyaml/config.json b/esphomeyaml/config.json index 455d130d90..bfa362fb5e 100644 --- a/esphomeyaml/config.json +++ b/esphomeyaml/config.json @@ -1,6 +1,6 @@ { "name": "esphomeyaml", - "version": "1.8.2", + "version": "1.9.0b1", "slug": "esphomeyaml", "description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.", "url": "https://esphomelib.com/esphomeyaml/index.html", @@ -15,6 +15,11 @@ "map": [ "config:rw" ], + "arch": [ + "amd64", + "armhf", + "i386" + ], "environment": { "ESPHOMEYAML_OTA_HOST_PORT": "6053" }, diff --git a/esphomeyaml/config.py b/esphomeyaml/config.py index 814df4905b..9aaefbe860 100644 --- a/esphomeyaml/config.py +++ b/esphomeyaml/config.py @@ -10,7 +10,7 @@ from voluptuous.humanize import humanize_error from esphomeyaml import core, yaml_util, core_config from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import color +from esphomeyaml.helpers import color, MockObjClass from esphomeyaml.util import safe_print _LOGGER = logging.getLogger(__name__) @@ -106,11 +106,29 @@ def do_id_pass(result): # Check searched IDs for id, prefix, config in searching_ids: - if id.id is not None and not any(v[0].id == id.id for v in declare_ids): - result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config) + if id.id is not None: + # manually declared + match = next((v[0] for v in declare_ids if v[0].id == id.id), None) + if match is None: + # No declared ID with this name + result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config) + continue + if not isinstance(match.type, MockObjClass) or not isinstance(id.type, MockObjClass): + continue + if not match.type.inherits_from(id.type): + result.add_error("ID '{}' of type {} doesn't inherit from {}. Please double check " + "your ID is pointing to the correct value" + "".format(id.id, match.type, id.type)) + if id.id is None and id.type is not None: - id.id = next((v[0].id for v in declare_ids if v[0].type == id.type), None) - if id.id is None: + for v in declare_ids: + if v[0] is None or not isinstance(v[0].type, MockObjClass): + continue + inherits = v[0].type.inherits_from(id.type) + if inherits: + id.id = v[0].id + break + else: result.add_error("Couldn't resolve ID for type {}".format(id.type), '.'.join(prefix), config) diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index 09a30dab6b..dac218c76b 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -13,7 +13,7 @@ from esphomeyaml import core, helpers from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \ CONF_NAME, CONF_PAYLOAD_AVAILABLE, \ CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \ - ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_INTERNAL + ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_INTERNAL, CONF_SETUP_PRIORITY from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds @@ -296,7 +296,8 @@ def time_period_str_colon(value): def time_period_str_unit(value): """Validate and transform time period with time unit and integer value.""" if isinstance(value, int): - value = str(value) + raise vol.Invalid("Don't know what '{}' means as it has no time *unit*! Did you mean " + "'{}s'?".format(value, value)) elif not isinstance(value, (str, unicode)): raise vol.Invalid("Expected string for time period with unit.") @@ -555,8 +556,14 @@ i2c_address = hex_uint8_t def percentage(value): - if isinstance(value, (str, unicode)) and value.endswith('%'): + has_percent_sign = isinstance(value, (str, unicode)) and value.endswith('%') + if has_percent_sign: value = float(value[:-1].rstrip()) / 100.0 + if value > 1: + msg = "Percentage must not be higher than 100%." + if not has_percent_sign: + msg += " Please put a percent sign after the number!" + raise vol.Invalid(msg) return zero_to_one_float(value) @@ -634,9 +641,6 @@ def file_(value): return value -REGISTERED_IDS = set() - - class GenerateID(vol.Optional): def __init__(self, key=CONF_ID): super(GenerateID, self).__init__(key, default=lambda: None) @@ -681,3 +685,7 @@ MQTT_COMPONENT_SCHEMA = vol.Schema({ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ vol.Optional(CONF_COMMAND_TOPIC): subscribe_topic, }) + +COMPONENT_SCHEMA = vol.Schema({ + vol.Optional(CONF_SETUP_PRIORITY): vol.Coerce(float) +}) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index b7b7c9701a..25d198dc0a 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -1,11 +1,11 @@ """Constants used by esphomeyaml.""" MAJOR_VERSION = 1 -MINOR_VERSION = 8 -PATCH_VERSION = '2' +MINOR_VERSION = 9 +PATCH_VERSION = '0b6' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) -ESPHOMELIB_VERSION = '1.8.2' +ESPHOMELIB_VERSION = '1.9.0b6' ESP_PLATFORM_ESP32 = 'ESP32' ESP_PLATFORM_ESP8266 = 'ESP8266' @@ -202,6 +202,7 @@ CONF_RECEIVE_TIMEOUT = 'receive_timeout' CONF_SCAN_INTERVAL = 'scan_interval' CONF_MAC_ADDRESS = 'mac_address' CONF_SETUP_MODE = 'setup_mode' +CONF_SETUP_PRIORITY = 'setup_priority' CONF_IIR_FILTER = 'iir_filter' CONF_MEASUREMENT_DURATION = 'measurement_duration' CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference' @@ -222,6 +223,7 @@ CONF_ON_PRESS = 'on_press' CONF_ON_RELEASE = 'on_release' CONF_ON_CLICK = 'on_click' CONF_ON_DOUBLE_CLICK = 'on_double_click' +CONF_ON_MULTI_CLICK = 'on_multi_click' CONF_MIN_LENGTH = 'min_length' CONF_MAX_LENGTH = 'max_length' CONF_ON_VALUE = 'on_value' @@ -350,6 +352,26 @@ CONF_ARGS = 'args' CONF_FORMAT = 'format' CONF_COLOR_CORRECT = 'color_correct' CONF_ON_JSON_MESSAGE = 'on_json_message' +CONF_ACCELERATION = 'acceleration' +CONF_DECELERATION = 'deceleration' +CONF_MAX_SPEED = 'max_speed' +CONF_TARGET = 'target' +CONF_POSITION = 'position' +CONF_STEP_PIN = 'step_pin' +CONF_DIR_PIN = 'dir_pin' +CONF_SLEEP_PIN = 'sleep_pin' +CONF_SEND_FIRST_AT = 'send_first_at' +CONF_TIME_ID = 'time_id' +CONF_RESTORE_STATE = 'restore_state' +CONF_TIMING = 'timing' +CONF_INVALID_COOLDOWN = 'invalid_cooldown' +CONF_MY9231_ID = 'my9231_id' +CONF_NUM_CHANNELS = 'num_channels' +CONF_UPDATE_ON_BOOT = 'update_on_boot' +CONF_INITIAL_VALUE = 'initial_value' +CONF_RESTORE_VALUE = 'restore_value' +CONF_PINS = 'pins' + ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_' ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage' diff --git a/esphomeyaml/core_config.py b/esphomeyaml/core_config.py index b236d7cb2b..16405c5fc1 100644 --- a/esphomeyaml/core_config.py +++ b/esphomeyaml/core_config.py @@ -16,6 +16,7 @@ from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266 from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, NoArg, Pvariable, RawExpression, add, const_char_p, \ esphomelib_ns, relative_path +from esphomeyaml.util import safe_print _LOGGER = logging.getLogger(__name__) @@ -46,6 +47,8 @@ def validate_board(value): def validate_simple_esphomelib_version(value): value = cv.string_strict(value) if value.upper() == 'LATEST': + if ESPHOMELIB_VERSION == 'dev': + return validate_simple_esphomelib_version('dev') return { CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_TAG: 'v' + ESPHOMELIB_VERSION, @@ -53,7 +56,7 @@ def validate_simple_esphomelib_version(value): elif value.upper() == 'DEV': return { CONF_REPOSITORY: LIBRARY_URI_REPO, - CONF_BRANCH: 'master' + CONF_BRANCH: 'dev' } elif VERSION_REGEX.match(value) is not None: return { @@ -230,10 +233,16 @@ def update_esphomelib_repo(config): # local changes, cannot update _LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.") return - rc, _, _ = run_command('git', '-C', esphomelib_path, 'pull') + _LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path) + rc, stdout, _ = run_command('git', '-c', 'color.ui=always', '-C', esphomelib_path, + 'pull', '--stat') if rc != 0: _LOGGER.warn("Couldn't auto-update local git copy of esphomelib.") return + stdout = stdout.strip() + if 'Already up to date' in stdout: + return + safe_print(stdout) def to_code(config): diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index f9739d24b9..d12dc0fec8 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -13,7 +13,8 @@ from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_BUILD_PATH from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml import const, core, __main__ from esphomeyaml.__main__ import get_serial_ports -from esphomeyaml.helpers import quote, relative_path +from esphomeyaml.helpers import relative_path +from esphomeyaml.util import shlex_quote try: import tornado @@ -51,7 +52,7 @@ class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler): if self.proc is not None: return command = self.build_command(message) - _LOGGER.debug(u"WebSocket opened for command %s", [quote(x) for x in command]) + _LOGGER.debug(u"WebSocket opened for command %s", [shlex_quote(x) for x in command]) self.proc = tornado.process.Subprocess(command, stdout=tornado.process.Subprocess.STREAM, stderr=subprocess.STDOUT) @@ -131,6 +132,13 @@ class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket): return ["esphomeyaml", config_file, "clean"] +class EsphomeyamlHassConfigHandler(EsphomeyamlCommandWebSocket): + def build_command(self, message): + js = json.loads(message) + config_file = os.path.join(CONFIG_DIR, js['configuration']) + return ["esphomeyaml", config_file, "hass-config"] + + class SerialPortRequestHandler(BaseHandler): def get(self): if not self.is_authenticated(): @@ -229,6 +237,7 @@ def make_app(debug=False): (r"/validate", EsphomeyamlValidateHandler), (r"/clean-mqtt", EsphomeyamlCleanMqttHandler), (r"/clean", EsphomeyamlCleanHandler), + (r"/hass-config", EsphomeyamlHassConfigHandler), (r"/download.bin", DownloadBinaryRequestHandler), (r"/serial-ports", SerialPortRequestHandler), (r"/wizard.html", WizardRequestHandler), diff --git a/esphomeyaml/dashboard/templates/index.html b/esphomeyaml/dashboard/templates/index.html index ccb231ab9c..9ac93d0a9d 100644 --- a/esphomeyaml/dashboard/templates/index.html +++ b/esphomeyaml/dashboard/templates/index.html @@ -215,6 +215,7 @@ @@ -491,6 +492,18 @@ + + add @@ -904,6 +917,54 @@ }); }); + const hassConfigModalElem = document.getElementById("modal-hass-config"); + + document.querySelectorAll(".action-hass-config").forEach((btn) => { + btn.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(hassConfigModalElem); + const log = hassConfigModalElem.querySelector(".log"); + log.innerHTML = ""; + const stopLogsButton = hassConfigModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = hassConfigModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/hass-config"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + const msg = data.data; + log.innerHTML += colorReplace(msg); + } else if (data.event === "exit") { + if (data.code === 0) { + M.toast({html: "Program exited successfully."}); + downloadButton.classList.remove('disabled'); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } + stopLogsButton.innerHTML = "Close"; + stopped = true; + } + }); + logSocket.addEventListener('open', () => { + const msg = JSON.stringify({configuration: configuration}); + logSocket.send(msg); + }); + logSocket.addEventListener('close', () => { + if (!stopped) { + M.toast({html: 'Terminated process.'}); + } + }); + modalInstance.options.onCloseStart = () => { + logSocket.close(); + }; + }); + }); + const modalSetupElem = document.getElementById("modal-wizard"); const setupWizardStart = document.getElementById('setup-wizard-start'); const startWizard = () => { diff --git a/esphomeyaml/espota2.py b/esphomeyaml/espota2.py index f9b7b21190..f400175d79 100755 --- a/esphomeyaml/espota2.py +++ b/esphomeyaml/espota2.py @@ -4,6 +4,8 @@ import random import socket import sys +from esphomeyaml.core import ESPHomeYAMLError + RESPONSE_OK = 0 RESPONSE_REQUEST_AUTH = 1 @@ -27,22 +29,29 @@ OTA_VERSION_1_0 = 1 MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45] _LOGGER = logging.getLogger(__name__) +LAST_PROGRESS = -1 def update_progress(progress): + global LAST_PROGRESS + bar_length = 60 status = "" if progress >= 1: progress = 1 status = "Done...\r\n" + new_progress = int(progress * 100) + if new_progress == LAST_PROGRESS: + return + LAST_PROGRESS = new_progress block = int(round(bar_length * progress)) text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block), - int(progress * 100), status) + new_progress, status) sys.stderr.write(text) sys.stderr.flush() -class OTAError(Exception): +class OTAError(ESPHomeYAMLError): pass @@ -66,9 +75,9 @@ def receive_exactly(sock, amount, msg, expect, decode=True): try: check_error(data, expect) - except OTAError: + except OTAError as err: sock.close() - raise + raise OTAError("Error {}: {}".format(msg, err)) while len(data) < amount: try: @@ -85,7 +94,8 @@ def check_error(data, expect): if dat == RESPONSE_ERROR_MAGIC: raise OTAError("Error: Invalid magic byte") if dat == RESPONSE_ERROR_UPDATE_PREPARE: - raise OTAError("Error: Couldn't prepare flash memory for update. Is the binary too big?") + raise OTAError("Error: Couldn't prepare flash memory for update. Is the binary too big? " + "Please try restarting the ESP.") if dat == RESPONSE_ERROR_AUTH_INVALID: raise OTAError("Error: Authentication invalid. Is the password correct?") if dat == RESPONSE_ERROR_WRITING_FLASH: @@ -111,7 +121,7 @@ def send_check(sock, data, msg): data = ''.join([chr(x) for x in data]) elif isinstance(data, int): data = chr(data) - sock.send(data) + sock.sendall(data) except socket.error as err: raise OTAError("Error sending {}: {}".format(msg, err)) @@ -123,6 +133,8 @@ def perform_ota(sock, password, file_handle, filename): file_handle.seek(0) _LOGGER.debug("MD5 of binary is %s", file_md5) + # Enable nodelay, we need it for phase 1 + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) send_check(sock, MAGIC_BYTES, 'magic bytes') _, version = receive_exactly(sock, 2, 'version', RESPONSE_OK) @@ -169,7 +181,12 @@ def perform_ota(sock, password, file_handle, filename): send_check(sock, file_md5, 'file checksum') receive_exactly(sock, 1, 'file checksum', RESPONSE_BIN_MD5_OK) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096) + # Disable nodelay for transfer + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0) + # Limit send buffer (usually around 100kB) in order to have progress bar + # show the actual progress + sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192) + offset = 0 update_progress(0.0) while True: @@ -186,21 +203,66 @@ def perform_ota(sock, password, file_handle, filename): update_progress(offset / float(file_size)) + # Enable nodelay for last checks + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + sys.stderr.write('\n') _LOGGER.info("Waiting for result...") receive_exactly(sock, 1, 'receive OK', RESPONSE_RECEIVE_OK) receive_exactly(sock, 1, 'Update end', RESPONSE_UPDATE_END_OK) + send_check(sock, RESPONSE_OK, 'end acknowledgement') _LOGGER.info("OTA successful") -def run_ota(remote_host, remote_port, password, filename): - _LOGGER.info("Connecting to %s:%s...", remote_host, remote_port) - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(5.0) +def is_ip_address(host): + parts = host.split('.') + if len(parts) != 4: + return False try: - sock.connect((remote_host, remote_port)) + for p in parts: + int(p) + return True + except ValueError: + return False + + +def resolve_ip_address(host): + if is_ip_address(host): + _LOGGER.info("Connecting to %s", host) + return host + + _LOGGER.info("Resolving IP Address of %s", host) + hosts = [host] + if host.endswith('.local'): + hosts.append(host[:-6]) + + errors = [] + for x in hosts: + try: + ip = socket.gethostbyname(x) + break + except socket.error as err: + errors.append(err) + else: + _LOGGER.error("Error resolving IP address of %s. Is it connected to WiFi?", + host) + + _LOGGER.error("(If this error persists, please set a static IP address: " + "https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips)") + raise OTAError("Errors: {}".format(', '.join(str(x) for x in errors))) + + _LOGGER.info(" -> %s", ip) + return ip + + +def run_ota(remote_host, remote_port, password, filename): + ip = resolve_ip_address(remote_host) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(10.0) + try: + sock.connect((ip, remote_port)) except socket.error as err: sock.close() _LOGGER.error("Connecting to %s:%s failed: %s", remote_host, remote_port, err) diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index 3f517fdce2..fcd368bbbf 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -1,16 +1,15 @@ from __future__ import print_function +from collections import OrderedDict, deque import inspect import logging import os -import re -from collections import OrderedDict, deque from esphomeyaml import core from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \ CONF_INVERTED, \ CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PCF8574, \ - CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC + CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, CONF_SETUP_PRIORITY from esphomeyaml.core import ESPHomeYAMLError, HexInt, Lambda, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds @@ -408,15 +407,31 @@ def get_variable(id): yield None +def get_variable_with_full_id(id): + while True: + for k, v in _VARIABLES.iteritems(): + if k == id: + yield (k, v) + return + _LOGGER.debug("Waiting for variable %s", id) + yield None, None + + def process_lambda(value, parameters, capture='=', return_type=None): + from esphomeyaml.components.globals import GlobalVariableComponent + if value is None: yield return parts = value.parts[:] for i, id in enumerate(value.requires_ids): - var = None - for var in get_variable(id): + for full_id, var in get_variable_with_full_id(id): yield + if full_id is not None and isinstance(full_id.type, MockObjClass) and \ + full_id.type.inherits_from(GlobalVariableComponent): + parts[i * 3 + 1] = var.value() + continue + if parts[i * 3 + 2] == '.': parts[i * 3 + 1] = var._ else: @@ -531,6 +546,20 @@ class MockObj(Expression): obj.requires.append(self) return obj + def class_(self, name, *parents): + obj = MockObjClass(u'{}::{}'.format(self.base, name), u'.', parents=parents) + obj.requires.append(self) + return obj + + def struct(self, name): + return self.class_(name) + + def enum(self, name, is_class=False): + if is_class: + return self.namespace(name) + + return self + def operator(self, name): if name == 'ref': obj = MockObj(u'{} &'.format(self.base), u'') @@ -557,6 +586,37 @@ class MockObj(Expression): return obj +class MockObjClass(MockObj): + def __init__(self, *args, **kwargs): + parens = kwargs.pop('parents') + MockObj.__init__(self, *args, **kwargs) + self._parents = [] + for paren in parens: + if not isinstance(paren, MockObjClass): + raise ValueError + self._parents.append(paren) + # pylint: disable=protected-access + self._parents += paren._parents + + def inherits_from(self, other): + if self == other: + return True + for parent in self._parents: + if parent == other: + return True + return False + + def template(self, args): + if not isinstance(args, TemplateArguments): + args = TemplateArguments(args) + new_parents = self._parents[:] + new_parents.append(self) + obj = MockObjClass(u'{}{}'.format(self.base, args), parents=new_parents) + obj.requires.append(self) + obj.requires.append(args) + return obj + + global_ns = MockObj('', '') float_ = global_ns.namespace('float') bool_ = global_ns.namespace('bool') @@ -565,21 +625,30 @@ std_string = std_ns.string uint8 = global_ns.namespace('uint8_t') uint16 = global_ns.namespace('uint16_t') uint32 = global_ns.namespace('uint32_t') +int32 = global_ns.namespace('int32_t') const_char_p = global_ns.namespace('const char *') NAN = global_ns.namespace('NAN') esphomelib_ns = global_ns # using namespace esphomelib; NoArg = esphomelib_ns.NoArg App = esphomelib_ns.App -Application = esphomelib_ns.namespace('Application') -optional = esphomelib_ns.optional +io_ns = esphomelib_ns.namespace('io') +Nameable = esphomelib_ns.class_('Nameable') +Trigger = esphomelib_ns.class_('Trigger') +Action = esphomelib_ns.class_('Action') +Component = esphomelib_ns.class_('Component') +PollingComponent = esphomelib_ns.class_('PollingComponent', Component) +Application = esphomelib_ns.class_('Application') +optional = esphomelib_ns.class_('optional') arduino_json_ns = global_ns.namespace('ArduinoJson') -JsonObject = arduino_json_ns.JsonObject +JsonObject = arduino_json_ns.class_('JsonObject') JsonObjectRef = JsonObject.operator('ref') JsonObjectConstRef = JsonObjectRef.operator('const') +Controller = esphomelib_ns.class_('Controller') +StoringController = esphomelib_ns.class_('StoringController', Controller) -GPIOPin = esphomelib_ns.GPIOPin -GPIOOutputPin = esphomelib_ns.GPIOOutputPin -GPIOInputPin = esphomelib_ns.GPIOInputPin +GPIOPin = esphomelib_ns.class_('GPIOPin') +GPIOOutputPin = esphomelib_ns.class_('GPIOOutputPin', GPIOPin) +GPIOInputPin = esphomelib_ns.class_('GPIOInputPin', GPIOPin) def get_gpio_pin_number(conf): @@ -647,20 +716,9 @@ def setup_mqtt_component(obj, config): availability[CONF_PAYLOAD_NOT_AVAILABLE])) -# shlex's quote for Python 2.7 -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return u"''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" +def setup_component(obj, config): + if CONF_SETUP_PRIORITY in config: + add(obj.set_setup_priority(config[CONF_SETUP_PRIORITY])) def color(the_color, message='', reset=None): diff --git a/esphomeyaml/pins.py b/esphomeyaml/pins.py index 1b67860ed4..8d736ee9b2 100644 --- a/esphomeyaml/pins.py +++ b/esphomeyaml/pins.py @@ -2,11 +2,11 @@ import logging import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import core -from esphomeyaml.components import pcf8574 +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \ ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 +from esphomeyaml.helpers import Component, esphomelib_ns, io_ns _LOGGER = logging.getLogger(__name__) @@ -285,8 +285,11 @@ def shorthand_input_pin(value): return {CONF_NUMBER: value} +I2CDevice = esphomelib_ns.class_('I2CDevice') +PCF8574Component = io_ns.class_('PCF8574Component', Component, I2CDevice) + PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({ - vol.Required(CONF_PCF8574): cv.use_variable_id(pcf8574.PCF8574Component), + vol.Required(CONF_PCF8574): cv.use_variable_id(PCF8574Component), vol.Required(CONF_NUMBER): vol.Coerce(int), vol.Optional(CONF_MODE): vol.All(vol.Upper, cv.one_of("OUTPUT")), vol.Optional(CONF_INVERTED, default=False): cv.boolean, diff --git a/esphomeyaml/platformio_api.py b/esphomeyaml/platformio_api.py new file mode 100644 index 0000000000..74f8997ed1 --- /dev/null +++ b/esphomeyaml/platformio_api.py @@ -0,0 +1,211 @@ +import json +import logging +import re +import subprocess + +from esphomeyaml.const import CONF_BUILD_PATH, CONF_ESPHOMEYAML +from esphomeyaml.helpers import relative_path +from esphomeyaml.util import run_external_command + +_LOGGER = logging.getLogger(__name__) + + +def run_platformio_cli(*args, **kwargs): + import platformio.__main__ + + cmd = ['platformio'] + list(args) + return run_external_command(platformio.__main__.main, + *cmd, **kwargs) + + +def run_platformio_cli_run(config, verbose, *args, **kwargs): + build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) + command = ['run', '-d', build_path] + if verbose: + command += ['-v'] + command += list(args) + return run_platformio_cli(*command, **kwargs) + + +def run_compile(config, verbose): + return run_platformio_cli_run(config, verbose) + + +def run_upload(config, verbose, port): + return run_platformio_cli_run(config, verbose, '-t', 'upload', '--upload-port', port) + + +def run_idedata(config): + args = ['-t', 'idedata'] + stdout = run_platformio_cli_run(config, False, *args, capture_stdout=True) + match = re.search(r'{.*}', stdout) + if match is None: + return IDEData(None) + try: + return IDEData(json.loads(match.group())) + except ValueError: + return IDEData(None) + + +IDE_DATA = None + + +def get_idedata(config): + global IDE_DATA + + if IDE_DATA is None: + _LOGGER.info("Need to fetch platformio IDE-data, please stand by") + IDE_DATA = run_idedata(config) + return IDE_DATA + + +# ESP logs stack trace decoder, based on https://github.com/me-no-dev/EspExceptionDecoder +ESP8266_EXCEPTION_CODES = { + 0: "Illegal instruction", + 1: "SYSCALL instruction", + 2: "InstructionFetchError: Processor internal physical address or data error during " + "instruction fetch", + 3: "LoadStoreError: Processor internal physical address or data error during load or store", + 4: "Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT " + "register", + 5: "Alloca: MOVSP instruction, if caller's registers are not in the register file", + 6: "IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero", + 7: "reserved", + 8: "Privileged: Attempt to execute a privileged operation when CRING ? 0", + 9: "LoadStoreAlignmentCause: Load or store to an unaligned address", + 10: "reserved", + 11: "reserved", + 12: "InstrPIFDataError: PIF data error during instruction fetch", + 13: "LoadStorePIFDataError: Synchronous PIF data error during LoadStore access", + 14: "InstrPIFAddrError: PIF address error during instruction fetch", + 15: "LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access", + 16: "InstTLBMiss: Error during Instruction TLB refill", + 17: "InstTLBMultiHit: Multiple instruction TLB entries matched", + 18: "InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level " + "less than CRING", + 19: "reserved", + 20: "InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute " + "that does not permit instruction fetch", + 21: "reserved", + 22: "reserved", + 23: "reserved", + 24: "LoadStoreTLBMiss: Error during TLB refill for a load or store", + 25: "LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store", + 26: "LoadStorePrivilege: A load or store referenced a virtual address at a ring level less " + "than ", + 27: "reserved", + 28: "LoadProhibited: A load referenced a page mapped with an attribute that does not permit " + "loads", + 29: "StoreProhibited: A store referenced a page mapped with an attribute that does not permit " + "stores", +} + + +def _decode_pc(config, addr): + idedata = get_idedata(config) + if not idedata.addr2line_path or not idedata.firmware_elf_path: + return + command = [idedata.addr2line_path, '-pfiaC', '-e', idedata.firmware_elf_path, addr] + try: + translation = subprocess.check_output(command).strip() + except Exception: # pylint: disable=broad-except + return + + if "?? ??:0" in translation: + # Nothing useful + return + translation = translation.replace(' at ??:?', '').replace(':?', '') + _LOGGER.warning("Decoded %s", translation) + + +def _parse_register(config, regex, line): + match = regex.match(line) + if match is not None: + _decode_pc(config, match.group(1)) + + +STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r'Exception \(([0-9]*)\):') +STACKTRACE_ESP8266_PC_RE = re.compile(r'epc1=0x(4[0-9a-fA-F]{7})') +STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r'excvaddr=0x(4[0-9a-fA-F]{7})') +STACKTRACE_ESP32_PC_RE = re.compile(r'PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') +STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r'EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') +STACKTRACE_BAD_ALLOC_RE = re.compile(r'^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$') +STACKTRACE_ESP32_BACKTRACE_RE = re.compile(r'Backtrace:(?:\s+0x4[0-9a-fA-F]{7}:0x3[0-9a-fA-F]{7})+') +STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') +STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') + + +def process_stacktrace(config, line, backtrace_state): + line = line.strip() + # ESP8266 Exception type + match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line) + if match is not None: + code = match.group(1) + _LOGGER.warning("Exception type: %s", ESP8266_EXCEPTION_CODES.get(code, 'unknown')) + + # ESP8266 PC/EXCVADDR + _parse_register(config, STACKTRACE_ESP8266_PC_RE, line) + _parse_register(config, STACKTRACE_ESP8266_EXCVADDR_RE, line) + # ESP32 PC/EXCVADDR + _parse_register(config, STACKTRACE_ESP32_PC_RE, line) + _parse_register(config, STACKTRACE_ESP32_EXCVADDR_RE, line) + + # bad alloc + match = re.match(STACKTRACE_BAD_ALLOC_RE, line) + if match is not None: + _LOGGER.warning("Memory allocation of %s bytes failed at %s", + match.group(2), match.group(1)) + _decode_pc(config, match.group(1)) + + # ESP32 single-line backtrace + match = re.match(STACKTRACE_ESP32_BACKTRACE_RE, line) + if match is not None: + _LOGGER.warning("Found stack trace! Trying to decode it") + for addr in re.finditer(STACKTRACE_ESP32_BACKTRACE_PC_RE, line): + _decode_pc(config, addr.group()) + + # ESP8266 multi-line backtrace + if '>>>stack>>>' in line: + # Start of backtrace + backtrace_state = True + _LOGGER.warning("Found stack trace! Trying to decode it") + elif '<</.platformio/packages/toolchain-xtensa32/bin/xtensa-esp32-elf-gcc + return self.raw.get("cc_path") + + @property + def addr2line_path(self): + cc_path = self.cc_path + if cc_path is None: + return None + # replace gcc at end with addr2line + return cc_path[:-3] + 'addr2line' diff --git a/esphomeyaml/util.py b/esphomeyaml/util.py index c52b54284f..bb9f4b3e66 100644 --- a/esphomeyaml/util.py +++ b/esphomeyaml/util.py @@ -1,5 +1,12 @@ from __future__ import print_function +import io +import logging +import re +import sys + +_LOGGER = logging.getLogger(__name__) + class Registry(dict): def register(self, name): @@ -30,3 +37,47 @@ def safe_print(message=""): print(message.encode('ascii', 'backslashreplace')) except UnicodeEncodeError: print("Cannot print line because of invalid locale!") + + +def shlex_quote(s): + if not s: + return u"''" + if re.search(r'[^\w@%+=:,./-]', s) is None: + return s + + return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" + + +def run_external_command(func, *cmd, **kwargs): + def mock_exit(return_code): + raise SystemExit(return_code) + + orig_argv = sys.argv + orig_exit = sys.exit # mock sys.exit + full_cmd = u' '.join(shlex_quote(x) for x in cmd) + _LOGGER.info(u"Running: %s", full_cmd) + + capture_stdout = kwargs.get('capture_stdout', False) + if capture_stdout: + sys.stdout = io.BytesIO() + + try: + sys.argv = list(cmd) + sys.exit = mock_exit + return func() or 0 + except KeyboardInterrupt: + return 1 + except SystemExit as err: + return err.args[0] + except Exception as err: # pylint: disable=broad-except + _LOGGER.error(u"Running command failed: %s", err) + _LOGGER.error(u"Please try running %s locally.", full_cmd) + finally: + sys.argv = orig_argv + sys.exit = orig_exit + + if capture_stdout: + # pylint: disable=lost-exception + stdout = sys.stdout.getvalue() + sys.stdout = sys.__stdout__ + return stdout diff --git a/esphomeyaml/writer.py b/esphomeyaml/writer.py index c045fade11..4f1900c662 100644 --- a/esphomeyaml/writer.py +++ b/esphomeyaml/writer.py @@ -103,6 +103,7 @@ def get_ini_content(config, path): build_flags |= get_build_flags(config, 'BUILD_FLAGS') build_flags.add(u"-DESPHOMEYAML_USE") build_flags.add("-Wno-unused-variable") + build_flags.add("-Wno-unused-but-set-variable") build_flags |= get_build_flags(config, 'required_build_flags') build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS') diff --git a/tests/test1.yaml b/tests/test1.yaml index 6961bd2598..d1eabb16b9 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -67,12 +67,10 @@ mqtt: ESP_LOGD("main", "Got message %s", x.c_str()); - topic: livingroom/ota_mode then: - - deep_sleep.prevent: - id: deep_sleep_1 + - deep_sleep.prevent: deep_sleep_1 - topic: livingroom/ota_mode then: - - deep_sleep.enter: - id: deep_sleep_1 + - deep_sleep.enter: deep_sleep_1 on_json_message: topic: the/topic then: @@ -97,6 +95,7 @@ i2c: scl: 22 scan: True frequency: 100kHz + setup_priority: -100 spi: clk_pin: GPIO21 @@ -157,6 +156,7 @@ sensor: icon: "mdi:water-percent" accuracy_decimals: 5 expire_after: 120s + setup_priority: -100 filters: - offset: 2.0 - multiply: 1.2 @@ -165,6 +165,7 @@ sensor: - sliding_window_moving_average: window_size: 15 send_every: 15 + send_first_at: 15 - exponential_moving_average: alpha: 0.1 send_every: 15 @@ -309,10 +310,14 @@ sensor: name: "HLW8012 Voltage" power: name: "HLW8012 Power" + id: hlw8012_power update_interval: 15s current_resistor: 0.001 ohm voltage_divider: 2351 change_mode_every: 16 + - platform: total_daily_energy + power_id: hlw8012_power + name: "HLW8012 Total Daily Energy" - platform: hmc5883l address: 0x68 field_strength_x: @@ -497,6 +502,31 @@ binary_sensor: - then: - lambda: >- ESP_LOGD("main", "Double Clicked"); + on_multi_click: + - timing: + - ON for at most 1s + - OFF for at most 1s + - ON for at most 1s + - OFF for at least 0.2s + then: + - logger.log: + format: "Multi Clicked TWO" + level: warn + - timing: + - OFF for 1s to 2s + - ON for 1s to 2s + - OFF for at least 0.5s + then: + - logger.log: + format: "Multi Clicked LONG SINGLE" + level: warn + - timing: + - ON for at most 1s + - OFF for at least 0.5s + then: + - logger.log: + format: "Multi Clicked SINGLE" + level: warn id: binary_sensor1 - platform: status name: "Living Room Status" @@ -541,6 +571,13 @@ pca9685: frequency: 500 address: 0x0 +my9231: + data_pin: GPIO12 + clock_pin: GPIO14 + num_channels: 6 + num_chips: 2 + bit_depth: 16 + output: - platform: gpio pin: GPIO26 @@ -585,6 +622,24 @@ output: number: 0 mode: OUTPUT inverted: False + - platform: my9231 + id: my_0 + channel: 0 + - platform: my9231 + id: my_1 + channel: 1 + - platform: my9231 + id: my_2 + channel: 2 + - platform: my9231 + id: my_3 + channel: 3 + - platform: my9231 + id: my_4 + channel: 4 + - platform: my9231 + id: my_5 + channel: 5 light: - platform: binary @@ -715,7 +770,6 @@ switch: icon: "mdi:restart" inverted: True command_topic: custom_command_topic - power_on_value: True - platform: remote_transmitter name: "Panasonic TV Off" nec: @@ -797,8 +851,15 @@ switch: optimistic: True turn_on_action: - switch.turn_on: living_room_lights_on + - output.set_level: + id: gpio_19 + level: 50% + - output.set_level: + id: gpio_19 + level: !lambda 'return 0.5;' turn_off_action: - switch.turn_on: living_room_lights_off + restore_state: False - platform: restart name: "Living Room Restart" - platform: shutdown @@ -825,12 +886,26 @@ switch: // Switch is OFF, do something else here } optimistic: true + restore_state: True - platform: uart name: "UART String Output" data: 'DataToSend' - platform: uart name: "UART Bytes Output" data: [0xDE, 0xAD, 0xBE, 0xEF] + - platform: template + optimistic: true + name: Stepper Switch + turn_on_action: + - stepper.set_target: + id: my_stepper + target: !lambda |- + static int32_t i = 0; + i += 1000; + if (i > 5000) { + i = -5000; + } + return i; fan: - platform: binary @@ -941,3 +1016,32 @@ pcf8574: - id: 'pcf8574_hub' address: 0x21 pcf8575: False + +stepper: +- platform: a4988 + id: my_stepper + step_pin: GPIO23 + dir_pin: GPIO24 + sleep_pin: GPIO25 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 + + +globals: +- id: glob_int + type: int + restore_value: yes + initial_value: '0' +- id: glob_float + type: float + restore_value: yes + initial_value: '0.0f' +- id: glob_bool + type: bool + restore_value: no + initial_value: 'true' +- id: glob_string + type: std::string + restore_value: no + # initial_value: ""