diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3d46b9a2f9..52ee89e4f6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help us improve +about: Create a report to help esphomelib improve --- @@ -9,7 +9,9 @@ about: Create a report to help us improve - esphomeyaml [here] - This is mostly for reporting bugs when compiling and when you get a long stack trace while compiling or if a configuration fails to validate. - esphomelib [https://github.com/OttoWinter/esphomelib] - Report bugs there if the ESP is crashing or a feature is not working as expected. - esphomedocs [https://github.com/OttoWinter/esphomedocs] - Report bugs there if the documentation is wrong/outdated. -- Provide as many details as possible. Paste logs, configuration sample and code into the backticks (```). Do not delete any text from this template! +- Provide as many details as possible. Paste logs, configuration sample and code into the backticks (```). + + DO NOT DELETE ANY TEXT from this template! Otherwise the issue may be closed without a comment. --> **Operating environment (Hass.io/Docker/pip/etc.):** @@ -33,7 +35,7 @@ Please add the link to the documentation at https://esphomelib.com/esphomeyaml/i **Problem-relevant YAML-configuration entries:** ```yaml - +PASTE YAML FILE HERE ``` **Traceback (if applicable):** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 05b02948b2..2a033e374e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,16 +7,15 @@ about: Suggest an idea for this project -**Is your feature request related to a problem? Please describe.** - -Ex. I'm always frustrated when [...] +**Is your feature request related to a problem/use-case? Please describe.** + -**Describe the solution you'd like** -A description of what you want to happen. +**Describe the solution you'd like:** + -**Additional context** -Add any other context about the feature request here. +**Additional context:** + diff --git a/.github/ISSUE_TEMPLATE/new-integration.md b/.github/ISSUE_TEMPLATE/new-integration.md index fe80521c6f..34fdb086ad 100644 --- a/.github/ISSUE_TEMPLATE/new-integration.md +++ b/.github/ISSUE_TEMPLATE/new-integration.md @@ -4,17 +4,10 @@ about: Suggest a new integration for esphomelib --- - +DO NOT POST NEW INTEGRATION REQUESTS HERE! -**What new integration would you wish to have?** - +Please post all new integration requests in the esphomelib repository: -**If possible, provide a link to an existing library for the integration:** +https://github.com/OttoWinter/esphomelib/issues -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Additional context** +Thank you! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6e1fc9be00..f0fdd660ae 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,15 +6,9 @@ **Pull request in [esphomedocs](https://github.com/OttoWinter/esphomedocs) with documentation (if applicable):** OttoWinter/esphomedocs# **Pull request in [esphomelib](https://github.com/OttoWinter/esphomelib) with C++ framework changes (if applicable):** OttoWinter/esphomelib# -## Example entry for YAML configuration (if applicable): -```yaml - -``` - ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). - - [ ] Check this box if you have read, understand, comply, and agree with the [Code of Conduct](https://github.com/OttoWinter/esphomeyaml/blob/master/CODE_OF_CONDUCT.md). If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs). diff --git a/.gitignore b/.gitignore index 9de7ba163d..d871a420e0 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,4 @@ venv.bak/ config/ tests/build/ +tests/.esphomeyaml/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20493fea0f..f18b5047a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,8 @@ stages: .lint: &lint stage: lint + before_script: + - pip install -e . tags: - python2.7 - esphomeyaml-lint @@ -24,9 +26,6 @@ stages: - esphomeyaml-test variables: TZ: UTC - cache: - paths: - - tests/build .docker-builder: &docker-builder before_script: @@ -62,21 +61,20 @@ test2: stage: build script: - docker run --rm --privileged hassioaddons/qemu-user-static:latest - - BUILD_FROM=homeassistant/${ADDON_ARCH}-base-ubuntu:latest + - BUILD_FROM=hassioaddons/ubuntu-base-${ADDON_ARCH}:2.2.0 - 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}" - | 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}" \ + --build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")" \ + --build-arg "BUILD_ARCH=${ADDON_ARCH}" \ + --build-arg "BUILD_REF=${CI_COMMIT_SHA}" \ + --build-arg "BUILD_VERSION=${ADDON_VERSION}" \ --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" \ --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ --file "docker/Dockerfile.hassio" \ @@ -95,48 +93,48 @@ test2: script: - version="${CI_COMMIT_TAG#v}" - echo "Publishing release version ${version}" - - docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - echo "Tag ${CI_REGISTRY}/ottowinter/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}" + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - | docker tag \ - "${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" + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - echo "Tag ${CI_REGISTRY}/ottowinter/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" + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - | docker tag \ - "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/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 \ - "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - 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}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" only: @@ -150,34 +148,34 @@ test2: script: - version="${CI_COMMIT_TAG#v}" - echo "Publishing beta version ${version}" - - docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - echo "Tag ${CI_REGISTRY}/ottowinter/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}" + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - echo "Tag ${CI_REGISTRY}/ottowinter/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" + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - | docker tag \ - "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/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}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" only: @@ -190,7 +188,7 @@ build:normal: <<: *docker-builder stage: build script: - - docker build -t "${CI_REGISTRY}/esphomeyaml:dev" . + - docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" . .build-hassio-edge: &build-hassio-edge <<: *build-hassio @@ -214,7 +212,6 @@ build:hassio-armhf: <<: *build-hassio-release variables: ADDON_ARCH: armhf - ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" #build:hassio-aarch64-edge: # <<: *build-hassio-edge @@ -226,7 +223,6 @@ build:hassio-armhf: # <<: *build-hassio-release # variables: # ADDON_ARCH: aarch64 -# ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" build:hassio-i386-edge: <<: *build-hassio-edge @@ -238,7 +234,6 @@ build:hassio-i386: <<: *build-hassio-release variables: ADDON_ARCH: i386 - ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" build:hassio-amd64-edge: <<: *build-hassio-edge @@ -250,7 +245,6 @@ build:hassio-amd64: <<: *build-hassio-release variables: ADDON_ARCH: amd64 - ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" # Deploy jobs deploy-release:armhf: @@ -267,7 +261,7 @@ deploy-beta:armhf: # <<: *deploy-release # variables: # ADDON_ARCH: aarch64 -# + #deploy-beta:aarch64: # <<: *deploy-beta # variables: diff --git a/.travis.yml b/.travis.yml index f928b372a0..ce9b00e2e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,30 @@ sudo: false language: python -python: - - "2.7" -jobs: + +matrix: + fast_finish: true include: - - name: "Lint" - install: - - pip install -r requirements.txt - - pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow + - python: "2.7" + env: TARGET=Lint2.7 + install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow script: - flake8 esphomeyaml - pylint esphomeyaml - - name: "Test" - install: - - pip install -e . - - pip install tzlocal pillow + - python: "3.5.3" + env: TARGET=Lint3.5 + install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow + script: + - flake8 esphomeyaml + - pylint esphomeyaml + - python: "2.7" + env: TARGET=Test2.7 + install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow + script: + - esphomeyaml tests/test1.yaml compile + - esphomeyaml tests/test2.yaml compile + - python: "3.5.3" + env: TARGET=Test3.5 + install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow script: - esphomeyaml tests/test1.yaml compile - esphomeyaml tests/test2.yaml compile diff --git a/Dockerfile b/Dockerfile index 48342c0124..4af3ac8a3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,8 +21,7 @@ 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 +RUN pip install --no-cache-dir --no-binary :all: -e . WORKDIR /config ENTRYPOINT ["esphomeyaml"] diff --git a/MANIFEST.in b/MANIFEST.in index ab00d7e896..83308b2be5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,17 @@ include README.md include esphomeyaml/dashboard/templates/index.html +include esphomeyaml/dashboard/templates/login.html +include esphomeyaml/dashboard/static/ace.js +include esphomeyaml/dashboard/static/esphomeyaml.css +include esphomeyaml/dashboard/static/esphomeyaml.js +include esphomeyaml/dashboard/static/favicon.ico +include esphomeyaml/dashboard/static/jquery.min.js +include esphomeyaml/dashboard/static/jquery.validate.min.js +include esphomeyaml/dashboard/static/jquery-ui.min.js +include esphomeyaml/dashboard/static/materialize.min.css +include esphomeyaml/dashboard/static/materialize.min.js include esphomeyaml/dashboard/static/materialize-stepper.min.css include esphomeyaml/dashboard/static/materialize-stepper.min.js +include esphomeyaml/dashboard/static/mode-yaml.js +include esphomeyaml/dashboard/static/theme-dreamweaver.js +include esphomeyaml/dashboard/static/ext-searchbox.js diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio index 651f8b8a68..f0f4165263 100644 --- a/docker/Dockerfile.hassio +++ b/docker/Dockerfile.hassio @@ -1,42 +1,75 @@ -# Dockerfile for HassIO add-on -ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest +ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0 +# hadolint ignore=DL3006 FROM ${BUILD_FROM} -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Set shell +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Copy root filesystem +COPY esphomeyaml-edge/rootfs / +COPY setup.py setup.cfg MANIFEST.in /opt/esphomeyaml/ +COPY esphomeyaml /opt/esphomeyaml/esphomeyaml + +RUN \ + # Temporarily move nginx.conf (otherwise dpkg fails) + mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp \ + # Install add-on dependencies + && apt-get update \ + && apt-get install -y --no-install-recommends \ + # Python for esphomeyaml python \ python-pip \ python-setuptools \ + # Python Pillow for display component python-pil \ + # Git for esphomelib downloads 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"] + # Ping for dashboard online/offline status + iputils-ping \ + # NGINX proxy + nginx \ + \ + && mv /etc/nginx/nginx.conf.bkp /etc/nginx/nginx.conf \ + \ + && pip2 install --no-cache-dir --no-binary :all: -e /opt/esphomeyaml \ + \ + # Change some platformio settings + && 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 \ + \ + # Build an empty platformio project to force platformio to install all fw build dependencies + # The return-code will be non-zero since there's nothing to build. + && (platformio run -d /opt/pio; echo "Done") \ + \ + # Cleanup + && rm -fr \ + /tmp/* \ + /var/{cache,log}/* \ + /var/lib/apt/lists/* \ + /opt/pio/ # Build arugments -ARG ADDON_ARCH -ARG ADDON_VERSION +ARG BUILD_ARCH=amd64 +ARG BUILD_DATE +ARG BUILD_REF +ARG BUILD_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.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + io.hass.arch="${BUILD_ARCH}" \ io.hass.type="addon" \ - io.hass.version="${ADDON_VERSION}" \ - io.hass.url="https://esphomelib.com/esphomeyaml/index.html" \ - maintainer="Otto Winter " + io.hass.version=${BUILD_VERSION} \ + maintainer="Otto Winter " \ + org.label-schema.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + org.label-schema.build-date=${BUILD_DATE} \ + org.label-schema.name="esphomeyaml" \ + org.label-schema.schema-version="1.0" \ + org.label-schema.url="https://esphomelib.com" \ + org.label-schema.usage="https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml/README.md" \ + org.label-schema.vcs-ref=${BUILD_REF} \ + org.label-schema.vcs-url="https://github.com/OttoWinter/esphomeyaml" \ + org.label-schema.vendor="esphomelib" \ No newline at end of file diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index dc87801b53..9e16b74fbd 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -3,4 +3,4 @@ FROM python:2.7 COPY requirements.txt /requirements.txt RUN pip install -r /requirements.txt && \ - pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow + pip install flake8==3.6.0 pylint==1.9.4 pillow diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index 2d7206eba7..32978c0785 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -8,12 +8,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ 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 + 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 COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt && \ - pip install --no-cache-dir tzlocal pillow +RUN pip install --no-cache-dir -r /requirements.txt diff --git a/esphomeyaml-beta/config.json b/esphomeyaml-beta/config.json index 0c741b3f58..9802be83df 100644 --- a/esphomeyaml-beta/config.json +++ b/esphomeyaml-beta/config.json @@ -1,8 +1,8 @@ { "name": "esphomeyaml-beta", - "version": "1.9.0b5", + "version": "1.9.3", "slug": "esphomeyaml-beta", - "description": "Beta version of esphomeyaml HassIO add-on.", + "description": "Beta version of esphomeyaml Hass.io add-on.", "url": "https://beta.esphomelib.com/esphomeyaml/index.html", "startup": "application", "webui": "http://[HOST]:[PORT:6052]", diff --git a/esphomeyaml-beta/icon.png b/esphomeyaml-beta/icon.png new file mode 100644 index 0000000000..6018ac5fc7 Binary files /dev/null and b/esphomeyaml-beta/icon.png differ diff --git a/esphomeyaml-beta/logo.png b/esphomeyaml-beta/logo.png new file mode 100644 index 0000000000..a202ef0a28 Binary files /dev/null and b/esphomeyaml-beta/logo.png differ diff --git a/esphomeyaml-edge/Dockerfile b/esphomeyaml-edge/Dockerfile index 6c81bcaf29..e258d8b0f0 100644 --- a/esphomeyaml-edge/Dockerfile +++ b/esphomeyaml-edge/Dockerfile @@ -1,24 +1,73 @@ -# Dockerfile for HassIO edge add-on -ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest +ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0 +# hadolint ignore=DL3006 FROM ${BUILD_FROM} -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Set shell +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Copy root filesystem +COPY rootfs / + +RUN \ + # Temporarily move nginx.conf (otherwise dpkg fails) + mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp \ + # Install add-on dependencies + && apt-get update \ + && apt-get install -y --no-install-recommends \ + # Python for esphomeyaml python \ python-pip \ python-setuptools \ + # Python Pillow for display component python-pil \ + # Git for esphomelib downloads 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 + # Ping for dashboard online/offline status + iputils-ping \ + # NGINX proxy + nginx \ + \ + && mv /etc/nginx/nginx.conf.bkp /etc/nginx/nginx.conf \ + \ + && pip2 install --no-cache-dir --no-binary :all: https://github.com/OttoWinter/esphomeyaml/archive/dev.zip \ + \ + # Change some platformio settings + && 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 \ + \ + # Build an empty platformio project to force platformio to install all fw build dependencies + # The return-code will be non-zero since there's nothing to build. + && (platformio run -d /opt/pio; echo "Done") \ + \ + # Cleanup + && rm -fr \ + /tmp/* \ + /var/{cache,log}/* \ + /var/lib/apt/lists/* \ + /opt/pio/ -COPY platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio +# Build arugments +ARG BUILD_ARCH=amd64 +ARG BUILD_DATE +ARG BUILD_REF +ARG BUILD_VERSION -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"] +# Labels +LABEL \ + io.hass.name="esphomeyaml-edge" \ + io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="Otto Winter " \ + org.label-schema.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + org.label-schema.build-date=${BUILD_DATE} \ + org.label-schema.name="esphomeyaml-edge" \ + org.label-schema.schema-version="1.0" \ + org.label-schema.url="https://esphomelib.com" \ + org.label-schema.usage="https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml-edge/README.md" \ + org.label-schema.vcs-ref=${BUILD_REF} \ + org.label-schema.vcs-url="https://github.com/OttoWinter/esphomeyaml" \ + org.label-schema.vendor="esphomelib" diff --git a/esphomeyaml-edge/README.md b/esphomeyaml-edge/README.md new file mode 100644 index 0000000000..1fb5d03ea8 --- /dev/null +++ b/esphomeyaml-edge/README.md @@ -0,0 +1,109 @@ +# Esphomeyaml Hass.io Add-On + +[![esphomeyaml logo](https://raw.githubusercontent.com/OttoWinter/esphomeyaml/dev/esphomeyaml-edge/logo.png)](https://esphomelib.com/esphomeyaml/index.html) + +[![GitHub stars](https://img.shields.io/github/stars/OttoWinter/esphomelib.svg?style=social&label=Star&maxAge=2592000)](https://github.com/OttoWinter/esphomelib) +[![GitHub Release][releases-shield]][releases] +[![Discord][discord-shield]][discord] + +## About + +This add-on allows you to manage and program your ESP8266 and ESP32 based microcontrollers +directly through Hass.io **with no programming experience required**. All you need to do +is write YAML configuration files; the rest (over-the-air updates, compiling) is all +handled by esphomeyaml. + +

+ +

+ +[_View the esphomeyaml documentation here_](https://esphomelib.com/esphomeyaml/index.html) + +## Example + +With esphomeyaml, you can go from a few lines of YAML straight to a custom-made +firmware. For example, to include a [DHT22][dht22]. +temperature and humidity sensor, you just need to include 8 lines of YAML +in your configuration file: + + + +Then just click UPLOAD and the sensor will magically appear in Home Assistant: + + + +## Installation + +To install this Hass.io add-on you need to add the esphomeyaml add-on repository +first: + +1. Add the epshomeyaml add-ons repository to your Hass.io instance. You can do this by navigating to the "Add-on Store" tab in the Hass.io panel and then entering https://github.com/OttoWinter/esphomeyaml in the "Add new repository by URL" field. +2. Now scroll down and select the "esphomeyaml" add-on. +3. Press install to download the add-on and unpack it on your machine. This can take some time. +4. Optional: If you're using SSL certificates and want to encrypt your communication to this add-on, please enter `true` into the `ssl` field and set the `fullchain` and `certfile` options accordingly. +5. Start the add-on, check the logs of the add-on to see if everything went well. +6. Click "OPEN WEB UI" to open the esphomeyaml dashboard. You will be asked for your Home Assistant credentials - esphomeyaml uses Hass.io's authentication system to log you in. + +**NOTE**: Installation on RPis running in 64-bit mode is currently not possible. Please use the 32-bit variant of HassOS instead. + +You can view the esphomeyaml docs here: https://esphomelib.com/esphomeyaml/index.html + +## Configuration + +**Note**: _Remember to restart the add-on when the configuration is changed._ + +Example add-on configuration: + +```json +{ + "ssl": false, + "certfile": "fullchain.pem", + "keyfile": "privkey.pem", + "port": 6052 +} +``` + +### Option: `port` + +The port to start the dashboard server on. Default is 6052. + +### Option: `ssl` + +Enables/Disables encrypted SSL (HTTPS) connections to the web server of this add-on. +Set it to `true` to encrypt communications, `false` otherwise. +Please note that if you set this to `true` you must also generate the key and certificate +files for encryption. For example using [Let's Encrypt](https://www.home-assistant.io/addons/lets_encrypt/) +or [Self-signed certificates](https://www.home-assistant.io/docs/ecosystem/certificates/tls_self_signed_certificate/). + +### Option: `certfile` + +The certificate file to use for SSL. If this file doesn't exist, the add-on start will fail. + +**Note**: The file MUST be stored in `/ssl/`, which is the default for Hass.io + +### Option: `keyfile` + +The private key file to use for SSL. If this file doesn't exist, the add-on start will fail. + +**Note**: The file MUST be stored in `/ssl/`, which is the default for Hass.io + +### Option: `leave_front_door_open` + +Adding this option to the add-on configuration allows you to disable +authentication by setting it to `true`. + +### Option: `esphomeyaml_version` + +Manually override which esphomeyaml version to use in the addon. +For example to install the latest development version, use `"esphomeyaml_version": "dev"`, +or for version 1.10.0: `"esphomeyaml_version": "v1.10.0""`. + +Please note that this does not always work and is only meant for testing, usually the +esphomeyaml add-on and dashboard version must match to guarantee a working system. + +[discord-shield]: https://img.shields.io/discord/429907082951524364.svg +[dht22]: https://esphomelib.com/esphomeyaml/components/sensor/dht.html +[discord]: https://discord.me/KhAMKrd +[releases-shield]: https://img.shields.io/github/release/OttoWinter/esphomeyaml.svg +[releases]: https://esphomelib.com/esphomeyaml/changelog/index.html +[repository]: https://github.com/OttoWinter/esphomeyaml diff --git a/esphomeyaml-edge/build.json b/esphomeyaml-edge/build.json index f60fa4f7fe..eb0295384a 100644 --- a/esphomeyaml-edge/build.json +++ b/esphomeyaml-edge/build.json @@ -1,10 +1,10 @@ { - "squash": false, - "build_from": { - "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": {} + "squash": false, + "build_from": { + "aarch64": "hassioaddons/ubuntu-base-aarch64:2.2.0", + "amd64": "hassioaddons/ubuntu-base-amd64:2.2.0", + "armhf": "hassioaddons/ubuntu-base-armhf:2.2.0", + "i386": "hassioaddons/ubuntu-base-i386:2.2.0" + }, + "args": {} } diff --git a/esphomeyaml-edge/config.json b/esphomeyaml-edge/config.json index 951d7d56cc..540e13241f 100644 --- a/esphomeyaml-edge/config.json +++ b/esphomeyaml-edge/config.json @@ -2,32 +2,38 @@ "name": "esphomeyaml-edge", "version": "dev", "slug": "esphomeyaml-edge", - "description": "Development build of the esphomeyaml HassIO add-on.", - "url": "https://esphomelib.com/esphomeyaml/index.html", - "startup": "application", + "description": "Development Version! Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files", + "url": "https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml-edge/README.md", "webui": "http://[HOST]:[PORT:6052]", - "boot": "auto", - "ports": { - "6052/tcp": 6052, - "6053/tcp": 6053 - }, + "startup": "application", "arch": [ "aarch64", "amd64", "armhf", "i386" ], - "auto_uart": true, + "hassio_api": true, + "auth_api": true, + "hassio_role": "default", + "homeassistant_api": false, + "host_network": true, + "boot": "auto", "map": [ + "ssl", "config:rw" ], "options": { - "password": "" + "ssl": false, + "certfile": "fullchain.pem", + "keyfile": "privkey.pem", + "port": 6052 }, "schema": { - "password": "str?" - }, - "environment": { - "ESPHOMEYAML_OTA_HOST_PORT": "6053" + "ssl": "bool", + "certfile": "str", + "keyfile": "str", + "port": "int", + "leave_front_door_open": "bool?", + "esphomeyaml_version": "str?" } } diff --git a/esphomeyaml-edge/icon.png b/esphomeyaml-edge/icon.png new file mode 100644 index 0000000000..6018ac5fc7 Binary files /dev/null and b/esphomeyaml-edge/icon.png differ diff --git a/esphomeyaml-edge/images/dht-example.png b/esphomeyaml-edge/images/dht-example.png new file mode 100644 index 0000000000..9d984cb25a Binary files /dev/null and b/esphomeyaml-edge/images/dht-example.png differ diff --git a/esphomeyaml-edge/images/screenshot.png b/esphomeyaml-edge/images/screenshot.png new file mode 100644 index 0000000000..193e01589b Binary files /dev/null and b/esphomeyaml-edge/images/screenshot.png differ diff --git a/esphomeyaml-edge/images/temperature-humidity.png b/esphomeyaml-edge/images/temperature-humidity.png new file mode 100644 index 0000000000..adf95ef0c7 Binary files /dev/null and b/esphomeyaml-edge/images/temperature-humidity.png differ diff --git a/esphomeyaml-edge/logo.png b/esphomeyaml-edge/logo.png new file mode 100644 index 0000000000..cd37247307 Binary files /dev/null and b/esphomeyaml-edge/logo.png differ diff --git a/esphomeyaml-edge/rootfs/etc/cont-init.d/10-requirements.sh b/esphomeyaml-edge/rootfs/etc/cont-init.d/10-requirements.sh new file mode 100755 index 0000000000..de37e10334 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/cont-init.d/10-requirements.sh @@ -0,0 +1,35 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# This files check if all user configuration requirements are met +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +# Check SSL requirements, if enabled +if hass.config.true 'ssl'; then + if ! hass.config.has_value 'certfile'; then + hass.die 'SSL is enabled, but no certfile was specified.' + fi + + if ! hass.config.has_value 'keyfile'; then + hass.die 'SSL is enabled, but no keyfile was specified' + fi + + if ! hass.file_exists "/ssl/$(hass.config.get 'certfile')"; then + if ! hass.file_exists "/ssl/$(hass.config.get 'keyfile')"; then + # Both files are missing, let's print a friendlier error message + text="You enabled encrypted connections using the \"ssl\": true option. + However, the SSL files \"$(hass.config.get 'certfile')\" and \"$(hass.config.get 'keyfile')\" + were not found. If you're using Hass.io on your local network and don't want + to encrypt connections to the esphomeyaml dashboard, you can manually disable + SSL by setting \"ssl\" to false." + hass.die "${text}" + fi + hass.die 'The configured certfile is not found' + fi + + if ! hass.file_exists "/ssl/$(hass.config.get 'keyfile')"; then + hass.die 'The configured keyfile is not found' + fi +fi diff --git a/esphomeyaml-edge/rootfs/etc/cont-init.d/20-nginx.sh b/esphomeyaml-edge/rootfs/etc/cont-init.d/20-nginx.sh new file mode 100755 index 0000000000..281463b4dc --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/cont-init.d/20-nginx.sh @@ -0,0 +1,28 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# Configures NGINX for use with esphomeyaml +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +declare certfile +declare keyfile +declare port + +mkdir -p /var/log/nginx + +# Enable SSL +if hass.config.true 'ssl'; then + rm /etc/nginx/nginx.conf + mv /etc/nginx/nginx-ssl.conf /etc/nginx/nginx.conf + + certfile=$(hass.config.get 'certfile') + keyfile=$(hass.config.get 'keyfile') + + sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/nginx.conf + sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/nginx.conf +fi + +port=$(hass.config.get 'port') +sed -i "s/%%port%%/${port}/g" /etc/nginx/nginx.conf diff --git a/esphomeyaml-edge/rootfs/etc/cont-init.d/30-esphomeyaml.sh b/esphomeyaml-edge/rootfs/etc/cont-init.d/30-esphomeyaml.sh new file mode 100644 index 0000000000..0131b09b74 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/cont-init.d/30-esphomeyaml.sh @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# This files installs the user esphomeyaml version if specified +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +declare esphomeyaml_version + +if hass.config.has_value 'esphomeyaml_version'; then + esphomeyaml_version=$(hass.config.get 'esphomeyaml_version') + pip2 install --no-cache-dir --no-binary :all: "https://github.com/OttoWinter/esphomeyaml/archive/${esphomeyaml_version}.zip" +fi diff --git a/esphomeyaml-edge/rootfs/etc/nginx/nginx-ssl.conf b/esphomeyaml-edge/rootfs/etc/nginx/nginx-ssl.conf new file mode 100755 index 0000000000..1d761595a9 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/nginx/nginx-ssl.conf @@ -0,0 +1,62 @@ +worker_processes 1; +pid /var/run/nginx.pid; +error_log stderr; + +events { + worker_connections 1024; +} + +http { + access_log stdout; + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + upstream esphomeyaml { + ip_hash; + server unix:/var/run/esphomeyaml.sock; + } + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + server { + server_name hassio.local; + listen %%port%% default_server ssl; + root /dev/null; + + ssl_certificate /ssl/%%certfile%%; + ssl_certificate_key /ssl/%%keyfile%%; + ssl_protocols TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA; + ssl_ecdh_curve secp384r1; + ssl_session_timeout 10m; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; + ssl_stapling on; + ssl_stapling_verify on; + + # Redirect http requests to https on the same port. + # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/ + error_page 497 https://$http_host$request_uri; + + location / { + proxy_redirect off; + proxy_pass http://esphomeyaml; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Authorization ""; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + } + } +} diff --git a/esphomeyaml-edge/rootfs/etc/nginx/nginx.conf b/esphomeyaml-edge/rootfs/etc/nginx/nginx.conf new file mode 100755 index 0000000000..596fc3e604 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/nginx/nginx.conf @@ -0,0 +1,46 @@ +worker_processes 1; +pid /var/run/nginx.pid; +error_log stderr; + +events { + worker_connections 1024; +} + +http { + access_log stdout; + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + upstream esphomeyaml { + ip_hash; + server unix:/var/run/esphomeyaml.sock; + } + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + server { + server_name hassio.local; + listen %%port%% default_server; + root /dev/null; + + location / { + proxy_redirect off; + proxy_pass http://esphomeyaml; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Authorization ""; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + } + } +} diff --git a/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/finish b/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/finish new file mode 100755 index 0000000000..4d0e9a35ff --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/finish @@ -0,0 +1,9 @@ +#!/usr/bin/execlineb -S0 +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# Take down the S6 supervision tree when esphomeyaml fails +# ============================================================================== +if -n { s6-test $# -ne 0 } +if -n { s6-test ${1} -eq 256 } + +s6-svscanctl -t /var/run/s6/services diff --git a/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/run b/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/run new file mode 100755 index 0000000000..8f71cd8095 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/run @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# Runs the esphomeyaml dashboard +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +if hass.config.true 'leave_front_door_open'; then + export DISABLE_HA_AUTHENTICATION=true +fi + +hass.log.info "Starting esphomeyaml dashboard..." +exec esphomeyaml /config/esphomeyaml dashboard --socket /var/run/esphomeyaml.sock --hassio diff --git a/esphomeyaml-edge/rootfs/etc/services.d/nginx/finish b/esphomeyaml-edge/rootfs/etc/services.d/nginx/finish new file mode 100755 index 0000000000..e0c2ac25ef --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/services.d/nginx/finish @@ -0,0 +1,9 @@ +#!/usr/bin/execlineb -S0 +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# Take down the S6 supervision tree when NGINX fails +# ============================================================================== +if -n { s6-test $# -ne 0 } +if -n { s6-test ${1} -eq 256 } + +s6-svscanctl -t /var/run/s6/services diff --git a/esphomeyaml-edge/rootfs/etc/services.d/nginx/run b/esphomeyaml-edge/rootfs/etc/services.d/nginx/run new file mode 100755 index 0000000000..51c18ab9a9 --- /dev/null +++ b/esphomeyaml-edge/rootfs/etc/services.d/nginx/run @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: esphomeyaml +# Runs the NGINX proxy +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +hass.log.info "Starting NGINX..." +exec nginx -g "daemon off;" diff --git a/esphomeyaml-edge/rootfs/opt/pio/platformio.ini b/esphomeyaml-edge/rootfs/opt/pio/platformio.ini new file mode 100644 index 0000000000..7f6ab6851d --- /dev/null +++ b/esphomeyaml-edge/rootfs/opt/pio/platformio.ini @@ -0,0 +1,12 @@ +; This file allows the docker build file to install the required platformio +; platforms + +[env:espressif8266] +platform = espressif8266 +board = nodemcuv2 +framework = arduino + +[env:espressif32] +platform = espressif32 +board = nodemcu-32s +framework = arduino diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index af6c3223e0..61eea83d40 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -2,25 +2,29 @@ from __future__ import print_function import argparse from collections import OrderedDict +from datetime import datetime import logging import os import random import sys -from datetime import datetime -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, statement, relative_path -from esphomeyaml.util import safe_print, run_external_command +from esphomeyaml import const, core_config, mqtt, platformio_api, wizard, writer, yaml_util +from esphomeyaml.api.client import run_logs +from esphomeyaml.config import get_component, iter_components, read_config, strip_default_ids +from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_LOGGER, CONF_USE_CUSTOM_CODE, \ + CONF_BROKER +from esphomeyaml.core import CORE, EsphomeyamlError +from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement +from esphomeyaml.helpers import color, indent +from esphomeyaml.py_compat import safe_input, text_type, IS_PY2 +from esphomeyaml.storage_json import StorageJSON, esphomeyaml_storage_path, \ + start_update_check_thread, storage_path +from esphomeyaml.util import run_external_command, safe_print _LOGGER = logging.getLogger(__name__) -PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c'] +PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ethernet', 'ota', 'mqtt', 'web_server', 'api', + 'i2c'] def get_serial_ports(): @@ -32,37 +36,64 @@ def get_serial_ports(): continue if "VID:PID" in info: result.append((port, desc)) + result.sort(key=lambda x: x[0]) return result -def choose_serial_port(config): - result = get_serial_ports() +def choose_prompt(options): + if not options: + raise ValueError + + if len(options) == 1: + return options[0][1] + + safe_print(u"Found multiple options, please choose one:") + for i, (desc, _) in enumerate(options): + safe_print(u" [{}] {}".format(i + 1, desc)) - if not result: - return 'OTA' - safe_print(u"Found multiple serial port options, please choose one:") - for i, (res, desc) in enumerate(result): - safe_print(u" [{}] {} ({})".format(i, res, desc)) - safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config))) - safe_print() while True: - opt = raw_input('(number): ') - if opt in result: - opt = result.index(opt) + opt = safe_input('(number): ') + if opt in options: + opt = options.index(opt) break try: opt = int(opt) - if opt < 0 or opt > len(result): + if opt < 1 or opt > len(options): raise ValueError break except ValueError: safe_print(color('red', u"Invalid option: '{}'".format(opt))) - if opt == len(result): - return 'OTA' - return result[opt][0] + return options[opt - 1][1] -def run_miniterm(config, port, escape=False): +def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api): + options = [] + for res, desc in get_serial_ports(): + options.append((u"{} ({})".format(res, desc), res)) + if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config): + options.append((u"Over The Air ({})".format(CORE.address), CORE.address)) + if default == 'OTA': + return CORE.address + if show_mqtt and 'mqtt' in CORE.config: + options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT')) + if default == 'OTA': + return 'MQTT' + if default is not None: + return default + if check_default is not None and check_default in [opt[1] for opt in options]: + return check_default + return choose_prompt(options) + + +def get_port_type(port): + if port.startswith('/') or port.startswith('COM'): + return 'SERIAL' + if port == 'MQTT': + return 'MQTT' + return 'NETWORK' + + +def run_miniterm(config, port): import serial if CONF_LOGGER not in config: _LOGGER.info("Logger is not enabled. Not starting UART logs.") @@ -80,11 +111,13 @@ def run_miniterm(config, port, escape=False): except serial.SerialException: _LOGGER.error("Serial port closed!") return - line = raw.replace('\r', '').replace('\n', '') + if IS_PY2: + line = raw.replace('\r', '').replace('\n', '') + else: + line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', + 'backslashreplace') time = datetime.now().time().strftime('[%H:%M:%S]') message = time + line - if escape: - message = message.replace('\033', '\\033') safe_print(message) backtrace_state = platformio_api.process_stacktrace( @@ -94,91 +127,65 @@ def run_miniterm(config, port, escape=False): def write_cpp(config): _LOGGER.info("Generating C++ source...") - add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml') + CORE.add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml') for domain in PRE_INITIALIZE: if domain == CONF_ESPHOMEYAML or domain not in config: continue - add_job(get_component(domain).to_code, config[domain], domain=domain) + CORE.add_job(get_component(domain).to_code, config[domain], domain=domain) for domain, component, conf in iter_components(config): if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'): continue - add_job(component.to_code, conf, domain=domain) + CORE.add_job(component.to_code, conf, domain=domain) - flush_tasks() + CORE.flush_tasks() add(RawStatement('')) add(RawStatement('')) all_code = [] - for exp in _EXPRESSIONS: + for exp in CORE.expressions: if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: if isinstance(exp, Expression) and not exp.required: continue - if isinstance(exp, AssignmentExpression) and not exp.obj.required: - if not exp.has_side_effects(): - continue - exp = exp.rhs - all_code.append(unicode(statement(exp))) + all_code.append(text_type(statement(exp))) - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - writer.write_platformio_project(config, build_path) + writer.write_platformio_project() code_s = indent('\n'.join(line.rstrip() for line in all_code)) - cpp_path = os.path.join(build_path, 'src', 'main.cpp') - writer.write_cpp(code_s, cpp_path) + writer.write_cpp(code_s) return 0 def compile_program(args, config): _LOGGER.info("Compiling app...") - return platformio_api.run_compile(config, args.verbose) - - -def get_upload_host(config): - if CONF_MANUAL_IP in config[CONF_WIFI]: - host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP]) - elif CONF_HOSTNAME in config[CONF_WIFI]: - host = config[CONF_WIFI][CONF_HOSTNAME] + config[CONF_WIFI][CONF_DOMAIN] - else: - host = config[CONF_ESPHOMEYAML][CONF_NAME] + config[CONF_WIFI][CONF_DOMAIN] - return host + update_check = not os.getenv('ESPHOMEYAML_NO_UPDATE_CHECK', '') + if update_check: + thread = start_update_check_thread(esphomeyaml_storage_path(CORE.config_dir)) + rc = platformio_api.run_compile(config, args.verbose) + if update_check: + thread.join() + return rc def upload_using_esptool(config, port): import esptool - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') + path = os.path.join(CORE.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_external_command(esptool._main, *cmd) -def upload_program(config, args, port): - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - +def upload_program(config, args, host): # if upload is to a serial port use platformio, otherwise assume ota - serial_port = port.startswith('/') or port.startswith('COM') - if port != 'OTA' and serial_port: - if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: - return upload_using_esptool(config, port) - 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!") - return -1 - - # If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf - # hostname. This is to support use cases where zeroconf (hostname.local) does not work. - if port != 'OTA': - host = port - else: - host = get_upload_host(config) + if get_port_type(host) == 'SERIAL': + if CORE.is_esp8266: + return upload_using_esptool(config, host) + return platformio_api.run_upload(config, args.verbose, host) from esphomeyaml.components import ota from esphomeyaml import espota2 - bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') if args.host_port is not None: host_port = args.host_port else: @@ -188,20 +195,31 @@ def upload_program(config, args, port): remote_port = ota.get_port(config) password = ota.get_auth(config) - res = espota2.run_ota(host, remote_port, password, bin_file) + storage = StorageJSON.load(storage_path()) + res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin) if res == 0: + if storage is not None and storage.use_legacy_ota: + storage.use_legacy_ota = False + storage.save(storage_path()) return res - _LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...") - return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file) + if storage is not None and not storage.use_legacy_ota: + return res + + _LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...") + return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, + CORE.firmware_bin) -def show_logs(config, args, port, escape=False): - serial_port = port.startswith('/') or port.startswith('COM') - if port != 'OTA' and serial_port: - run_miniterm(config, port, escape=escape) +def show_logs(config, args, port): + if get_port_type(port) == 'SERIAL': + run_miniterm(config, port) return 0 - return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id, - escape=escape) + if get_port_type(port) == 'NETWORK': + return run_logs(config, port) + if get_port_type(port) == 'MQTT': + return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) + + raise ValueError def clean_mqtt(config, args): @@ -239,26 +257,8 @@ def command_wizard(args): return wizard.wizard(args.configuration) -def strip_default_ids(config): - value = config - if isinstance(config, list): - value = type(config)() - for x in config: - if isinstance(x, core.ID) and not x.is_manual: - continue - value.append(strip_default_ids(x)) - return value - elif isinstance(config, dict): - value = type(config)() - for k, v in config.iteritems(): - if isinstance(v, core.ID) and not v.is_manual: - continue - value[k] = strip_default_ids(v) - return value - return value - - def command_config(args, config): + _LOGGER.info("Configuration is valid!") if not args.verbose: config = strip_default_ids(config) safe_print(yaml_util.dump(config)) @@ -280,7 +280,8 @@ def command_compile(args, config): def command_upload(args, config): - port = args.upload_port or choose_serial_port(config) + port = choose_upload_log_host(default=args.upload_port, check_default=None, + show_ota=True, show_mqtt=False, show_api=False) exit_code = upload_program(config, args, port) if exit_code != 0: return exit_code @@ -289,8 +290,9 @@ def command_upload(args, config): def command_logs(args, config): - port = args.serial_port or choose_serial_port(config) - return show_logs(config, args, port, escape=args.escape) + port = choose_upload_log_host(default=args.serial_port, check_default=None, + show_ota=False, show_mqtt=True, show_api=True) + return show_logs(config, args, port) def command_run(args, config): @@ -301,14 +303,17 @@ def command_run(args, config): if exit_code != 0: return exit_code _LOGGER.info(u"Successfully compiled program.") - port = args.upload_port or choose_serial_port(config) + port = choose_upload_log_host(default=args.upload_port, check_default=None, + show_ota=True, show_mqtt=False, show_api=True) exit_code = upload_program(config, args, port) if exit_code != 0: return exit_code _LOGGER.info(u"Successfully uploaded program.") if args.no_logs: return 0 - return show_logs(config, args, port, escape=args.escape) + port = choose_upload_log_host(default=args.upload_port, check_default=port, + show_ota=False, show_mqtt=True, show_api=True) + return show_logs(config, args, port) def command_clean_mqtt(args, config): @@ -325,9 +330,8 @@ def command_version(args): def command_clean(args, config): - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) try: - writer.clean_build(build_path) + writer.clean_build() except OSError as err: _LOGGER.error("Error deleting build files: %s", err) return 1 @@ -386,6 +390,8 @@ def parse_args(argv): parser = argparse.ArgumentParser(prog='esphomeyaml') parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.", action='store_true') + parser.add_argument('--dashboard', help="Internal flag to set if the command is run from the " + "dashboard.", action='store_true') parser.add_argument('configuration', help='Your YAML configuration file.') subparsers = parser.add_subparsers(help='Commands', dest='command') @@ -403,9 +409,6 @@ def parse_args(argv): parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. " "For example /dev/cu.SLAB_USBtoUART.") parser_upload.add_argument('--host-port', help="Specify the host port.", type=int) - parser_upload.add_argument('--use-esptoolpy', - help="Use esptool.py for the uploading (only for ESP8266)", - action='store_true') parser_logs = subparsers.add_parser('logs', help='Validate the configuration ' 'and show all MQTT logs.') @@ -415,8 +418,6 @@ def parse_args(argv): parser_logs.add_argument('--client-id', help='Manually set the client id.') parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use" "For example /dev/cu.SLAB_USBtoUART.") - parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard", - action='store_true') parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, ' 'upload it, and start MQTT logs.') @@ -429,11 +430,6 @@ def parse_args(argv): parser_run.add_argument('--username', help='Manually set the MQTT username for logs.') parser_run.add_argument('--password', help='Manually set the MQTT password for logs.') parser_run.add_argument('--client-id', help='Manually set the client id for logs.') - parser_run.add_argument('--escape', help="Escape ANSI color codes for running in dashboard", - action='store_true') - parser_run.add_argument('--use-esptoolpy', - help="Use esptool.py for the uploading (only for ESP8266)", - action='store_true') parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from " "retain messages.") @@ -453,39 +449,49 @@ def parse_args(argv): dashboard = subparsers.add_parser('dashboard', help="Create a simple web server for a dashboard.") - dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int, - default=6052) + dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.", + type=int, default=6052) dashboard.add_argument("--password", help="The optional password to require for all requests.", type=str, default='') dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", action='store_true') + dashboard.add_argument("--hassio", + help="Internal flag used to tell esphomeyaml is started as a Hass.io " + "add-on.", + action="store_true") + dashboard.add_argument("--socket", + help="Make the dashboard serve under a unix socket", type=str) - subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added" - "to Home Assistant when not using MQTT discovery.") + 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:]) def run_esphomeyaml(argv): args = parse_args(argv) + CORE.dashboard = args.dashboard + setup_log(args.verbose) if args.command in PRE_CONFIG_ACTIONS: try: return PRE_CONFIG_ACTIONS[args.command](args) - except ESPHomeYAMLError as e: + except EsphomeyamlError as e: _LOGGER.error(e) return 1 - core.CONFIG_PATH = args.configuration + CORE.config_path = args.configuration - config = read_config(core.CONFIG_PATH) + config = read_config(args.verbose) if config is None: return 1 + CORE.config = config if args.command in POST_CONFIG_ACTIONS: try: return POST_CONFIG_ACTIONS[args.command](args, config) - except ESPHomeYAMLError as e: + except EsphomeyamlError as e: _LOGGER.error(e) return 1 safe_print(u"Unknown command {}".format(args.command)) @@ -495,7 +501,7 @@ def run_esphomeyaml(argv): def main(): try: return run_esphomeyaml(sys.argv) - except ESPHomeYAMLError as e: + except EsphomeyamlError as e: _LOGGER.error(e) return 1 except KeyboardInterrupt: diff --git a/esphomeyaml/api/__init__.py b/esphomeyaml/api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphomeyaml/api/api.proto b/esphomeyaml/api/api.proto new file mode 100644 index 0000000000..bde079be16 --- /dev/null +++ b/esphomeyaml/api/api.proto @@ -0,0 +1,330 @@ +syntax = "proto3"; + +// The Home Assistant protocol is structured as a simple +// TCP socket with short binary messages encoded in the protocol buffers format +// First, a message in this protocol has a specific format: +// * VarInt denoting the size of the message object. (type is not part of this) +// * VarInt denoting the type of message. +// * The message object encoded as a ProtoBuf message + +// The connection is established in 4 steps: +// * First, the client connects to the server and sends a "Hello Request" identifying itself +// * The server responds with a "Hello Response" and selects the protocol version +// * After receiving this message, the client attempts to authenticate itself using +// the password and a "Connect Request" +// * The server responds with a "Connect Response" and notifies of invalid password. +// If anything in this initial process fails, the connection must immediately closed +// by both sides and _no_ disconnection message is to be sent. + +// Message sent at the beginning of each connection +// Can only be sent by the client and only at the beginning of the connection +message HelloRequest { + // Description of client (like User Agent) + // For example "Home Assistant" + // Not strictly necessary to send but nice for debugging + // purposes. + string client_info = 1; +} + +// Confirmation of successful connection request. +// Can only be sent by the server and only at the beginning of the connection +message HelloResponse { + // The version of the API to use. The _client_ (for example Home Assistant) needs to check + // for compatibility and if necessary adopt to an older API. + // Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_ + // Minor is for breaking changes in individual messages - a mismatch will lead to a warning message + uint32 api_version_major = 1; + uint32 api_version_minor = 2; + + // A string identifying the server (ESP); like client info this may be empty + // and only exists for debugging/logging purposes. + // For example "ESPHome v1.10.0 on ESP8266" + string server_info = 3; +} + +// Message sent at the beginning of each connection to authenticate the client +// Can only be sent by the client and only at the beginning of the connection +message ConnectRequest { + // The password to log in with + string password = 1; +} + +// Confirmation of successful connection. After this the connection is available for all traffic. +// Can only be sent by the server and only at the beginning of the connection +message ConnectResponse { + bool invalid_password = 1; +} + +// Request to close the connection. +// Can be sent by both the client and server +message DisconnectRequest { + // Do not close the connection before the acknowledgement arrives +} + +message DisconnectResponse { + // Empty - Both parties are required to close the connection after this + // message has been received. +} + +message PingRequest { + // Empty +} + +message PingResponse { + // Empty +} + +message DeviceInfoRequest { + // Empty +} + +message DeviceInfoResponse { + bool uses_password = 1; + + // The name of the node, given by "App.set_name()" + string name = 2; + + // The mac address of the device. For example "AC:BC:32:89:0E:A9" + string mac_address = 3; + + // A string describing the ESPHome version. For example "1.10.0" + string esphome_core_version = 4; + + // A string describing the date of compilation, this is generated by the compiler + // and therefore may not be in the same format all the time. + // If the user isn't using esphomeyaml, this will also not be set. + string compilation_time = 5; + + // The model of the board. For example NodeMCU + string model = 6; + + bool has_deep_sleep = 7; +} + +message ListEntitiesRequest { + // Empty +} + +message ListEntitiesBinarySensorResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string device_class = 5; + bool is_status_binary_sensor = 6; +} +message ListEntitiesCoverResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + bool is_optimistic = 5; +} +message ListEntitiesFanResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + bool supports_oscillation = 5; + bool supports_speed = 6; +} +message ListEntitiesLightResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + bool supports_brightness = 5; + bool supports_rgb = 6; + bool supports_white_value = 7; + bool supports_color_temperature = 8; + float min_mireds = 9; + float max_mireds = 10; + repeated string effects = 11; +} +message ListEntitiesSensorResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; + string unit_of_measurement = 6; + int32 accuracy_decimals = 7; +} +message ListEntitiesSwitchResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; + bool optimistic = 6; +} +message ListEntitiesTextSensorResponse { + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; +} +message ListEntitiesDoneResponse { + // Empty +} + +message SubscribeStatesRequest { + // Empty +} +message BinarySensorStateResponse { + fixed32 key = 1; + bool state = 2; +} +message CoverStateResponse { + fixed32 key = 1; + enum CoverState { + OPEN = 0; + CLOSED = 1; + } + CoverState state = 2; +} +enum FanSpeed { + LOW = 0; + MEDIUM = 1; + HIGH = 2; +} +message FanStateResponse { + fixed32 key = 1; + bool state = 2; + bool oscillating = 3; + FanSpeed speed = 4; +} +message LightStateResponse { + fixed32 key = 1; + bool state = 2; + float brightness = 3; + float red = 4; + float green = 5; + float blue = 6; + float white = 7; + float color_temperature = 8; + string effect = 9; +} +message SensorStateResponse { + fixed32 key = 1; + float state = 2; +} +message SwitchStateResponse { + fixed32 key = 1; + bool state = 2; +} +message TextSensorStateResponse { + fixed32 key = 1; + string state = 2; +} + +message CoverCommandRequest { + fixed32 key = 1; + enum CoverCommand { + OPEN = 0; + CLOSE = 1; + STOP = 2; + } + bool has_state = 2; + CoverCommand command = 3; +} +message FanCommandRequest { + fixed32 key = 1; + bool has_state = 2; + bool state = 3; + bool has_speed = 4; + FanSpeed speed = 5; + bool has_oscillating = 6; + bool oscillating = 7; +} +message LightCommandRequest { + fixed32 key = 1; + bool has_state = 2; + bool state = 3; + bool has_brightness = 4; + float brightness = 5; + bool has_rgb = 6; + float red = 7; + float green = 8; + float blue = 9; + bool has_white = 10; + float white = 11; + bool has_color_temperature = 12; + float color_temperature = 13; + bool has_transition_length = 14; + uint32 transition_length = 15; + bool has_flash_length = 16; + uint32 flash_length = 17; + bool has_effect = 18; + string effect = 19; +} +message SwitchCommandRequest { + fixed32 key = 1; + bool state = 2; +} + +enum LogLevel { + NONE = 0; + ERROR = 1; + WARN = 2; + INFO = 3; + DEBUG = 4; + VERBOSE = 5; + VERY_VERBOSE = 6; +} + +message SubscribeLogsRequest { + LogLevel level = 1; + bool dump_config = 2; +} + +message SubscribeLogsResponse { + LogLevel level = 1; + string tag = 2; + string message = 3; + bool send_failed = 4; +} + +message SubscribeServiceCallsRequest { + +} + +message ServiceCallResponse { + string service = 1; + map data = 2; + map data_template = 3; + map variables = 4; +} + +// 1. Client sends SubscribeHomeAssistantStatesRequest +// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async) +// 3. Client sends HomeAssistantStateResponse for state changes. +message SubscribeHomeAssistantStatesRequest { + +} + +message SubscribeHomeAssistantStateResponse { + string entity_id = 1; +} + +message HomeAssistantStateResponse { + string entity_id = 1; + string state = 2; +} + +message GetTimeRequest { + +} + +message GetTimeResponse { + fixed32 epoch_seconds = 1; +} + diff --git a/esphomeyaml/api/api_pb2.py b/esphomeyaml/api/api_pb2.py new file mode 100644 index 0000000000..2212db8339 --- /dev/null +++ b/esphomeyaml/api/api_pb2.py @@ -0,0 +1,2484 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: api.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='api.proto', + package='', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\tapi.proto\"#\n\x0cHelloRequest\x12\x13\n\x0b\x63lient_info\x18\x01 \x01(\t\"Z\n\rHelloResponse\x12\x19\n\x11\x61pi_version_major\x18\x01 \x01(\r\x12\x19\n\x11\x61pi_version_minor\x18\x02 \x01(\r\x12\x13\n\x0bserver_info\x18\x03 \x01(\t\"\"\n\x0e\x43onnectRequest\x12\x10\n\x08password\x18\x01 \x01(\t\"+\n\x0f\x43onnectResponse\x12\x18\n\x10invalid_password\x18\x01 \x01(\x08\"\x13\n\x11\x44isconnectRequest\"\x14\n\x12\x44isconnectResponse\"\r\n\x0bPingRequest\"\x0e\n\x0cPingResponse\"\x13\n\x11\x44\x65viceInfoRequest\"\xad\x01\n\x12\x44\x65viceInfoResponse\x12\x15\n\ruses_password\x18\x01 \x01(\x08\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0bmac_address\x18\x03 \x01(\t\x12\x1c\n\x14\x65sphome_core_version\x18\x04 \x01(\t\x12\x18\n\x10\x63ompilation_time\x18\x05 \x01(\t\x12\r\n\x05model\x18\x06 \x01(\t\x12\x16\n\x0ehas_deep_sleep\x18\x07 \x01(\x08\"\x15\n\x13ListEntitiesRequest\"\x9a\x01\n ListEntitiesBinarySensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65vice_class\x18\x05 \x01(\t\x12\x1f\n\x17is_status_binary_sensor\x18\x06 \x01(\x08\"s\n\x19ListEntitiesCoverResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x15\n\ris_optimistic\x18\x05 \x01(\x08\"\x90\x01\n\x17ListEntitiesFanResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1c\n\x14supports_oscillation\x18\x05 \x01(\x08\x12\x16\n\x0esupports_speed\x18\x06 \x01(\x08\"\x8a\x02\n\x19ListEntitiesLightResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1b\n\x13supports_brightness\x18\x05 \x01(\x08\x12\x14\n\x0csupports_rgb\x18\x06 \x01(\x08\x12\x1c\n\x14supports_white_value\x18\x07 \x01(\x08\x12\"\n\x1asupports_color_temperature\x18\x08 \x01(\x08\x12\x12\n\nmin_mireds\x18\t \x01(\x02\x12\x12\n\nmax_mireds\x18\n \x01(\x02\x12\x0f\n\x07\x65\x66\x66\x65\x63ts\x18\x0b \x03(\t\"\xa3\x01\n\x1aListEntitiesSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x1b\n\x13unit_of_measurement\x18\x06 \x01(\t\x12\x19\n\x11\x61\x63\x63uracy_decimals\x18\x07 \x01(\x05\"\x7f\n\x1aListEntitiesSwitchResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x12\n\noptimistic\x18\x06 \x01(\x08\"o\n\x1eListEntitiesTextSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\"\x1a\n\x18ListEntitiesDoneResponse\"\x18\n\x16SubscribeStatesRequest\"7\n\x19\x42inarySensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"t\n\x12\x43overStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12-\n\x05state\x18\x02 \x01(\x0e\x32\x1e.CoverStateResponse.CoverState\"\"\n\nCoverState\x12\x08\n\x04OPEN\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\"]\n\x10\x46\x61nStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x13\n\x0boscillating\x18\x03 \x01(\x08\x12\x18\n\x05speed\x18\x04 \x01(\x0e\x32\t.FanSpeed\"\xa8\x01\n\x12LightStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x12\n\nbrightness\x18\x03 \x01(\x02\x12\x0b\n\x03red\x18\x04 \x01(\x02\x12\r\n\x05green\x18\x05 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x06 \x01(\x02\x12\r\n\x05white\x18\x07 \x01(\x02\x12\x19\n\x11\x63olor_temperature\x18\x08 \x01(\x02\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\t \x01(\t\"1\n\x13SensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x02\"1\n\x13SwitchStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"5\n\x17TextSensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\t\"\x98\x01\n\x13\x43overCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\x32\n\x07\x63ommand\x18\x03 \x01(\x0e\x32!.CoverCommandRequest.CoverCommand\"-\n\x0c\x43overCommand\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04STOP\x10\x02\"\x9d\x01\n\x11\x46\x61nCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x11\n\thas_speed\x18\x04 \x01(\x08\x12\x18\n\x05speed\x18\x05 \x01(\x0e\x32\t.FanSpeed\x12\x17\n\x0fhas_oscillating\x18\x06 \x01(\x08\x12\x13\n\x0boscillating\x18\x07 \x01(\x08\"\x95\x03\n\x13LightCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x16\n\x0ehas_brightness\x18\x04 \x01(\x08\x12\x12\n\nbrightness\x18\x05 \x01(\x02\x12\x0f\n\x07has_rgb\x18\x06 \x01(\x08\x12\x0b\n\x03red\x18\x07 \x01(\x02\x12\r\n\x05green\x18\x08 \x01(\x02\x12\x0c\n\x04\x62lue\x18\t \x01(\x02\x12\x11\n\thas_white\x18\n \x01(\x08\x12\r\n\x05white\x18\x0b \x01(\x02\x12\x1d\n\x15has_color_temperature\x18\x0c \x01(\x08\x12\x19\n\x11\x63olor_temperature\x18\r \x01(\x02\x12\x1d\n\x15has_transition_length\x18\x0e \x01(\x08\x12\x19\n\x11transition_length\x18\x0f \x01(\r\x12\x18\n\x10has_flash_length\x18\x10 \x01(\x08\x12\x14\n\x0c\x66lash_length\x18\x11 \x01(\r\x12\x12\n\nhas_effect\x18\x12 \x01(\x08\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\x13 \x01(\t\"2\n\x14SwitchCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"E\n\x14SubscribeLogsRequest\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x13\n\x0b\x64ump_config\x18\x02 \x01(\x08\"d\n\x15SubscribeLogsResponse\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x13\n\x0bsend_failed\x18\x04 \x01(\x08\"\x1e\n\x1cSubscribeServiceCallsRequest\"\xdf\x02\n\x13ServiceCallResponse\x12\x0f\n\x07service\x18\x01 \x01(\t\x12,\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x1e.ServiceCallResponse.DataEntry\x12=\n\rdata_template\x18\x03 \x03(\x0b\x32&.ServiceCallResponse.DataTemplateEntry\x12\x36\n\tvariables\x18\x04 \x03(\x0b\x32#.ServiceCallResponse.VariablesEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11\x44\x61taTemplateEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"%\n#SubscribeHomeAssistantStatesRequest\"8\n#SubscribeHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\">\n\x1aHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"\x10\n\x0eGetTimeRequest\"(\n\x0fGetTimeResponse\x12\x15\n\repoch_seconds\x18\x01 \x01(\x07*)\n\x08\x46\x61nSpeed\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02*]\n\x08LogLevel\x12\x08\n\x04NONE\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x08\n\x04WARN\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\x0b\n\x07VERBOSE\x10\x05\x12\x10\n\x0cVERY_VERBOSE\x10\x06\x62\x06proto3') +) + +_FANSPEED = _descriptor.EnumDescriptor( + name='FanSpeed', + full_name='FanSpeed', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='LOW', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='MEDIUM', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='HIGH', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=3822, + serialized_end=3863, +) +_sym_db.RegisterEnumDescriptor(_FANSPEED) + +FanSpeed = enum_type_wrapper.EnumTypeWrapper(_FANSPEED) +_LOGLEVEL = _descriptor.EnumDescriptor( + name='LogLevel', + full_name='LogLevel', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ERROR', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='WARN', index=2, number=2, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='INFO', index=3, number=3, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='DEBUG', index=4, number=4, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='VERBOSE', index=5, number=5, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='VERY_VERBOSE', index=6, number=6, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=3865, + serialized_end=3958, +) +_sym_db.RegisterEnumDescriptor(_LOGLEVEL) + +LogLevel = enum_type_wrapper.EnumTypeWrapper(_LOGLEVEL) +LOW = 0 +MEDIUM = 1 +HIGH = 2 +NONE = 0 +ERROR = 1 +WARN = 2 +INFO = 3 +DEBUG = 4 +VERBOSE = 5 +VERY_VERBOSE = 6 + + +_COVERSTATERESPONSE_COVERSTATE = _descriptor.EnumDescriptor( + name='CoverState', + full_name='CoverStateResponse.CoverState', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='OPEN', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='CLOSED', index=1, number=1, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=1808, + serialized_end=1842, +) +_sym_db.RegisterEnumDescriptor(_COVERSTATERESPONSE_COVERSTATE) + +_COVERCOMMANDREQUEST_COVERCOMMAND = _descriptor.EnumDescriptor( + name='CoverCommand', + full_name='CoverCommandRequest.CoverCommand', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='OPEN', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='CLOSE', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='STOP', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=2375, + serialized_end=2420, +) +_sym_db.RegisterEnumDescriptor(_COVERCOMMANDREQUEST_COVERCOMMAND) + + +_HELLOREQUEST = _descriptor.Descriptor( + name='HelloRequest', + full_name='HelloRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='client_info', full_name='HelloRequest.client_info', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=13, + serialized_end=48, +) + + +_HELLORESPONSE = _descriptor.Descriptor( + name='HelloResponse', + full_name='HelloResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='api_version_major', full_name='HelloResponse.api_version_major', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='api_version_minor', full_name='HelloResponse.api_version_minor', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_info', full_name='HelloResponse.server_info', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=50, + serialized_end=140, +) + + +_CONNECTREQUEST = _descriptor.Descriptor( + name='ConnectRequest', + full_name='ConnectRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='password', full_name='ConnectRequest.password', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=142, + serialized_end=176, +) + + +_CONNECTRESPONSE = _descriptor.Descriptor( + name='ConnectResponse', + full_name='ConnectResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='invalid_password', full_name='ConnectResponse.invalid_password', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=178, + serialized_end=221, +) + + +_DISCONNECTREQUEST = _descriptor.Descriptor( + name='DisconnectRequest', + full_name='DisconnectRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=223, + serialized_end=242, +) + + +_DISCONNECTRESPONSE = _descriptor.Descriptor( + name='DisconnectResponse', + full_name='DisconnectResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=244, + serialized_end=264, +) + + +_PINGREQUEST = _descriptor.Descriptor( + name='PingRequest', + full_name='PingRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=266, + serialized_end=279, +) + + +_PINGRESPONSE = _descriptor.Descriptor( + name='PingResponse', + full_name='PingResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=281, + serialized_end=295, +) + + +_DEVICEINFOREQUEST = _descriptor.Descriptor( + name='DeviceInfoRequest', + full_name='DeviceInfoRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=297, + serialized_end=316, +) + + +_DEVICEINFORESPONSE = _descriptor.Descriptor( + name='DeviceInfoResponse', + full_name='DeviceInfoResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='uses_password', full_name='DeviceInfoResponse.uses_password', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='DeviceInfoResponse.name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='mac_address', full_name='DeviceInfoResponse.mac_address', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='esphome_core_version', full_name='DeviceInfoResponse.esphome_core_version', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='compilation_time', full_name='DeviceInfoResponse.compilation_time', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='model', full_name='DeviceInfoResponse.model', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_deep_sleep', full_name='DeviceInfoResponse.has_deep_sleep', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=319, + serialized_end=492, +) + + +_LISTENTITIESREQUEST = _descriptor.Descriptor( + name='ListEntitiesRequest', + full_name='ListEntitiesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=494, + serialized_end=515, +) + + +_LISTENTITIESBINARYSENSORRESPONSE = _descriptor.Descriptor( + name='ListEntitiesBinarySensorResponse', + full_name='ListEntitiesBinarySensorResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesBinarySensorResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesBinarySensorResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesBinarySensorResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesBinarySensorResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='device_class', full_name='ListEntitiesBinarySensorResponse.device_class', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='is_status_binary_sensor', full_name='ListEntitiesBinarySensorResponse.is_status_binary_sensor', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=518, + serialized_end=672, +) + + +_LISTENTITIESCOVERRESPONSE = _descriptor.Descriptor( + name='ListEntitiesCoverResponse', + full_name='ListEntitiesCoverResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesCoverResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesCoverResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesCoverResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesCoverResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='is_optimistic', full_name='ListEntitiesCoverResponse.is_optimistic', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=674, + serialized_end=789, +) + + +_LISTENTITIESFANRESPONSE = _descriptor.Descriptor( + name='ListEntitiesFanResponse', + full_name='ListEntitiesFanResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesFanResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesFanResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesFanResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesFanResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_oscillation', full_name='ListEntitiesFanResponse.supports_oscillation', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_speed', full_name='ListEntitiesFanResponse.supports_speed', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=792, + serialized_end=936, +) + + +_LISTENTITIESLIGHTRESPONSE = _descriptor.Descriptor( + name='ListEntitiesLightResponse', + full_name='ListEntitiesLightResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesLightResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesLightResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesLightResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesLightResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_brightness', full_name='ListEntitiesLightResponse.supports_brightness', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_rgb', full_name='ListEntitiesLightResponse.supports_rgb', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_white_value', full_name='ListEntitiesLightResponse.supports_white_value', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='supports_color_temperature', full_name='ListEntitiesLightResponse.supports_color_temperature', index=7, + number=8, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='min_mireds', full_name='ListEntitiesLightResponse.min_mireds', index=8, + number=9, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='max_mireds', full_name='ListEntitiesLightResponse.max_mireds', index=9, + number=10, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='effects', full_name='ListEntitiesLightResponse.effects', index=10, + number=11, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=939, + serialized_end=1205, +) + + +_LISTENTITIESSENSORRESPONSE = _descriptor.Descriptor( + name='ListEntitiesSensorResponse', + full_name='ListEntitiesSensorResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesSensorResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesSensorResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesSensorResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesSensorResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='icon', full_name='ListEntitiesSensorResponse.icon', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unit_of_measurement', full_name='ListEntitiesSensorResponse.unit_of_measurement', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='accuracy_decimals', full_name='ListEntitiesSensorResponse.accuracy_decimals', index=6, + number=7, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1208, + serialized_end=1371, +) + + +_LISTENTITIESSWITCHRESPONSE = _descriptor.Descriptor( + name='ListEntitiesSwitchResponse', + full_name='ListEntitiesSwitchResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesSwitchResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesSwitchResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesSwitchResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesSwitchResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='icon', full_name='ListEntitiesSwitchResponse.icon', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='optimistic', full_name='ListEntitiesSwitchResponse.optimistic', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1373, + serialized_end=1500, +) + + +_LISTENTITIESTEXTSENSORRESPONSE = _descriptor.Descriptor( + name='ListEntitiesTextSensorResponse', + full_name='ListEntitiesTextSensorResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='object_id', full_name='ListEntitiesTextSensorResponse.object_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='key', full_name='ListEntitiesTextSensorResponse.key', index=1, + number=2, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='ListEntitiesTextSensorResponse.name', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unique_id', full_name='ListEntitiesTextSensorResponse.unique_id', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='icon', full_name='ListEntitiesTextSensorResponse.icon', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1502, + serialized_end=1613, +) + + +_LISTENTITIESDONERESPONSE = _descriptor.Descriptor( + name='ListEntitiesDoneResponse', + full_name='ListEntitiesDoneResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1615, + serialized_end=1641, +) + + +_SUBSCRIBESTATESREQUEST = _descriptor.Descriptor( + name='SubscribeStatesRequest', + full_name='SubscribeStatesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1643, + serialized_end=1667, +) + + +_BINARYSENSORSTATERESPONSE = _descriptor.Descriptor( + name='BinarySensorStateResponse', + full_name='BinarySensorStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='BinarySensorStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='BinarySensorStateResponse.state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1669, + serialized_end=1724, +) + + +_COVERSTATERESPONSE = _descriptor.Descriptor( + name='CoverStateResponse', + full_name='CoverStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='CoverStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='CoverStateResponse.state', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _COVERSTATERESPONSE_COVERSTATE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1726, + serialized_end=1842, +) + + +_FANSTATERESPONSE = _descriptor.Descriptor( + name='FanStateResponse', + full_name='FanStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='FanStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='FanStateResponse.state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='oscillating', full_name='FanStateResponse.oscillating', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='speed', full_name='FanStateResponse.speed', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1844, + serialized_end=1937, +) + + +_LIGHTSTATERESPONSE = _descriptor.Descriptor( + name='LightStateResponse', + full_name='LightStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='LightStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='LightStateResponse.state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='brightness', full_name='LightStateResponse.brightness', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='red', full_name='LightStateResponse.red', index=3, + number=4, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='green', full_name='LightStateResponse.green', index=4, + number=5, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='blue', full_name='LightStateResponse.blue', index=5, + number=6, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='white', full_name='LightStateResponse.white', index=6, + number=7, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='color_temperature', full_name='LightStateResponse.color_temperature', index=7, + number=8, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='effect', full_name='LightStateResponse.effect', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1940, + serialized_end=2108, +) + + +_SENSORSTATERESPONSE = _descriptor.Descriptor( + name='SensorStateResponse', + full_name='SensorStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='SensorStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='SensorStateResponse.state', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2110, + serialized_end=2159, +) + + +_SWITCHSTATERESPONSE = _descriptor.Descriptor( + name='SwitchStateResponse', + full_name='SwitchStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='SwitchStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='SwitchStateResponse.state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2161, + serialized_end=2210, +) + + +_TEXTSENSORSTATERESPONSE = _descriptor.Descriptor( + name='TextSensorStateResponse', + full_name='TextSensorStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='TextSensorStateResponse.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='TextSensorStateResponse.state', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2212, + serialized_end=2265, +) + + +_COVERCOMMANDREQUEST = _descriptor.Descriptor( + name='CoverCommandRequest', + full_name='CoverCommandRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='CoverCommandRequest.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_state', full_name='CoverCommandRequest.has_state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='command', full_name='CoverCommandRequest.command', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _COVERCOMMANDREQUEST_COVERCOMMAND, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2268, + serialized_end=2420, +) + + +_FANCOMMANDREQUEST = _descriptor.Descriptor( + name='FanCommandRequest', + full_name='FanCommandRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='FanCommandRequest.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_state', full_name='FanCommandRequest.has_state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='FanCommandRequest.state', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_speed', full_name='FanCommandRequest.has_speed', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='speed', full_name='FanCommandRequest.speed', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_oscillating', full_name='FanCommandRequest.has_oscillating', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='oscillating', full_name='FanCommandRequest.oscillating', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2423, + serialized_end=2580, +) + + +_LIGHTCOMMANDREQUEST = _descriptor.Descriptor( + name='LightCommandRequest', + full_name='LightCommandRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='LightCommandRequest.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_state', full_name='LightCommandRequest.has_state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='LightCommandRequest.state', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_brightness', full_name='LightCommandRequest.has_brightness', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='brightness', full_name='LightCommandRequest.brightness', index=4, + number=5, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_rgb', full_name='LightCommandRequest.has_rgb', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='red', full_name='LightCommandRequest.red', index=6, + number=7, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='green', full_name='LightCommandRequest.green', index=7, + number=8, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='blue', full_name='LightCommandRequest.blue', index=8, + number=9, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_white', full_name='LightCommandRequest.has_white', index=9, + number=10, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='white', full_name='LightCommandRequest.white', index=10, + number=11, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_color_temperature', full_name='LightCommandRequest.has_color_temperature', index=11, + number=12, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='color_temperature', full_name='LightCommandRequest.color_temperature', index=12, + number=13, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_transition_length', full_name='LightCommandRequest.has_transition_length', index=13, + number=14, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='transition_length', full_name='LightCommandRequest.transition_length', index=14, + number=15, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_flash_length', full_name='LightCommandRequest.has_flash_length', index=15, + number=16, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='flash_length', full_name='LightCommandRequest.flash_length', index=16, + number=17, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='has_effect', full_name='LightCommandRequest.has_effect', index=17, + number=18, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='effect', full_name='LightCommandRequest.effect', index=18, + number=19, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2583, + serialized_end=2988, +) + + +_SWITCHCOMMANDREQUEST = _descriptor.Descriptor( + name='SwitchCommandRequest', + full_name='SwitchCommandRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='SwitchCommandRequest.key', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='SwitchCommandRequest.state', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2990, + serialized_end=3040, +) + + +_SUBSCRIBELOGSREQUEST = _descriptor.Descriptor( + name='SubscribeLogsRequest', + full_name='SubscribeLogsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='level', full_name='SubscribeLogsRequest.level', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dump_config', full_name='SubscribeLogsRequest.dump_config', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3042, + serialized_end=3111, +) + + +_SUBSCRIBELOGSRESPONSE = _descriptor.Descriptor( + name='SubscribeLogsResponse', + full_name='SubscribeLogsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='level', full_name='SubscribeLogsResponse.level', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='tag', full_name='SubscribeLogsResponse.tag', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='message', full_name='SubscribeLogsResponse.message', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='send_failed', full_name='SubscribeLogsResponse.send_failed', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3113, + serialized_end=3213, +) + + +_SUBSCRIBESERVICECALLSREQUEST = _descriptor.Descriptor( + name='SubscribeServiceCallsRequest', + full_name='SubscribeServiceCallsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3215, + serialized_end=3245, +) + + +_SERVICECALLRESPONSE_DATAENTRY = _descriptor.Descriptor( + name='DataEntry', + full_name='ServiceCallResponse.DataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ServiceCallResponse.DataEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ServiceCallResponse.DataEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3453, + serialized_end=3496, +) + +_SERVICECALLRESPONSE_DATATEMPLATEENTRY = _descriptor.Descriptor( + name='DataTemplateEntry', + full_name='ServiceCallResponse.DataTemplateEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ServiceCallResponse.DataTemplateEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ServiceCallResponse.DataTemplateEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3498, + serialized_end=3549, +) + +_SERVICECALLRESPONSE_VARIABLESENTRY = _descriptor.Descriptor( + name='VariablesEntry', + full_name='ServiceCallResponse.VariablesEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ServiceCallResponse.VariablesEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ServiceCallResponse.VariablesEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3551, + serialized_end=3599, +) + +_SERVICECALLRESPONSE = _descriptor.Descriptor( + name='ServiceCallResponse', + full_name='ServiceCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='service', full_name='ServiceCallResponse.service', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='ServiceCallResponse.data', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data_template', full_name='ServiceCallResponse.data_template', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='variables', full_name='ServiceCallResponse.variables', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[_SERVICECALLRESPONSE_DATAENTRY, _SERVICECALLRESPONSE_DATATEMPLATEENTRY, _SERVICECALLRESPONSE_VARIABLESENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3248, + serialized_end=3599, +) + + +_SUBSCRIBEHOMEASSISTANTSTATESREQUEST = _descriptor.Descriptor( + name='SubscribeHomeAssistantStatesRequest', + full_name='SubscribeHomeAssistantStatesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3601, + serialized_end=3638, +) + + +_SUBSCRIBEHOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( + name='SubscribeHomeAssistantStateResponse', + full_name='SubscribeHomeAssistantStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='entity_id', full_name='SubscribeHomeAssistantStateResponse.entity_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3640, + serialized_end=3696, +) + + +_HOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( + name='HomeAssistantStateResponse', + full_name='HomeAssistantStateResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='entity_id', full_name='HomeAssistantStateResponse.entity_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='state', full_name='HomeAssistantStateResponse.state', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3698, + serialized_end=3760, +) + + +_GETTIMEREQUEST = _descriptor.Descriptor( + name='GetTimeRequest', + full_name='GetTimeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3762, + serialized_end=3778, +) + + +_GETTIMERESPONSE = _descriptor.Descriptor( + name='GetTimeResponse', + full_name='GetTimeResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='epoch_seconds', full_name='GetTimeResponse.epoch_seconds', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3780, + serialized_end=3820, +) + +_COVERSTATERESPONSE.fields_by_name['state'].enum_type = _COVERSTATERESPONSE_COVERSTATE +_COVERSTATERESPONSE_COVERSTATE.containing_type = _COVERSTATERESPONSE +_FANSTATERESPONSE.fields_by_name['speed'].enum_type = _FANSPEED +_COVERCOMMANDREQUEST.fields_by_name['command'].enum_type = _COVERCOMMANDREQUEST_COVERCOMMAND +_COVERCOMMANDREQUEST_COVERCOMMAND.containing_type = _COVERCOMMANDREQUEST +_FANCOMMANDREQUEST.fields_by_name['speed'].enum_type = _FANSPEED +_SUBSCRIBELOGSREQUEST.fields_by_name['level'].enum_type = _LOGLEVEL +_SUBSCRIBELOGSRESPONSE.fields_by_name['level'].enum_type = _LOGLEVEL +_SERVICECALLRESPONSE_DATAENTRY.containing_type = _SERVICECALLRESPONSE +_SERVICECALLRESPONSE_DATATEMPLATEENTRY.containing_type = _SERVICECALLRESPONSE +_SERVICECALLRESPONSE_VARIABLESENTRY.containing_type = _SERVICECALLRESPONSE +_SERVICECALLRESPONSE.fields_by_name['data'].message_type = _SERVICECALLRESPONSE_DATAENTRY +_SERVICECALLRESPONSE.fields_by_name['data_template'].message_type = _SERVICECALLRESPONSE_DATATEMPLATEENTRY +_SERVICECALLRESPONSE.fields_by_name['variables'].message_type = _SERVICECALLRESPONSE_VARIABLESENTRY +DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST +DESCRIPTOR.message_types_by_name['HelloResponse'] = _HELLORESPONSE +DESCRIPTOR.message_types_by_name['ConnectRequest'] = _CONNECTREQUEST +DESCRIPTOR.message_types_by_name['ConnectResponse'] = _CONNECTRESPONSE +DESCRIPTOR.message_types_by_name['DisconnectRequest'] = _DISCONNECTREQUEST +DESCRIPTOR.message_types_by_name['DisconnectResponse'] = _DISCONNECTRESPONSE +DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST +DESCRIPTOR.message_types_by_name['PingResponse'] = _PINGRESPONSE +DESCRIPTOR.message_types_by_name['DeviceInfoRequest'] = _DEVICEINFOREQUEST +DESCRIPTOR.message_types_by_name['DeviceInfoResponse'] = _DEVICEINFORESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesRequest'] = _LISTENTITIESREQUEST +DESCRIPTOR.message_types_by_name['ListEntitiesBinarySensorResponse'] = _LISTENTITIESBINARYSENSORRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesCoverResponse'] = _LISTENTITIESCOVERRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesFanResponse'] = _LISTENTITIESFANRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesLightResponse'] = _LISTENTITIESLIGHTRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesSensorResponse'] = _LISTENTITIESSENSORRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesSwitchResponse'] = _LISTENTITIESSWITCHRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesTextSensorResponse'] = _LISTENTITIESTEXTSENSORRESPONSE +DESCRIPTOR.message_types_by_name['ListEntitiesDoneResponse'] = _LISTENTITIESDONERESPONSE +DESCRIPTOR.message_types_by_name['SubscribeStatesRequest'] = _SUBSCRIBESTATESREQUEST +DESCRIPTOR.message_types_by_name['BinarySensorStateResponse'] = _BINARYSENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name['CoverStateResponse'] = _COVERSTATERESPONSE +DESCRIPTOR.message_types_by_name['FanStateResponse'] = _FANSTATERESPONSE +DESCRIPTOR.message_types_by_name['LightStateResponse'] = _LIGHTSTATERESPONSE +DESCRIPTOR.message_types_by_name['SensorStateResponse'] = _SENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name['SwitchStateResponse'] = _SWITCHSTATERESPONSE +DESCRIPTOR.message_types_by_name['TextSensorStateResponse'] = _TEXTSENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name['CoverCommandRequest'] = _COVERCOMMANDREQUEST +DESCRIPTOR.message_types_by_name['FanCommandRequest'] = _FANCOMMANDREQUEST +DESCRIPTOR.message_types_by_name['LightCommandRequest'] = _LIGHTCOMMANDREQUEST +DESCRIPTOR.message_types_by_name['SwitchCommandRequest'] = _SWITCHCOMMANDREQUEST +DESCRIPTOR.message_types_by_name['SubscribeLogsRequest'] = _SUBSCRIBELOGSREQUEST +DESCRIPTOR.message_types_by_name['SubscribeLogsResponse'] = _SUBSCRIBELOGSRESPONSE +DESCRIPTOR.message_types_by_name['SubscribeServiceCallsRequest'] = _SUBSCRIBESERVICECALLSREQUEST +DESCRIPTOR.message_types_by_name['ServiceCallResponse'] = _SERVICECALLRESPONSE +DESCRIPTOR.message_types_by_name['SubscribeHomeAssistantStatesRequest'] = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST +DESCRIPTOR.message_types_by_name['SubscribeHomeAssistantStateResponse'] = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE +DESCRIPTOR.message_types_by_name['HomeAssistantStateResponse'] = _HOMEASSISTANTSTATERESPONSE +DESCRIPTOR.message_types_by_name['GetTimeRequest'] = _GETTIMEREQUEST +DESCRIPTOR.message_types_by_name['GetTimeResponse'] = _GETTIMERESPONSE +DESCRIPTOR.enum_types_by_name['FanSpeed'] = _FANSPEED +DESCRIPTOR.enum_types_by_name['LogLevel'] = _LOGLEVEL +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict( + DESCRIPTOR = _HELLOREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:HelloRequest) + )) +_sym_db.RegisterMessage(HelloRequest) + +HelloResponse = _reflection.GeneratedProtocolMessageType('HelloResponse', (_message.Message,), dict( + DESCRIPTOR = _HELLORESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:HelloResponse) + )) +_sym_db.RegisterMessage(HelloResponse) + +ConnectRequest = _reflection.GeneratedProtocolMessageType('ConnectRequest', (_message.Message,), dict( + DESCRIPTOR = _CONNECTREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ConnectRequest) + )) +_sym_db.RegisterMessage(ConnectRequest) + +ConnectResponse = _reflection.GeneratedProtocolMessageType('ConnectResponse', (_message.Message,), dict( + DESCRIPTOR = _CONNECTRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ConnectResponse) + )) +_sym_db.RegisterMessage(ConnectResponse) + +DisconnectRequest = _reflection.GeneratedProtocolMessageType('DisconnectRequest', (_message.Message,), dict( + DESCRIPTOR = _DISCONNECTREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:DisconnectRequest) + )) +_sym_db.RegisterMessage(DisconnectRequest) + +DisconnectResponse = _reflection.GeneratedProtocolMessageType('DisconnectResponse', (_message.Message,), dict( + DESCRIPTOR = _DISCONNECTRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:DisconnectResponse) + )) +_sym_db.RegisterMessage(DisconnectResponse) + +PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict( + DESCRIPTOR = _PINGREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:PingRequest) + )) +_sym_db.RegisterMessage(PingRequest) + +PingResponse = _reflection.GeneratedProtocolMessageType('PingResponse', (_message.Message,), dict( + DESCRIPTOR = _PINGRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:PingResponse) + )) +_sym_db.RegisterMessage(PingResponse) + +DeviceInfoRequest = _reflection.GeneratedProtocolMessageType('DeviceInfoRequest', (_message.Message,), dict( + DESCRIPTOR = _DEVICEINFOREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:DeviceInfoRequest) + )) +_sym_db.RegisterMessage(DeviceInfoRequest) + +DeviceInfoResponse = _reflection.GeneratedProtocolMessageType('DeviceInfoResponse', (_message.Message,), dict( + DESCRIPTOR = _DEVICEINFORESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:DeviceInfoResponse) + )) +_sym_db.RegisterMessage(DeviceInfoResponse) + +ListEntitiesRequest = _reflection.GeneratedProtocolMessageType('ListEntitiesRequest', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesRequest) + )) +_sym_db.RegisterMessage(ListEntitiesRequest) + +ListEntitiesBinarySensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesBinarySensorResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESBINARYSENSORRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesBinarySensorResponse) + )) +_sym_db.RegisterMessage(ListEntitiesBinarySensorResponse) + +ListEntitiesCoverResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesCoverResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESCOVERRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesCoverResponse) + )) +_sym_db.RegisterMessage(ListEntitiesCoverResponse) + +ListEntitiesFanResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesFanResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESFANRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesFanResponse) + )) +_sym_db.RegisterMessage(ListEntitiesFanResponse) + +ListEntitiesLightResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesLightResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESLIGHTRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesLightResponse) + )) +_sym_db.RegisterMessage(ListEntitiesLightResponse) + +ListEntitiesSensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesSensorResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESSENSORRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesSensorResponse) + )) +_sym_db.RegisterMessage(ListEntitiesSensorResponse) + +ListEntitiesSwitchResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesSwitchResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESSWITCHRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesSwitchResponse) + )) +_sym_db.RegisterMessage(ListEntitiesSwitchResponse) + +ListEntitiesTextSensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesTextSensorResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESTEXTSENSORRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesTextSensorResponse) + )) +_sym_db.RegisterMessage(ListEntitiesTextSensorResponse) + +ListEntitiesDoneResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesDoneResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTENTITIESDONERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ListEntitiesDoneResponse) + )) +_sym_db.RegisterMessage(ListEntitiesDoneResponse) + +SubscribeStatesRequest = _reflection.GeneratedProtocolMessageType('SubscribeStatesRequest', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBESTATESREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeStatesRequest) + )) +_sym_db.RegisterMessage(SubscribeStatesRequest) + +BinarySensorStateResponse = _reflection.GeneratedProtocolMessageType('BinarySensorStateResponse', (_message.Message,), dict( + DESCRIPTOR = _BINARYSENSORSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:BinarySensorStateResponse) + )) +_sym_db.RegisterMessage(BinarySensorStateResponse) + +CoverStateResponse = _reflection.GeneratedProtocolMessageType('CoverStateResponse', (_message.Message,), dict( + DESCRIPTOR = _COVERSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:CoverStateResponse) + )) +_sym_db.RegisterMessage(CoverStateResponse) + +FanStateResponse = _reflection.GeneratedProtocolMessageType('FanStateResponse', (_message.Message,), dict( + DESCRIPTOR = _FANSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:FanStateResponse) + )) +_sym_db.RegisterMessage(FanStateResponse) + +LightStateResponse = _reflection.GeneratedProtocolMessageType('LightStateResponse', (_message.Message,), dict( + DESCRIPTOR = _LIGHTSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:LightStateResponse) + )) +_sym_db.RegisterMessage(LightStateResponse) + +SensorStateResponse = _reflection.GeneratedProtocolMessageType('SensorStateResponse', (_message.Message,), dict( + DESCRIPTOR = _SENSORSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SensorStateResponse) + )) +_sym_db.RegisterMessage(SensorStateResponse) + +SwitchStateResponse = _reflection.GeneratedProtocolMessageType('SwitchStateResponse', (_message.Message,), dict( + DESCRIPTOR = _SWITCHSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SwitchStateResponse) + )) +_sym_db.RegisterMessage(SwitchStateResponse) + +TextSensorStateResponse = _reflection.GeneratedProtocolMessageType('TextSensorStateResponse', (_message.Message,), dict( + DESCRIPTOR = _TEXTSENSORSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:TextSensorStateResponse) + )) +_sym_db.RegisterMessage(TextSensorStateResponse) + +CoverCommandRequest = _reflection.GeneratedProtocolMessageType('CoverCommandRequest', (_message.Message,), dict( + DESCRIPTOR = _COVERCOMMANDREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:CoverCommandRequest) + )) +_sym_db.RegisterMessage(CoverCommandRequest) + +FanCommandRequest = _reflection.GeneratedProtocolMessageType('FanCommandRequest', (_message.Message,), dict( + DESCRIPTOR = _FANCOMMANDREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:FanCommandRequest) + )) +_sym_db.RegisterMessage(FanCommandRequest) + +LightCommandRequest = _reflection.GeneratedProtocolMessageType('LightCommandRequest', (_message.Message,), dict( + DESCRIPTOR = _LIGHTCOMMANDREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:LightCommandRequest) + )) +_sym_db.RegisterMessage(LightCommandRequest) + +SwitchCommandRequest = _reflection.GeneratedProtocolMessageType('SwitchCommandRequest', (_message.Message,), dict( + DESCRIPTOR = _SWITCHCOMMANDREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SwitchCommandRequest) + )) +_sym_db.RegisterMessage(SwitchCommandRequest) + +SubscribeLogsRequest = _reflection.GeneratedProtocolMessageType('SubscribeLogsRequest', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBELOGSREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeLogsRequest) + )) +_sym_db.RegisterMessage(SubscribeLogsRequest) + +SubscribeLogsResponse = _reflection.GeneratedProtocolMessageType('SubscribeLogsResponse', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBELOGSRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeLogsResponse) + )) +_sym_db.RegisterMessage(SubscribeLogsResponse) + +SubscribeServiceCallsRequest = _reflection.GeneratedProtocolMessageType('SubscribeServiceCallsRequest', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBESERVICECALLSREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeServiceCallsRequest) + )) +_sym_db.RegisterMessage(SubscribeServiceCallsRequest) + +ServiceCallResponse = _reflection.GeneratedProtocolMessageType('ServiceCallResponse', (_message.Message,), dict( + + DataEntry = _reflection.GeneratedProtocolMessageType('DataEntry', (_message.Message,), dict( + DESCRIPTOR = _SERVICECALLRESPONSE_DATAENTRY, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataEntry) + )) + , + + DataTemplateEntry = _reflection.GeneratedProtocolMessageType('DataTemplateEntry', (_message.Message,), dict( + DESCRIPTOR = _SERVICECALLRESPONSE_DATATEMPLATEENTRY, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataTemplateEntry) + )) + , + + VariablesEntry = _reflection.GeneratedProtocolMessageType('VariablesEntry', (_message.Message,), dict( + DESCRIPTOR = _SERVICECALLRESPONSE_VARIABLESENTRY, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ServiceCallResponse.VariablesEntry) + )) + , + DESCRIPTOR = _SERVICECALLRESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:ServiceCallResponse) + )) +_sym_db.RegisterMessage(ServiceCallResponse) +_sym_db.RegisterMessage(ServiceCallResponse.DataEntry) +_sym_db.RegisterMessage(ServiceCallResponse.DataTemplateEntry) +_sym_db.RegisterMessage(ServiceCallResponse.VariablesEntry) + +SubscribeHomeAssistantStatesRequest = _reflection.GeneratedProtocolMessageType('SubscribeHomeAssistantStatesRequest', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStatesRequest) + )) +_sym_db.RegisterMessage(SubscribeHomeAssistantStatesRequest) + +SubscribeHomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType('SubscribeHomeAssistantStateResponse', (_message.Message,), dict( + DESCRIPTOR = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStateResponse) + )) +_sym_db.RegisterMessage(SubscribeHomeAssistantStateResponse) + +HomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType('HomeAssistantStateResponse', (_message.Message,), dict( + DESCRIPTOR = _HOMEASSISTANTSTATERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:HomeAssistantStateResponse) + )) +_sym_db.RegisterMessage(HomeAssistantStateResponse) + +GetTimeRequest = _reflection.GeneratedProtocolMessageType('GetTimeRequest', (_message.Message,), dict( + DESCRIPTOR = _GETTIMEREQUEST, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:GetTimeRequest) + )) +_sym_db.RegisterMessage(GetTimeRequest) + +GetTimeResponse = _reflection.GeneratedProtocolMessageType('GetTimeResponse', (_message.Message,), dict( + DESCRIPTOR = _GETTIMERESPONSE, + __module__ = 'api_pb2' + # @@protoc_insertion_point(class_scope:GetTimeResponse) + )) +_sym_db.RegisterMessage(GetTimeResponse) + + +_SERVICECALLRESPONSE_DATAENTRY._options = None +_SERVICECALLRESPONSE_DATATEMPLATEENTRY._options = None +_SERVICECALLRESPONSE_VARIABLESENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/esphomeyaml/api/client.py b/esphomeyaml/api/client.py new file mode 100644 index 0000000000..446de042b5 --- /dev/null +++ b/esphomeyaml/api/client.py @@ -0,0 +1,490 @@ +from datetime import datetime +import functools +import logging +import socket +import threading +import time + +# pylint: disable=unused-import +from typing import Optional # noqa +from google.protobuf import message # noqa + +from esphomeyaml import const +import esphomeyaml.api.api_pb2 as pb +from esphomeyaml.const import CONF_PASSWORD, CONF_PORT +from esphomeyaml.core import EsphomeyamlError +from esphomeyaml.helpers import resolve_ip_address, indent, color +from esphomeyaml.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte, format_bytes +from esphomeyaml.util import safe_print + +_LOGGER = logging.getLogger(__name__) + + +class APIConnectionError(EsphomeyamlError): + pass + + +MESSAGE_TYPE_TO_PROTO = { + 1: pb.HelloRequest, + 2: pb.HelloResponse, + 3: pb.ConnectRequest, + 4: pb.ConnectResponse, + 5: pb.DisconnectRequest, + 6: pb.DisconnectResponse, + 7: pb.PingRequest, + 8: pb.PingResponse, + 9: pb.DeviceInfoRequest, + 10: pb.DeviceInfoResponse, + 11: pb.ListEntitiesRequest, + 12: pb.ListEntitiesBinarySensorResponse, + 13: pb.ListEntitiesCoverResponse, + 14: pb.ListEntitiesFanResponse, + 15: pb.ListEntitiesLightResponse, + 16: pb.ListEntitiesSensorResponse, + 17: pb.ListEntitiesSwitchResponse, + 18: pb.ListEntitiesTextSensorResponse, + 19: pb.ListEntitiesDoneResponse, + 20: pb.SubscribeStatesRequest, + 21: pb.BinarySensorStateResponse, + 22: pb.CoverStateResponse, + 23: pb.FanStateResponse, + 24: pb.LightStateResponse, + 25: pb.SensorStateResponse, + 26: pb.SwitchStateResponse, + 27: pb.TextSensorStateResponse, + 28: pb.SubscribeLogsRequest, + 29: pb.SubscribeLogsResponse, + 30: pb.CoverCommandRequest, + 31: pb.FanCommandRequest, + 32: pb.LightCommandRequest, + 33: pb.SwitchCommandRequest, + 34: pb.SubscribeServiceCallsRequest, + 35: pb.ServiceCallResponse, + 36: pb.GetTimeRequest, + 37: pb.GetTimeResponse, +} + + +def _varuint_to_bytes(value): + if value <= 0x7F: + return byte_to_bytes(value) + + ret = bytes() + while value: + temp = value & 0x7F + value >>= 7 + if value: + ret += byte_to_bytes(temp | 0x80) + else: + ret += byte_to_bytes(temp) + + return ret + + +def _bytes_to_varuint(value): + result = 0 + bitpos = 0 + for c in value: + val = char_to_byte(c) + result |= (val & 0x7F) << bitpos + bitpos += 7 + if (val & 0x80) == 0: + return result + return None + + +# pylint: disable=too-many-instance-attributes,not-callable +class APIClient(threading.Thread): + def __init__(self, address, port, password): + threading.Thread.__init__(self) + self._address = address # type: str + self._port = port # type: int + self._password = password # type: Optional[str] + self._socket = None # type: Optional[socket.socket] + self._socket_open_event = threading.Event() + self._socket_write_lock = threading.Lock() + self._connected = False + self._authenticated = False + self._message_handlers = [] + self._keepalive = 5 + self._ping_timer = None + self._refresh_ping() + + self.on_disconnect = None + self.on_connect = None + self.on_login = None + self.auto_reconnect = False + self._running_event = threading.Event() + self._stop_event = threading.Event() + + @property + def stopped(self): + return self._stop_event.is_set() + + def _refresh_ping(self): + if self._ping_timer is not None: + self._ping_timer.cancel() + self._ping_timer = None + + def func(): + self._ping_timer = None + + if self._connected: + try: + self.ping() + except APIConnectionError: + self._fatal_error() + else: + self._refresh_ping() + + self._ping_timer = threading.Timer(self._keepalive, func) + self._ping_timer.start() + + def _cancel_ping(self): + if self._ping_timer is not None: + self._ping_timer.cancel() + self._ping_timer = None + + def _close_socket(self): + self._cancel_ping() + if self._socket is not None: + self._socket.close() + self._socket = None + self._socket_open_event.clear() + self._connected = False + self._authenticated = False + self._message_handlers = [] + + def stop(self, force=False): + if self.stopped: + raise ValueError + + if self._connected and not force: + try: + self.disconnect() + except APIConnectionError: + pass + self._close_socket() + + self._stop_event.set() + if not force: + self.join() + + def connect(self): + if not self._running_event.wait(0.1): + raise APIConnectionError("You need to call start() first!") + + if self._connected: + raise APIConnectionError("Already connected!") + + try: + ip = resolve_ip_address(self._address) + except EsphomeyamlError as err: + _LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?", + self._address) + _LOGGER.warning("(If this error persists, please set a static IP address: " + "https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips)") + raise APIConnectionError(err) + + _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip) + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.settimeout(10.0) + self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + try: + self._socket.connect((ip, self._port)) + except socket.error as err: + self._fatal_error() + raise APIConnectionError("Error connecting to {}: {}".format(ip, err)) + self._socket.settimeout(0.1) + + self._socket_open_event.set() + + hello = pb.HelloRequest() + hello.client_info = 'esphomeyaml v{}'.format(const.__version__) + try: + resp = self._send_message_await_response(hello, pb.HelloResponse) + except APIConnectionError as err: + self._fatal_error() + raise err + _LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address, + resp.server_info, resp.api_version_major, resp.api_version_minor) + self._connected = True + if self.on_connect is not None: + self.on_connect() + + def _check_connected(self): + if not self._connected: + self._fatal_error() + raise APIConnectionError("Must be connected!") + + def login(self): + self._check_connected() + if self._authenticated: + raise APIConnectionError("Already logged in!") + + connect = pb.ConnectRequest() + if self._password is not None: + connect.password = self._password + resp = self._send_message_await_response(connect, pb.ConnectResponse) + if resp.invalid_password: + raise APIConnectionError("Invalid password!") + + self._authenticated = True + if self.on_login is not None: + self.on_login() + + def _fatal_error(self): + was_connected = self._connected + + self._close_socket() + + if was_connected and self.on_disconnect is not None: + self.on_disconnect() + + def _write(self, data): # type: (bytes) -> None + if self._socket is None: + raise APIConnectionError("Socket closed") + + _LOGGER.debug("Write: %s", format_bytes(data)) + with self._socket_write_lock: + try: + self._socket.sendall(data) + except socket.error as err: + self._fatal_error() + raise APIConnectionError("Error while writing data: {}".format(err)) + + def _send_message(self, msg): + # type: (message.Message) -> None + for message_type, klass in MESSAGE_TYPE_TO_PROTO.items(): + if isinstance(msg, klass): + break + else: + raise ValueError + + encoded = msg.SerializeToString() + _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg))) + if IS_PY2: + req = chr(0x00) + else: + req = bytes([0]) + req += _varuint_to_bytes(len(encoded)) + req += _varuint_to_bytes(message_type) + req += encoded + self._write(req) + self._refresh_ping() + + def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=1): + event = threading.Event() + responses = [] + + def on_message(resp): + if do_append(resp): + responses.append(resp) + if do_stop(resp): + event.set() + + self._message_handlers.append(on_message) + self._send_message(send_msg) + ret = event.wait(timeout) + try: + self._message_handlers.remove(on_message) + except ValueError: + pass + if not ret: + raise APIConnectionError("Timeout while waiting for message response!") + return responses + + def _send_message_await_response(self, send_msg, response_type, timeout=1): + def is_response(msg): + return isinstance(msg, response_type) + + return self._send_message_await_response_complex(send_msg, is_response, is_response, + timeout)[0] + + def device_info(self): + self._check_connected() + return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse) + + def ping(self): + self._check_connected() + return self._send_message_await_response(pb.PingRequest(), pb.PingResponse) + + def disconnect(self): + self._check_connected() + + try: + self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse) + except APIConnectionError: + pass + self._close_socket() + + if self.on_disconnect is not None: + self.on_disconnect() + + def _check_authenticated(self): + if not self._authenticated: + raise APIConnectionError("Must login first!") + + def subscribe_logs(self, on_log, log_level=None, dump_config=False): + self._check_authenticated() + + def on_msg(msg): + if isinstance(msg, pb.SubscribeLogsResponse): + on_log(msg) + + self._message_handlers.append(on_msg) + req = pb.SubscribeLogsRequest(dump_config=dump_config) + if log_level is not None: + req.level = log_level + self._send_message(req) + + def _recv(self, amount): + ret = bytes() + if amount == 0: + return ret + + while len(ret) < amount: + if self.stopped: + raise APIConnectionError("Stopped!") + if not self._socket_open_event.is_set(): + raise APIConnectionError("No socket!") + try: + val = self._socket.recv(amount - len(ret)) + except AttributeError: + raise APIConnectionError("Socket was closed") + except socket.timeout: + continue + except socket.error as err: + raise APIConnectionError("Error while receiving data: {}".format(err)) + ret += val + return ret + + def _recv_varint(self): + raw = bytes() + while not raw or char_to_byte(raw[-1]) & 0x80: + raw += self._recv(1) + return _bytes_to_varuint(raw) + + def _run_once(self): + if not self._socket_open_event.wait(0.1): + return + + # Preamble + if char_to_byte(self._recv(1)[0]) != 0x00: + raise APIConnectionError("Invalid preamble") + + length = self._recv_varint() + msg_type = self._recv_varint() + + raw_msg = self._recv(length) + if msg_type not in MESSAGE_TYPE_TO_PROTO: + _LOGGER.debug("Skipping message type %s", msg_type) + return + + msg = MESSAGE_TYPE_TO_PROTO[msg_type]() + msg.ParseFromString(raw_msg) + _LOGGER.debug("Got message: %s:\n%s", type(msg), indent(str(msg))) + for msg_handler in self._message_handlers[:]: + msg_handler(msg) + self._handle_internal_messages(msg) + self._refresh_ping() + + def run(self): + self._running_event.set() + while not self.stopped: + try: + self._run_once() + except APIConnectionError as err: + if self.stopped: + break + if self._connected: + _LOGGER.error("Error while reading incoming messages: %s", err) + self._fatal_error() + self._running_event.clear() + + def _handle_internal_messages(self, msg): + if isinstance(msg, pb.DisconnectRequest): + self._send_message(pb.DisconnectResponse()) + if self._socket is not None: + self._socket.close() + self._socket = None + self._connected = False + if self.on_disconnect is not None: + self.on_disconnect() + elif isinstance(msg, pb.PingRequest): + self._send_message(pb.PingResponse()) + elif isinstance(msg, pb.GetTimeRequest): + resp = pb.GetTimeResponse() + resp.epoch_seconds = int(time.time()) + self._send_message(resp) + + +def run_logs(config, address): + conf = config['api'] + port = conf[CONF_PORT] + password = conf[CONF_PASSWORD] + _LOGGER.info("Starting log output from %s using esphomelib API", address) + + cli = APIClient(address, port, password) + stopping = False + retry_timer = [] + + def try_connect(tries=0, is_disconnect=True): + if stopping: + return + + if is_disconnect: + _LOGGER.warning(u"Disconnected from API.") + + while retry_timer: + retry_timer.pop(0).cancel() + + error = None + try: + cli.connect() + cli.login() + except APIConnectionError as err: # noqa + error = err + + if error is None: + _LOGGER.info("Successfully connected to %s", address) + return + + wait_time = min(2**tries, 300) + _LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds", + error, wait_time) + timer = threading.Timer(wait_time, functools.partial(try_connect, tries + 1, is_disconnect)) + timer.start() + retry_timer.append(timer) + + def on_log(msg): + time_ = datetime.now().time().strftime(u'[%H:%M:%S]') + text = msg.message + if msg.send_failed: + text = color('white', '(Message skipped because it was too big to fit in ' + 'TCP buffer - This is only cosmetic)') + safe_print(time_ + text) + + has_connects = [] + + def on_login(): + try: + cli.subscribe_logs(on_log, dump_config=not has_connects) + has_connects.append(True) + except APIConnectionError: + cli.disconnect() + + cli.on_disconnect = try_connect + cli.on_login = on_login + cli.start() + + try: + try_connect(is_disconnect=False) + while True: + time.sleep(1) + except KeyboardInterrupt: + stopping = True + cli.stop(True) + while retry_timer: + retry_timer.pop(0).cancel() + return 0 diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index de32e7d39c..8e73a314ca 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -2,16 +2,15 @@ import copy import voluptuous as vol -from esphomeyaml import core import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \ - CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \ - CONF_ELSE, CONF_ID, CONF_IF, CONF_LAMBDA, \ - 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, PollingComponent, \ - Action, Component, Trigger + CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, CONF_ELSE, CONF_ID, CONF_IF, \ + CONF_LAMBDA, CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID, CONF_WHILE +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, TemplateArguments, add, \ + get_variable, process_lambda, templatable +from esphomeyaml.cpp_types import Action, App, Component, PollingComponent, Trigger, \ + esphomelib_ns, float_, uint32, void, bool_ from esphomeyaml.util import ServiceRegistry @@ -27,41 +26,82 @@ def maybe_simple_id(*validators): def validate_recursive_condition(value): - return CONDITIONS_SCHEMA(value) + is_list = isinstance(value, list) + value = cv.ensure_list()(value)[:] + for i, item in enumerate(value): + path = [i] if is_list else [] + item = copy.deepcopy(item) + if not isinstance(item, dict): + raise vol.Invalid(u"Condition must consist of key-value mapping! Got {}".format(item), + path) + key = next((x for x in item if x != CONF_CONDITION_ID), None) + if key is None: + raise vol.Invalid(u"Key missing from action! Got {}".format(item), path) + if key not in CONDITION_REGISTRY: + raise vol.Invalid(u"Unable to find condition with the name '{}', is the " + u"component loaded?".format(key), path + [key]) + item.setdefault(CONF_CONDITION_ID, None) + key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None) + if key2 is not None: + raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! " + u"Did you forget to indent the block inside the condition?" + u"".format(key, key2), path) + validator = CONDITION_REGISTRY[key][0] + try: + condition = validator(item[key]) + except vol.Invalid as err: + err.prepend(path) + raise err + value[i] = { + CONF_CONDITION_ID: cv.declare_variable_id(Condition)(item[CONF_CONDITION_ID]), + key: condition, + } + return value def validate_recursive_action(value): - value = cv.ensure_list(value)[:] + is_list = isinstance(value, list) + if not is_list: + value = [value] for i, item in enumerate(value): + path = [i] if is_list else [] item = copy.deepcopy(item) if not isinstance(item, dict): - raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item)) + raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item), + path) key = next((x for x in item if x != CONF_ACTION_ID), None) if key is None: - raise vol.Invalid(u"Key missing from action! Got {}".format(item)) + raise vol.Invalid(u"Key missing from action! Got {}".format(item), path) if key not in ACTION_REGISTRY: raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?" - u"".format(key)) + u"".format(key), path + [key]) item.setdefault(CONF_ACTION_ID, None) - key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None) + key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None) if key2 is not None: 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)) + u"Did you forget to indent the block inside the action?" + u"".format(key, key2), path) validator = ACTION_REGISTRY[key][0] + try: + action = validator(item[key]) + except vol.Invalid as err: + err.prepend(path) + raise err value[i] = { CONF_ACTION_ID: cv.declare_variable_id(Action)(item[CONF_ACTION_ID]), - key: validator(item[key]) + key: action, } return value ACTION_REGISTRY = ServiceRegistry() +CONDITION_REGISTRY = ServiceRegistry() # pylint: disable=invalid-name DelayAction = esphomelib_ns.class_('DelayAction', Action, Component) LambdaAction = esphomelib_ns.class_('LambdaAction', Action) IfAction = esphomelib_ns.class_('IfAction', Action) +WhileAction = esphomelib_ns.class_('WhileAction', Action) UpdateComponentAction = esphomelib_ns.class_('UpdateComponentAction', Action) Automation = esphomelib_ns.class_('Automation') @@ -71,17 +111,6 @@ 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(Condition), - vol.Optional(CONF_AND): validate_recursive_condition, - vol.Optional(CONF_OR): validate_recursive_condition, - vol.Optional(CONF_RANGE): vol.All(vol.Schema({ - vol.Optional(CONF_ABOVE): vol.Coerce(float), - vol.Optional(CONF_BELOW): vol.Coerce(float), - }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)), - vol.Optional(CONF_LAMBDA): cv.lambda_, -})]) - def validate_automation(extra_schema=None, extra_validators=None, single=False): schema = AUTOMATION_SCHEMA.extend(extra_schema or {}) @@ -122,63 +151,63 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): AUTOMATION_SCHEMA = vol.Schema({ 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.Optional(CONF_IF): validate_recursive_condition, vol.Required(CONF_THEN): validate_recursive_action, }) +AND_CONDITION_SCHEMA = validate_recursive_condition -def build_condition(config, arg_type): - template_arg = TemplateArguments(arg_type) - if isinstance(config, core.Lambda): - lambda_ = None - for lambda_ in process_lambda(config, [(arg_type, 'x')]): + +@CONDITION_REGISTRY.register(CONF_AND, AND_CONDITION_SCHEMA) +def and_condition_to_code(config, condition_id, arg_type, template_arg): + for conditions in build_conditions(config, arg_type): + yield + rhs = AndCondition.new(template_arg, conditions) + type = AndCondition.template(template_arg) + yield Pvariable(condition_id, rhs, type=type) + + +OR_CONDITION_SCHEMA = validate_recursive_condition + + +@CONDITION_REGISTRY.register(CONF_OR, OR_CONDITION_SCHEMA) +def or_condition_to_code(config, condition_id, arg_type, template_arg): + for conditions in build_conditions(config, arg_type): + yield + rhs = OrCondition.new(template_arg, conditions) + type = OrCondition.template(template_arg) + yield Pvariable(condition_id, rhs, type=type) + + +RANGE_CONDITION_SCHEMA = vol.All(vol.Schema({ + vol.Optional(CONF_ABOVE): cv.templatable(cv.float_), + vol.Optional(CONF_BELOW): cv.templatable(cv.float_), +}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) + + +@CONDITION_REGISTRY.register(CONF_RANGE, RANGE_CONDITION_SCHEMA) +def range_condition_to_code(config, condition_id, arg_type, template_arg): + for conditions in build_conditions(config, arg_type): + yield + rhs = RangeCondition.new(template_arg, conditions) + type = RangeCondition.template(template_arg) + condition = Pvariable(condition_id, rhs, type=type) + if CONF_ABOVE in config: + for template_ in templatable(config[CONF_ABOVE], arg_type, float_): yield - yield LambdaCondition.new(template_arg, lambda_) - elif CONF_AND in config: - yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg)) - elif CONF_OR in config: - yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg)) - elif CONF_LAMBDA in config: - lambda_ = None - for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]): + condition.set_min(template_) + if CONF_BELOW in config: + for template_ in templatable(config[CONF_BELOW], arg_type, float_): yield - yield LambdaCondition.new(template_arg, lambda_) - elif CONF_RANGE in config: - conf = config[CONF_RANGE] - rhs = RangeCondition.new(template_arg) - type = RangeCondition.template(template_arg) - condition = Pvariable(config[CONF_CONDITION_ID], rhs, type=type) - if CONF_ABOVE in conf: - template_ = None - for template_ in templatable(conf[CONF_ABOVE], arg_type, float_): - yield - condition.set_min(template_) - if CONF_BELOW in conf: - template_ = None - for template_ in templatable(conf[CONF_BELOW], arg_type, float_): - yield - condition.set_max(template_) - yield condition - else: - raise ESPHomeYAMLError(u"Unsupported condition {}".format(config)) - - -def build_conditions(config, arg_type): - conditions = [] - for conf in config: - condition = None - for condition in build_condition(conf, arg_type): - yield None - conditions.append(condition) - yield ArrayInitializer(*conditions) + condition.set_max(template_) + yield condition DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds) @ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA) -def delay_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def delay_action_to_code(config, action_id, arg_type, template_arg): rhs = App.register_component(DelayAction.new(template_arg)) type = DelayAction.template(template_arg) action = Pvariable(action_id, rhs, type=type) @@ -196,8 +225,7 @@ IF_ACTION_SCHEMA = vol.All({ @ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA) -def if_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def if_action_to_code(config, action_id, arg_type, template_arg): for conditions in build_conditions(config[CONF_CONDITION], arg_type): yield None rhs = IfAction.new(template_arg, conditions) @@ -214,19 +242,49 @@ def if_action_to_code(config, action_id, arg_type): yield action +WHILE_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_CONDITION): validate_recursive_condition, + vol.Required(CONF_THEN): validate_recursive_action, +}) + + +@ACTION_REGISTRY.register(CONF_WHILE, WHILE_ACTION_SCHEMA) +def while_action_to_code(config, action_id, arg_type, template_arg): + for conditions in build_conditions(config[CONF_CONDITION], arg_type): + yield None + rhs = WhileAction.new(template_arg, conditions) + type = WhileAction.template(template_arg) + action = Pvariable(action_id, rhs, type=type) + for actions in build_actions(config[CONF_THEN], arg_type): + yield None + add(action.add_then(actions)) + yield action + + LAMBDA_ACTION_SCHEMA = cv.lambda_ @ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA) -def lambda_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) - for lambda_ in process_lambda(config, [(arg_type, 'x')]): +def lambda_action_to_code(config, action_id, arg_type, template_arg): + for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=void): yield None rhs = LambdaAction.new(template_arg, lambda_) type = LambdaAction.template(template_arg) yield Pvariable(action_id, rhs, type=type) +LAMBDA_CONDITION_SCHEMA = cv.lambda_ + + +@CONDITION_REGISTRY.register(CONF_LAMBDA, LAMBDA_CONDITION_SCHEMA) +def lambda_condition_to_code(config, condition_id, arg_type, template_arg): + for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=bool_): + yield + rhs = LambdaCondition.new(template_arg, lambda_) + type = LambdaCondition.template(template_arg) + yield Pvariable(condition_id, rhs, type=type) + + CONF_COMPONENT_UPDATE = 'component.update' COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(PollingComponent), @@ -234,8 +292,7 @@ COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA) -def component_update_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def component_update_action_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = UpdateComponentAction.new(var) @@ -248,7 +305,8 @@ def build_action(full_config, arg_type): key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY) builder = ACTION_REGISTRY[key][1] - for result in builder(config, action_id, arg_type): + template_arg = TemplateArguments(arg_type) + for result in builder(config, action_id, arg_type, template_arg): yield None yield result @@ -263,6 +321,26 @@ def build_actions(config, arg_type): yield ArrayInitializer(*actions, multiline=False) +def build_condition(full_config, arg_type): + action_id = full_config[CONF_CONDITION_ID] + key, config = next((k, v) for k, v in full_config.items() if k in CONDITION_REGISTRY) + + builder = CONDITION_REGISTRY[key][1] + template_arg = TemplateArguments(arg_type) + for result in builder(config, action_id, arg_type, template_arg): + yield None + yield result + + +def build_conditions(config, arg_type): + conditions = [] + for conf in config: + for condition in build_condition(conf, arg_type): + yield None + conditions.append(condition) + yield ArrayInitializer(*conditions, multiline=False) + + def build_automation_(trigger, arg_type, config): rhs = App.make_automation(TemplateArguments(arg_type), trigger) type = Automation.template(arg_type) @@ -280,4 +358,4 @@ def build_automation_(trigger, arg_type, config): def build_automation(trigger, arg_type, config): - add_job(build_automation_, trigger, arg_type, config) + CORE.add_job(build_automation_, trigger, arg_type, config) diff --git a/esphomeyaml/components/ads1115.py b/esphomeyaml/components/ads1115.py index d8d1d9b7cd..70be84ae7f 100644 --- a/esphomeyaml/components/ads1115.py +++ b/esphomeyaml/components/ads1115.py @@ -1,27 +1,27 @@ import voluptuous as vol -from esphomeyaml.components import sensor, i2c +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_ID -from esphomeyaml.helpers import App, Pvariable, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component DEPENDENCIES = ['i2c'] +MULTI_CONF = True ADS1115Component = sensor.sensor_ns.class_('ADS1115Component', Component, i2c.I2CDevice) -ADS1115_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(ADS1115Component), vol.Required(CONF_ADDRESS): cv.i2c_address, }).extend(cv.COMPONENT_SCHEMA.schema) -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]) - var = Pvariable(conf[CONF_ID], rhs) - setup_component(var, conf) + rhs = App.make_ads1115_component(config[CONF_ADDRESS]) + var = Pvariable(config[CONF_ID], rhs) + setup_component(var, config) BUILD_FLAGS = '-DUSE_ADS1115_SENSOR' diff --git a/esphomeyaml/components/apds9960.py b/esphomeyaml/components/apds9960.py new file mode 100644 index 0000000000..e9f7782b43 --- /dev/null +++ b/esphomeyaml/components/apds9960.py @@ -0,0 +1,33 @@ +import voluptuous as vol + +from esphomeyaml.components import i2c, sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent + +DEPENDENCIES = ['i2c'] +MULTI_CONF = True + +CONF_APDS9960_ID = 'apds9960_id' +APDS9960 = sensor.sensor_ns.class_('APDS9960', PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(APDS9960), + vol.Optional(CONF_ADDRESS): cv.i2c_address, + vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + rhs = App.make_apds9960(config.get(CONF_UPDATE_INTERVAL)) + var = Pvariable(config[CONF_ID], rhs) + + if CONF_ADDRESS in config: + add(var.set_address(config[CONF_ADDRESS])) + + setup_component(var, config) + + +BUILD_FLAGS = '-DUSE_APDS9960' diff --git a/esphomeyaml/components/api.py b/esphomeyaml/components/api.py new file mode 100644 index 0000000000..eb8bc3b682 --- /dev/null +++ b/esphomeyaml/components/api.py @@ -0,0 +1,88 @@ +import voluptuous as vol + +from esphomeyaml.automation import ACTION_REGISTRY +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \ + CONF_SERVICE, CONF_VARIABLES, CONF_REBOOT_TIMEOUT +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, get_variable, process_lambda +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Action, App, Component, StoringController, esphomelib_ns + +api_ns = esphomelib_ns.namespace('api') +APIServer = api_ns.class_('APIServer', Component, StoringController) +HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', Action) +KeyValuePair = api_ns.class_('KeyValuePair') +TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair') + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(APIServer), + vol.Optional(CONF_PORT, default=6053): cv.port, + vol.Optional(CONF_PASSWORD, default=''): cv.string_strict, + vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds, +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + rhs = App.init_api_server() + api = Pvariable(config[CONF_ID], rhs) + + if config[CONF_PORT] != 6053: + add(api.set_port(config[CONF_PORT])) + if config.get(CONF_PASSWORD): + add(api.set_password(config[CONF_PASSWORD])) + if CONF_REBOOT_TIMEOUT in config: + add(api.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) + + setup_component(api, config) + + +BUILD_FLAGS = '-DUSE_API' + + +def lib_deps(config): + if CORE.is_esp32: + return 'AsyncTCP@1.0.1' + if CORE.is_esp8266: + return 'ESPAsyncTCP@1.1.3' + raise NotImplementedError + + +CONF_HOMEASSISTANT_SERVICE = 'homeassistant.service' +HOMEASSISTANT_SERVIC_ACTION_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.use_variable_id(APIServer), + vol.Required(CONF_SERVICE): cv.string, + vol.Optional(CONF_DATA): vol.Schema({ + cv.string: cv.string, + }), + vol.Optional(CONF_DATA_TEMPLATE): vol.Schema({ + cv.string: cv.string, + }), + vol.Optional(CONF_VARIABLES): vol.Schema({ + cv.string: cv.lambda_, + }), +}) + + +@ACTION_REGISTRY.register(CONF_HOMEASSISTANT_SERVICE, HOMEASSISTANT_SERVIC_ACTION_SCHEMA) +def homeassistant_service_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_home_assistant_service_call_action(template_arg) + type = HomeAssistantServiceCallAction.template(arg_type) + act = Pvariable(action_id, rhs, type=type) + add(act.set_service(config[CONF_SERVICE])) + if CONF_DATA in config: + datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA].items()] + add(act.set_data(ArrayInitializer(*datas))) + if CONF_DATA_TEMPLATE in config: + datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA_TEMPLATE].items()] + add(act.set_data_template(ArrayInitializer(*datas))) + if CONF_VARIABLES in config: + datas = [] + for key, value in config[CONF_VARIABLES].items(): + for value_ in process_lambda(value, []): + yield None + datas.append(TemplatableKeyValuePair(key, value_)) + add(act.set_variables(ArrayInitializer(*datas))) + yield act diff --git a/esphomeyaml/components/binary_sensor/__init__.py b/esphomeyaml/components/binary_sensor/__init__.py index e4cf8a264b..6e08acd9d6 100644 --- a/esphomeyaml/components/binary_sensor/__init__.py +++ b/esphomeyaml/components/binary_sensor/__init__.py @@ -1,16 +1,21 @@ import voluptuous as vol from esphomeyaml import automation, core +from esphomeyaml.automation import maybe_simple_id, CONDITION_REGISTRY, Condition from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component import esphomeyaml.config_validation as cv 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 + CONF_TIMING, CONF_TRIGGER_ID, CONF_ON_STATE +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import process_lambda, ArrayInitializer, add, Pvariable, \ + StructInitializer, get_variable +from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, NoArg, Component, App, bool_, \ + optional +from esphomeyaml.py_compat import string_types DEVICE_CLASSES = [ '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', @@ -25,6 +30,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ binary_sensor_ns = esphomelib_ns.namespace('binary_sensor') BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable) +BinarySensorPtr = BinarySensor.operator('ptr') MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent) # Triggers @@ -34,6 +40,10 @@ 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') +StateTrigger = binary_sensor_ns.class_('StateTrigger', Trigger.template(bool_)) + +# Condition +BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition) # Filters Filter = binary_sensor_ns.class_('Filter') @@ -46,13 +56,13 @@ LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT] -FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ +FILTERS_SCHEMA = cv.ensure_list({ vol.Optional(CONF_INVERT): None, vol.Optional(CONF_DELAYED_ON): cv.positive_time_period_milliseconds, vol.Optional(CONF_DELAYED_OFF): cv.positive_time_period_milliseconds, vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds, vol.Optional(CONF_LAMBDA): cv.lambda_, -}, cv.has_exactly_one_key(*FILTER_KEYS))]) +}, cv.has_exactly_one_key(*FILTER_KEYS)) MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ vol.Optional(CONF_STATE): cv.boolean, @@ -62,7 +72,7 @@ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ def parse_multi_click_timing_str(value): - if not isinstance(value, basestring): + if not isinstance(value, string_types): return value parts = value.lower().split(' ') @@ -150,7 +160,7 @@ def validate_multi_click_timing(value): BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent), - vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)), + vol.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), vol.Optional(CONF_FILTERS): FILTERS_SCHEMA, vol.Optional(CONF_ON_PRESS): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger), @@ -174,6 +184,9 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ validate_multi_click_timing), vol.Optional(CONF_INVALID_COOLDOWN): cv.positive_time_period_milliseconds, }), + vol.Optional(CONF_ON_STATE): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StateTrigger), + }), vol.Optional(CONF_INVERTED): cv.invalid( "The inverted binary_sensor property has been replaced by the " @@ -195,8 +208,8 @@ def setup_filter(config): elif CONF_HEARTBEAT in config: yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT])) elif CONF_LAMBDA in config: - lambda_ = None - for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')], + return_type=optional.template(bool_)): yield None yield LambdaFilter.new(lambda_) @@ -261,6 +274,11 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config): add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) automation.build_automation(trigger, NoArg, conf) + for conf in config.get(CONF_ON_STATE, []): + rhs = binary_sensor_var.make_state_trigger() + trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + automation.build_automation(trigger, bool_, conf) + setup_mqtt_component(mqtt_var, config) @@ -269,14 +287,14 @@ def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config): has_side_effects=False) mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False) - add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) + CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) def register_binary_sensor(var, config): binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True) rhs = App.register_binary_sensor(binary_sensor_var) mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True) - add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) + CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) def core_to_hass_config(data, config): @@ -290,3 +308,33 @@ def core_to_hass_config(data, config): BUILD_FLAGS = '-DUSE_BINARY_SENSOR' + + +CONF_BINARY_SENSOR_IS_ON = 'binary_sensor.is_on' +BINARY_SENSOR_IS_ON_CONDITION_SCHEMA = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(BinarySensor), +}) + + +@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_ON, BINARY_SENSOR_IS_ON_CONDITION_SCHEMA) +def binary_sensor_is_on_to_code(config, condition_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_binary_sensor_is_on_condition(template_arg) + type = BinarySensorCondition.template(arg_type) + yield Pvariable(condition_id, rhs, type=type) + + +CONF_BINARY_SENSOR_IS_OFF = 'binary_sensor.is_off' +BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(BinarySensor), +}) + + +@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_OFF, BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA) +def binary_sensor_is_off_to_code(config, condition_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_binary_sensor_is_off_condition(template_arg) + type = BinarySensorCondition.template(arg_type) + yield Pvariable(condition_id, rhs, type=type) diff --git a/esphomeyaml/components/binary_sensor/apds9960.py b/esphomeyaml/components/binary_sensor/apds9960.py new file mode 100644 index 0000000000..11444bce88 --- /dev/null +++ b/esphomeyaml/components/binary_sensor/apds9960.py @@ -0,0 +1,36 @@ +import voluptuous as vol + +from esphomeyaml.components import binary_sensor, sensor +from esphomeyaml.components.apds9960 import APDS9960, CONF_APDS9960_ID +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_DIRECTION, CONF_NAME +from esphomeyaml.cpp_generator import get_variable + +DEPENDENCIES = ['apds9960'] +APDS9960GestureDirectionBinarySensor = sensor.sensor_ns.class_( + 'APDS9960GestureDirectionBinarySensor', binary_sensor.BinarySensor) + +DIRECTIONS = { + 'UP': 'make_up_direction', + 'DOWN': 'make_down_direction', + 'LEFT': 'make_left_direction', + 'RIGHT': 'make_right_direction', +} + +PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(APDS9960GestureDirectionBinarySensor), + vol.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), + cv.GenerateID(CONF_APDS9960_ID): cv.use_variable_id(APDS9960) +})) + + +def to_code(config): + for hub in get_variable(config[CONF_APDS9960_ID]): + yield + func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]]) + rhs = func(config[CONF_NAME]) + 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/custom.py b/esphomeyaml/components/binary_sensor/custom.py new file mode 100644 index 0000000000..c170793b99 --- /dev/null +++ b/esphomeyaml/components/binary_sensor/custom.py @@ -0,0 +1,37 @@ +import voluptuous as vol + +from esphomeyaml.components import binary_sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_types import std_vector + +CustomBinarySensorConstructor = binary_sensor.binary_sensor_ns.class_( + 'CustomBinarySensorConstructor') + +PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomBinarySensorConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_BINARY_SENSORS): + cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(binary_sensor.BinarySensor), + })), +}) + + +def to_code(config): + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(binary_sensor.BinarySensorPtr)): + yield + + rhs = CustomBinarySensorConstructor(template_) + custom = variable(config[CONF_ID], rhs) + for i, sens in enumerate(config[CONF_BINARY_SENSORS]): + binary_sensor.register_binary_sensor(custom.get_binary_sensor(i), sens) + + +BUILD_FLAGS = '-DUSE_CUSTOM_BINARY_SENSOR' + + +def to_hass_config(data, config): + return [binary_sensor.core_to_hass_config(data, sens) for sens in config[CONF_BINARY_SENSORS]] diff --git a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py index 63e08a0baf..25c389e2cd 100644 --- a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py +++ b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py @@ -5,7 +5,8 @@ from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLE make_address_array import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME -from esphomeyaml.helpers import esphomelib_ns, get_variable +from esphomeyaml.cpp_generator import get_variable +from esphomeyaml.cpp_types import esphomelib_ns DEPENDENCIES = ['esp32_ble_tracker'] ESP32BLEPresenceDevice = esphomelib_ns.class_('ESP32BLEPresenceDevice', binary_sensor.BinarySensor) diff --git a/esphomeyaml/components/binary_sensor/esp32_touch.py b/esphomeyaml/components/binary_sensor/esp32_touch.py index fcf01e664c..6c90846276 100644 --- a/esphomeyaml/components/binary_sensor/esp32_touch.py +++ b/esphomeyaml/components/binary_sensor/esp32_touch.py @@ -4,7 +4,8 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.components.esp32_touch import ESP32TouchComponent from esphomeyaml.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import get_variable, global_ns +from esphomeyaml.cpp_generator import get_variable +from esphomeyaml.cpp_types import global_ns from esphomeyaml.pins import validate_gpio_pin ESP_PLATFORMS = [ESP_PLATFORM_ESP32] diff --git a/esphomeyaml/components/binary_sensor/gpio.py b/esphomeyaml/components/binary_sensor/gpio.py index 80a6d2b487..feb470d299 100644 --- a/esphomeyaml/components/binary_sensor/gpio.py +++ b/esphomeyaml/components/binary_sensor/gpio.py @@ -4,8 +4,9 @@ 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, \ - setup_component, Component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import Application, Component, App MakeGPIOBinarySensor = Application.struct('MakeGPIOBinarySensor') GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent', diff --git a/esphomeyaml/components/binary_sensor/nextion.py b/esphomeyaml/components/binary_sensor/nextion.py index 162392d223..ff4e9d34d2 100644 --- a/esphomeyaml/components/binary_sensor/nextion.py +++ b/esphomeyaml/components/binary_sensor/nextion.py @@ -4,7 +4,7 @@ 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 +from esphomeyaml.cpp_generator import get_variable DEPENDENCIES = ['display'] @@ -22,7 +22,6 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend def to_code(config): - hub = None for hub in get_variable(config[CONF_NEXTION_ID]): yield rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID], diff --git a/esphomeyaml/components/binary_sensor/pn532.py b/esphomeyaml/components/binary_sensor/pn532.py index 38031b650c..f0681ff6d7 100644 --- a/esphomeyaml/components/binary_sensor/pn532.py +++ b/esphomeyaml/components/binary_sensor/pn532.py @@ -5,7 +5,7 @@ from esphomeyaml.components import binary_sensor from esphomeyaml.components.pn532 import PN532Component from esphomeyaml.const import CONF_NAME, CONF_UID from esphomeyaml.core import HexInt -from esphomeyaml.helpers import ArrayInitializer, get_variable +from esphomeyaml.cpp_generator import get_variable, ArrayInitializer DEPENDENCIES = ['pn532'] diff --git a/esphomeyaml/components/binary_sensor/rdm6300.py b/esphomeyaml/components/binary_sensor/rdm6300.py index c4a11e4f31..24aa69f0fa 100644 --- a/esphomeyaml/components/binary_sensor/rdm6300.py +++ b/esphomeyaml/components/binary_sensor/rdm6300.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor, rdm6300 from esphomeyaml.const import CONF_NAME, CONF_UID -from esphomeyaml.helpers import get_variable +from esphomeyaml.cpp_generator import get_variable DEPENDENCIES = ['rdm6300'] diff --git a/esphomeyaml/components/binary_sensor/remote_receiver.py b/esphomeyaml/components/binary_sensor/remote_receiver.py index 4d66caca44..f189a2225b 100644 --- a/esphomeyaml/components/binary_sensor/remote_receiver.py +++ b/esphomeyaml/components/binary_sensor/remote_receiver.py @@ -11,7 +11,7 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAN 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_SAMSUNG, CONF_SONY, \ CONF_STATE -from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable +from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, Pvariable DEPENDENCIES = ['remote_receiver'] @@ -39,7 +39,7 @@ 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)), + vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), }), vol.Optional(CONF_NEC): vol.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, @@ -50,7 +50,7 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend }), vol.Optional(CONF_SONY): vol.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, - vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)), + vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), }), vol.Optional(CONF_PANASONIC): vol.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, @@ -73,40 +73,40 @@ def receiver_base(full_config): key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) if key == CONF_LG: return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_NEC: + if key == CONF_NEC: return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_PANASONIC: + if key == CONF_PANASONIC: return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_SAMSUNG: + if key == CONF_SAMSUNG: return SamsungReceiver.new(name, config[CONF_DATA]) - elif key == CONF_SONY: + if key == CONF_SONY: return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_RAW: + if key == CONF_RAW: data = ArrayInitializer(*config, multiline=False) return RawReceiver.new(name, data) - elif key == CONF_RC_SWITCH_RAW: + if key == CONF_RC_SWITCH_RAW: return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_CODE]), len(config[CONF_CODE])) - elif key == CONF_RC_SWITCH_TYPE_A: + if key == CONF_RC_SWITCH_TYPE_A: return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_GROUP]), binary_code(config[CONF_DEVICE]), config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_B: + if key == CONF_RC_SWITCH_TYPE_B: return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_C: + if key == CONF_RC_SWITCH_TYPE_C: return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_FAMILY][0]) - ord('a'), config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_D: + if key == CONF_RC_SWITCH_TYPE_D: return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_GROUP][0]) - ord('a'), config[CONF_DEVICE], config[CONF_STATE]) - else: - raise NotImplementedError("Unknown receiver type {}".format(config)) + + raise NotImplementedError("Unknown receiver type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/binary_sensor/status.py b/esphomeyaml/components/binary_sensor/status.py index 5fca06cf93..93e11dd24b 100644 --- a/esphomeyaml/components/binary_sensor/status.py +++ b/esphomeyaml/components/binary_sensor/status.py @@ -1,9 +1,10 @@ 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, setup_component, Component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, Component, App -DEPENDENCIES = ['mqtt'] MakeStatusBinarySensor = Application.struct('MakeStatusBinarySensor') StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor', diff --git a/esphomeyaml/components/binary_sensor/template.py b/esphomeyaml/components/binary_sensor/template.py index 3369b50aab..3078d56779 100644 --- a/esphomeyaml/components/binary_sensor/template.py +++ b/esphomeyaml/components/binary_sensor/template.py @@ -3,8 +3,9 @@ import voluptuous as vol 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, add, bool_, optional, process_lambda, variable, \ - setup_component, Component +from esphomeyaml.cpp_generator import variable, process_lambda, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, Component, App, optional, bool_ MakeTemplateBinarySensor = Application.struct('MakeTemplateBinarySensor') TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor', diff --git a/esphomeyaml/components/cover/__init__.py b/esphomeyaml/components/cover/__init__.py index a121ce9b08..1ff00054e6 100644 --- a/esphomeyaml/components/cover/__init__.py +++ b/esphomeyaml/components/cover/__init__.py @@ -1,11 +1,12 @@ import voluptuous as vol -from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component 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, Action, Nameable +from esphomeyaml.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID +from esphomeyaml.cpp_generator import Pvariable, add, get_variable +from esphomeyaml.cpp_types import Action, Nameable, esphomelib_ns PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -54,8 +55,7 @@ COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA) -def cover_open_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def cover_open_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_open_action(template_arg) @@ -70,8 +70,7 @@ COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA) -def cover_close_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def cover_close_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_close_action(template_arg) @@ -86,8 +85,7 @@ COVER_STOP_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA) -def cover_stop_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def cover_stop_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_stop_action(template_arg) diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index 564c13ceb4..f29b7b5c91 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -5,8 +5,9 @@ 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, \ - setup_component +from esphomeyaml.cpp_generator import variable, process_lambda, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, App, optional, NoArg MakeTemplateCover = Application.struct('MakeTemplateCover') TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover) diff --git a/esphomeyaml/components/custom_component.py b/esphomeyaml/components/custom_component.py new file mode 100644 index 0000000000..da85785fb6 --- /dev/null +++ b/esphomeyaml/components/custom_component.py @@ -0,0 +1,32 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_COMPONENTS +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Component, ComponentPtr, esphomelib_ns, std_vector + +CustomComponentConstructor = esphomelib_ns.class_('CustomComponentConstructor') +MULTI_CONF = True + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(CustomComponentConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Optional(CONF_COMPONENTS): cv.ensure_list(vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(Component) + }).extend(cv.COMPONENT_SCHEMA.schema)), +}) + + +def to_code(config): + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(ComponentPtr)): + yield + + rhs = CustomComponentConstructor(template_) + custom = variable(config[CONF_ID], rhs) + for i, comp in enumerate(config.get(CONF_COMPONENTS, [])): + setup_component(custom.get_component(i), comp) + + +BUILD_FLAGS = '-DUSE_CUSTOM_COMPONENT' diff --git a/esphomeyaml/components/dallas.py b/esphomeyaml/components/dallas.py index eae9fa9c82..5fc99f8288 100644 --- a/esphomeyaml/components/dallas.py +++ b/esphomeyaml/components/dallas.py @@ -1,25 +1,27 @@ 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_ID, CONF_PIN, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Pvariable, setup_component, PollingComponent +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DallasComponent = sensor.sensor_ns.class_('DallasComponent', PollingComponent) +MULTI_CONF = True -CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(DallasComponent), - vol.Required(CONF_PIN): pins.input_output_pin, + vol.Required(CONF_PIN): pins.input_pullup_pin, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA.schema)]) +}).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)) - var = Pvariable(conf[CONF_ID], rhs) - setup_component(var, conf) + rhs = App.make_dallas_component(config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL)) + var = Pvariable(config[CONF_ID], rhs) + setup_component(var, config) BUILD_FLAGS = '-DUSE_DALLAS_SENSOR' diff --git a/esphomeyaml/components/debug.py b/esphomeyaml/components/debug.py index 533ffb8114..5751f1517f 100644 --- a/esphomeyaml/components/debug.py +++ b/esphomeyaml/components/debug.py @@ -1,6 +1,7 @@ import voluptuous as vol -from esphomeyaml.helpers import App, add +from esphomeyaml.cpp_generator import add +from esphomeyaml.cpp_types import App DEPENDENCIES = ['logger'] diff --git a/esphomeyaml/components/deep_sleep.py b/esphomeyaml/components/deep_sleep.py index 4b5f57c2f5..7019c4d718 100644 --- a/esphomeyaml/components/deep_sleep.py +++ b/esphomeyaml/components/deep_sleep.py @@ -2,11 +2,11 @@ import voluptuous as vol from esphomeyaml import config_validation as cv, pins 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, 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 +from esphomeyaml.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \ + CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN +from esphomeyaml.cpp_generator import Pvariable, StructInitializer, add, get_variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import Action, App, Component, esphomelib_ns, global_ns def validate_pin_number(value): @@ -43,12 +43,11 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_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_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, + cv.one_of(*WAKEUP_PIN_MODES), upper=True), 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.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number), + vol.Required(CONF_MODE): cv.one_of(*EXT1_WAKEUP_MODES, upper=True), })), vol.Optional(CONF_RUN_CYCLES): cv.positive_int, vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, @@ -95,8 +94,7 @@ DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_DEEP_SLEEP_ENTER, DEEP_SLEEP_ENTER_ACTION_SCHEMA) -def deep_sleep_enter_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def deep_sleep_enter_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_enter_deep_sleep_action(template_arg) @@ -111,8 +109,7 @@ DEEP_SLEEP_PREVENT_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_DEEP_SLEEP_PREVENT, DEEP_SLEEP_PREVENT_ACTION_SCHEMA) -def deep_sleep_prevent_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def deep_sleep_prevent_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_prevent_deep_sleep_action(template_arg) diff --git a/esphomeyaml/components/display/__init__.py b/esphomeyaml/components/display/__init__.py index ffcaabecb6..db232551a4 100644 --- a/esphomeyaml/components/display/__init__.py +++ b/esphomeyaml/components/display/__init__.py @@ -3,7 +3,9 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import add, add_job, esphomelib_ns +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import add +from esphomeyaml.cpp_types import esphomelib_ns PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -50,7 +52,7 @@ def setup_display_core_(display_var, config): def setup_display(display_var, config): - add_job(setup_display_core_, display_var, config) + CORE.add_job(setup_display_core_, display_var, config) BUILD_FLAGS = '-DUSE_DISPLAY' diff --git a/esphomeyaml/components/display/lcd_gpio.py b/esphomeyaml/components/display/lcd_gpio.py index d36ac780e8..c796ed3f28 100644 --- a/esphomeyaml/components/display/lcd_gpio.py +++ b/esphomeyaml/components/display/lcd_gpio.py @@ -1,12 +1,13 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import display +import esphomeyaml.config_validation as cv 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, \ - setup_component, PollingComponent +from esphomeyaml.cpp_generator import Pvariable, add, process_lambda +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, PollingComponent, void LCDDisplay = display.display_ns.class_('LCDDisplay', PollingComponent) LCDDisplayRef = LCDDisplay.operator('ref') @@ -63,7 +64,8 @@ def to_code(config): add(lcd.set_rw_pin(rw)) if CONF_LAMBDA in config: - for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')], + return_type=void): yield add(lcd.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/lcd_pcf8574.py b/esphomeyaml/components/display/lcd_pcf8574.py index 761203c212..cc3792f992 100644 --- a/esphomeyaml/components/display/lcd_pcf8574.py +++ b/esphomeyaml/components/display/lcd_pcf8574.py @@ -1,11 +1,13 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import display, i2c -from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions, \ - LCDDisplay +from esphomeyaml.components.display.lcd_gpio import LCDDisplay, LCDDisplayRef, \ + validate_lcd_dimensions +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA -from esphomeyaml.helpers import App, Pvariable, add, process_lambda, setup_component +from esphomeyaml.cpp_generator import Pvariable, add, process_lambda +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, void DEPENDENCIES = ['i2c'] @@ -26,7 +28,8 @@ def to_code(config): add(lcd.set_address(config[CONF_ADDRESS])) if CONF_LAMBDA in config: - for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')], + return_type=void): yield add(lcd.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/max7219.py b/esphomeyaml/components/display/max7219.py index b5bf380c4d..fcca07154b 100644 --- a/esphomeyaml/components/display/max7219.py +++ b/esphomeyaml/components/display/max7219.py @@ -1,13 +1,14 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import display, spi from esphomeyaml.components.spi import SPIComponent +import esphomeyaml.config_validation as cv 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, setup_component, PollingComponent +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, PollingComponent, void DEPENDENCIES = ['spi'] @@ -38,7 +39,8 @@ def to_code(config): add(max7219.set_intensity(config[CONF_INTENSITY])) if CONF_LAMBDA in config: - for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')], + return_type=void): yield add(max7219.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/nextion.py b/esphomeyaml/components/display/nextion.py index 9f26647f96..4d051c39b6 100644 --- a/esphomeyaml/components/display/nextion.py +++ b/esphomeyaml/components/display/nextion.py @@ -2,9 +2,9 @@ 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, PollingComponent, Pvariable, add, get_variable, \ - process_lambda, \ - setup_component +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent, void DEPENDENCIES = ['uart'] @@ -24,7 +24,8 @@ def to_code(config): nextion = Pvariable(config[CONF_ID], rhs) if CONF_LAMBDA in config: - for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')], + return_type=void): yield add(nextion.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/ssd1306_i2c.py b/esphomeyaml/components/display/ssd1306_i2c.py index a80ba8d5b5..966ecdc844 100644 --- a/esphomeyaml/components/display/ssd1306_i2c.py +++ b/esphomeyaml/components/display/ssd1306_i2c.py @@ -1,13 +1,14 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import display 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, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, CONF_LAMBDA, CONF_MODEL, \ + CONF_RESET_PIN +from esphomeyaml.cpp_generator import Pvariable, add, process_lambda +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, void DEPENDENCIES = ['i2c'] @@ -36,7 +37,7 @@ def to_code(config): add(ssd.set_address(config[CONF_ADDRESS])) if CONF_LAMBDA in config: for lambda_ in process_lambda(config[CONF_LAMBDA], - [(display.DisplayBufferRef, 'it')]): + [(display.DisplayBufferRef, 'it')], return_type=void): yield add(ssd.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/ssd1306_spi.py b/esphomeyaml/components/display/ssd1306_spi.py index bf47a5e92a..635ccde046 100644 --- a/esphomeyaml/components/display/ssd1306_spi.py +++ b/esphomeyaml/components/display/ssd1306_spi.py @@ -1,14 +1,14 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins 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, setup_component, PollingComponent +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, CONF_ID, CONF_LAMBDA, \ + CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, PollingComponent, void DEPENDENCIES = ['spi'] @@ -27,7 +27,7 @@ MODELS = { 'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48, } -SSD1306_MODEL = vol.All(vol.Upper, vol.Replace(' ', '_'), cv.one_of(*MODELS)) +SSD1306_MODEL = cv.one_of(*MODELS, upper=True, space="_") PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(SPISSD1306), @@ -60,7 +60,7 @@ def to_code(config): add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC])) if CONF_LAMBDA in config: for lambda_ in process_lambda(config[CONF_LAMBDA], - [(display.DisplayBufferRef, 'it')]): + [(display.DisplayBufferRef, 'it')], return_type=void): yield add(ssd.set_writer(lambda_)) diff --git a/esphomeyaml/components/display/waveshare_epaper.py b/esphomeyaml/components/display/waveshare_epaper.py index 51723440cf..bfca5bb3fd 100644 --- a/esphomeyaml/components/display/waveshare_epaper.py +++ b/esphomeyaml/components/display/waveshare_epaper.py @@ -6,8 +6,10 @@ 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, setup_component, PollingComponent +from esphomeyaml.cpp_generator import get_variable, Pvariable, process_lambda, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, gpio_input_pin_expression, \ + setup_component +from esphomeyaml.cpp_types import PollingComponent, App, void DEPENDENCIES = ['spi'] @@ -43,7 +45,7 @@ PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ 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, - vol.Required(CONF_MODEL): vol.All(vol.Lower, cv.one_of(*MODELS)), + vol.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), 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, @@ -69,7 +71,8 @@ def to_code(config): raise NotImplementedError() if CONF_LAMBDA in config: - for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], + return_type=void): yield add(epaper.set_writer(lambda_)) if CONF_RESET_PIN in config: diff --git a/esphomeyaml/components/esp32_ble_beacon.py b/esphomeyaml/components/esp32_ble_beacon.py index 2d9e8d37a4..326314756f 100644 --- a/esphomeyaml/components/esp32_ble_beacon.py +++ b/esphomeyaml/components/esp32_ble_beacon.py @@ -2,8 +2,9 @@ import voluptuous as vol from esphomeyaml import config_validation as cv 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 +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, RawExpression, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns ESP_PLATFORMS = [ESP_PLATFORM_ESP32] @@ -14,7 +15,7 @@ CONF_MINOR = 'minor' CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon), - vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of('IBEACON')), + vol.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True), vol.Required(CONF_UUID): cv.uuid, vol.Optional(CONF_MAJOR): cv.uint16_t, vol.Optional(CONF_MINOR): cv.uint16_t, diff --git a/esphomeyaml/components/esp32_ble_tracker.py b/esphomeyaml/components/esp32_ble_tracker.py index 40be12590a..00f8834e41 100644 --- a/esphomeyaml/components/esp32_ble_tracker.py +++ b/esphomeyaml/components/esp32_ble_tracker.py @@ -4,8 +4,9 @@ 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, \ - setup_component, Component +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns ESP_PLATFORMS = [ESP_PLATFORM_ESP32] diff --git a/esphomeyaml/components/esp32_touch.py b/esphomeyaml/components/esp32_touch.py index 0591e1e92f..e6eb33e6e0 100644 --- a/esphomeyaml/components/esp32_touch.py +++ b/esphomeyaml/components/esp32_touch.py @@ -2,11 +2,13 @@ import voluptuous as vol from esphomeyaml import config_validation as cv from esphomeyaml.components import binary_sensor -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.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \ + CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \ + CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32 from esphomeyaml.core import TimePeriod -from esphomeyaml.helpers import App, Pvariable, add, global_ns, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, global_ns ESP_PLATFORMS = [ESP_PLATFORM_ESP32] @@ -19,6 +21,7 @@ def validate_voltage(values): if not value.endswith('V'): value += 'V' return cv.one_of(*values)(value) + return validator diff --git a/esphomeyaml/components/ethernet.py b/esphomeyaml/components/ethernet.py new file mode 100644 index 0000000000..51ff308bbc --- /dev/null +++ b/esphomeyaml/components/ethernet.py @@ -0,0 +1,73 @@ +import voluptuous as vol + +from esphomeyaml import pins +from esphomeyaml.components import wifi +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_DOMAIN, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_TYPE, \ + ESP_PLATFORM_ESP32 +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression +from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns + +CONFLICTS_WITH = ['wifi'] +ESP_PLATFORMS = [ESP_PLATFORM_ESP32] + +CONF_PHY_ADDR = 'phy_addr' +CONF_MDC_PIN = 'mdc_pin' +CONF_MDIO_PIN = 'mdio_pin' +CONF_CLK_MODE = 'clk_mode' +CONF_POWER_PIN = 'power_pin' + +EthernetType = esphomelib_ns.enum('EthernetType') +ETHERNET_TYPES = { + 'LAN8720': EthernetType.ETHERNET_TYPE_LAN8720, + 'TLK110': EthernetType.ETHERNET_TYPE_TLK110, +} + +eth_clock_mode_t = global_ns.enum('eth_clock_mode_t') +CLK_MODES = { + 'GPIO0_IN': eth_clock_mode_t.ETH_CLOCK_GPIO0_IN, + 'GPIO0_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT, + 'GPIO16_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT, + 'GPIO17_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT, +} + +EthernetComponent = esphomelib_ns.class_('EthernetComponent', Component) + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(EthernetComponent), + vol.Required(CONF_TYPE): cv.one_of(*ETHERNET_TYPES, upper=True), + vol.Required(CONF_MDC_PIN): pins.output_pin, + vol.Required(CONF_MDIO_PIN): pins.input_output_pin, + vol.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.one_of(*CLK_MODES, upper=True, space='_'), + vol.Optional(CONF_PHY_ADDR, default=0): vol.All(cv.int_, vol.Range(min=0, max=31)), + vol.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, + vol.Optional(CONF_MANUAL_IP): wifi.STA_MANUAL_IP_SCHEMA, + vol.Optional(CONF_HOSTNAME): cv.hostname, + vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, +}) + + +def to_code(config): + rhs = App.init_ethernet() + eth = Pvariable(config[CONF_ID], rhs) + + add(eth.set_phy_addr(config[CONF_PHY_ADDR])) + add(eth.set_mdc_pin(config[CONF_MDC_PIN])) + add(eth.set_mdio_pin(config[CONF_MDIO_PIN])) + add(eth.set_type(ETHERNET_TYPES[config[CONF_TYPE]])) + add(eth.set_clk_mode(CLK_MODES[config[CONF_CLK_MODE]])) + + if CONF_POWER_PIN in config: + for pin in gpio_output_pin_expression(config[CONF_POWER_PIN]): + yield + add(eth.set_power_pin(pin)) + + if CONF_HOSTNAME in config: + add(eth.set_hostname(config[CONF_HOSTNAME])) + + if CONF_MANUAL_IP in config: + add(eth.set_manual_ip(wifi.manual_ip(config[CONF_MANUAL_IP]))) + + +REQUIRED_BUILD_FLAGS = '-DUSE_ETHERNET' diff --git a/esphomeyaml/components/fan/__init__.py b/esphomeyaml/components/fan/__init__.py index 9874c4114f..cc7ac78c09 100644 --- a/esphomeyaml/components/fan/__init__.py +++ b/esphomeyaml/components/fan/__init__.py @@ -1,13 +1,14 @@ import voluptuous as vol -from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component 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_OSCILLATION_OUTPUT, CONF_NAME -from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ - TemplateArguments, get_variable, templatable, bool_, Action, Nameable, Component +from esphomeyaml.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_NAME, CONF_OSCILLATING, \ + CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_OUTPUT, CONF_OSCILLATION_STATE_TOPIC, \ + CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC +from esphomeyaml.cpp_generator import add, Pvariable, get_variable, templatable +from esphomeyaml.cpp_types import Application, Component, Nameable, esphomelib_ns, Action, bool_ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -32,13 +33,14 @@ FAN_SPEED_HIGH = FanSpeed.FAN_SPEED_HIGH FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(FanState), cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent), - vol.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.publish_topic, - vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.subscribe_topic, + vol.Optional(CONF_OSCILLATION_STATE_TOPIC): vol.All(cv.requires_component('mqtt'), + cv.publish_topic), + vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): vol.All(cv.requires_component('mqtt'), + cv.subscribe_topic), }) FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema) - FAN_SPEEDS = { 'OFF': FAN_SPEED_OFF, 'LOW': FAN_SPEED_LOW, @@ -47,10 +49,6 @@ FAN_SPEEDS = { } -def validate_fan_speed(value): - return vol.All(vol.Upper, cv.one_of(*FAN_SPEEDS))(value) - - def setup_fan_core_(fan_var, mqtt_var, config): if CONF_INTERNAL in config: add(fan_var.set_internal(config[CONF_INTERNAL])) @@ -74,7 +72,6 @@ def setup_fan(fan_obj, mqtt_obj, config): BUILD_FLAGS = '-DUSE_FAN' - CONF_FAN_TOGGLE = 'fan.toggle' FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(FanState), @@ -82,8 +79,7 @@ FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA) -def fan_toggle_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def fan_toggle_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) @@ -98,8 +94,7 @@ FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA) -def fan_turn_off_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def fan_turn_off_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) @@ -111,13 +106,12 @@ CONF_FAN_TURN_ON = 'fan.turn_on' FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ 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), + vol.Optional(CONF_SPEED): cv.templatable(cv.one_of(*FAN_SPEEDS, upper=True)), }) @ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA) -def fan_turn_on_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def fan_turn_on_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) diff --git a/esphomeyaml/components/fan/binary.py b/esphomeyaml/components/fan/binary.py index 1ce51c2a0b..f4dd31b2f3 100644 --- a/esphomeyaml/components/fan/binary.py +++ b/esphomeyaml/components/fan/binary.py @@ -3,7 +3,9 @@ import voluptuous as vol import esphomeyaml.config_validation as cv 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, setup_component +from esphomeyaml.cpp_generator import get_variable, variable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), diff --git a/esphomeyaml/components/fan/speed.py b/esphomeyaml/components/fan/speed.py index d1259d3ae1..8ad8944fc6 100644 --- a/esphomeyaml/components/fan/speed.py +++ b/esphomeyaml/components/fan/speed.py @@ -5,7 +5,8 @@ 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 -from esphomeyaml.helpers import App, add, get_variable, variable +from esphomeyaml.cpp_generator import get_variable, variable, add +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), diff --git a/esphomeyaml/components/font.py b/esphomeyaml/components/font.py index 1456c3e717..c69ffbd46e 100644 --- a/esphomeyaml/components/font.py +++ b/esphomeyaml/components/font.py @@ -1,15 +1,17 @@ # coding=utf-8 import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import core from esphomeyaml.components import display +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE -from esphomeyaml.core import HexInt -from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \ - relative_path +from esphomeyaml.core import CORE, HexInt +from esphomeyaml.cpp_generator import ArrayInitializer, MockObj, Pvariable, RawExpression, add +from esphomeyaml.cpp_types import App +from esphomeyaml.py_compat import sort_by_cmp DEPENDENCIES = ['display'] +MULTI_CONF = True Font = display.display_ns.class_('Font') Glyph = display.display_ns.class_('Glyph') @@ -32,12 +34,11 @@ def validate_glyphs(value): if len(x_) < len(y_): return -1 - elif len(x_) > len(y_): + if len(x_) > len(y_): return 1 - else: - raise vol.Invalid(u"Found duplicate glyph {}".format(x)) + raise vol.Invalid(u"Found duplicate glyph {}".format(x)) - value.sort(cmp=comparator) + sort_by_cmp(value, comparator) return value @@ -46,11 +47,11 @@ def validate_pillow_installed(value): import PIL except ImportError: raise vol.Invalid("Please install the pillow python package to use this feature. " - "(pip2 install pillow)") + "(pip install pillow)") if PIL.__version__[0] < '4': raise vol.Invalid("Please update your pillow installation to at least 4.0.x. " - "(pip2 install -U pillow)") + "(pip install -U pillow)") return value @@ -76,46 +77,45 @@ FONT_SCHEMA = vol.Schema({ cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None), }) -CONFIG_SCHEMA = vol.All(validate_pillow_installed, cv.ensure_list, [FONT_SCHEMA]) +CONFIG_SCHEMA = vol.All(validate_pillow_installed, FONT_SCHEMA) def to_code(config): from PIL import ImageFont - for conf in config: - path = relative_path(conf[CONF_FILE]) - try: - font = ImageFont.truetype(path, conf[CONF_SIZE]) - except Exception as e: - raise core.ESPHomeYAMLError(u"Could not load truetype file {}: {}".format(path, e)) + path = CORE.relative_path(config[CONF_FILE]) + try: + font = ImageFont.truetype(path, config[CONF_SIZE]) + except Exception as e: + raise core.EsphomeyamlError(u"Could not load truetype file {}: {}".format(path, e)) - ascent, descent = font.getmetrics() + ascent, descent = font.getmetrics() - glyph_args = {} - data = [] - for glyph in conf[CONF_GLYPHS]: - mask = font.getmask(glyph, mode='1') - _, (offset_x, offset_y) = font.font.getsize(glyph) - width, height = mask.size - width8 = ((width + 7) // 8) * 8 - glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812 - for y in range(height): - for x in range(width): - if not mask.getpixel((x, y)): - continue - pos = x + y * width8 - glyph_data[pos // 8] |= 0x80 >> (pos % 8) - glyph_args[glyph] = (len(data), offset_x, offset_y, width, height) - data += glyph_data + glyph_args = {} + data = [] + for glyph in config[CONF_GLYPHS]: + mask = font.getmask(glyph, mode='1') + _, (offset_x, offset_y) = font.font.getsize(glyph) + width, height = mask.size + width8 = ((width + 7) // 8) * 8 + glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812 + for y in range(height): + for x in range(width): + if not mask.getpixel((x, y)): + continue + pos = x + y * width8 + glyph_data[pos // 8] |= 0x80 >> (pos % 8) + glyph_args[glyph] = (len(data), offset_x, offset_y, width, height) + data += glyph_data - raw_data = MockObj(conf[CONF_RAW_DATA_ID]) - add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( - raw_data, len(data), - ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) + raw_data = MockObj(config[CONF_RAW_DATA_ID]) + add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( + raw_data, len(data), + ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) - glyphs = [] - for glyph in conf[CONF_GLYPHS]: - glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph])) + glyphs = [] + for glyph in config[CONF_GLYPHS]: + glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph])) - rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent) - Pvariable(conf[CONF_ID], rhs) + rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent) + Pvariable(config[CONF_ID], rhs) diff --git a/esphomeyaml/components/globals.py b/esphomeyaml/components/globals.py index d9a6cf0978..03da1a11ec 100644 --- a/esphomeyaml/components/globals.py +++ b/esphomeyaml/components/globals.py @@ -2,34 +2,34 @@ 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 +from esphomeyaml.cpp_generator import Pvariable, RawExpression, TemplateArguments, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns GlobalVariableComponent = esphomelib_ns.class_('GlobalVariableComponent', Component) -GLOBAL_VAR_SCHEMA = vol.Schema({ +MULTI_CONF = True + +CONFIG_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) + type_ = RawExpression(config[CONF_TYPE]) + template_args = TemplateArguments(type_) + res_type = GlobalVariableComponent.template(template_args) + initial_value = None + if CONF_INITIAL_VALUE in config: + initial_value = RawExpression(config[CONF_INITIAL_VALUE]) + rhs = App.Pmake_global_variable(template_args, initial_value) + glob = Pvariable(config[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_)) + if config.get(CONF_RESTORE_VALUE, False): + hash_ = hash(config[CONF_ID].id) % 2**32 + add(glob.set_restore_value(hash_)) - setup_component(glob, conf) + setup_component(glob, config) diff --git a/esphomeyaml/components/i2c.py b/esphomeyaml/components/i2c.py index 133b3031c6..b5c216745a 100644 --- a/esphomeyaml/components/i2c.py +++ b/esphomeyaml/components/i2c.py @@ -1,18 +1,20 @@ import voluptuous as vol -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, setup_component, Component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_RECEIVE_TIMEOUT, CONF_SCAN, CONF_SCL, \ + CONF_SDA +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns I2CComponent = esphomelib_ns.class_('I2CComponent', Component) I2CDevice = pins.I2CDevice CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(I2CComponent), - vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin, - vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin, + vol.Optional(CONF_SDA, default='SDA'): pins.input_pullup_pin, + vol.Optional(CONF_SCL, default='SCL'): pins.input_pullup_pin, vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)), vol.Optional(CONF_SCAN): cv.boolean, diff --git a/esphomeyaml/components/image.py b/esphomeyaml/components/image.py index ac0c95be2d..adfeffe7d3 100644 --- a/esphomeyaml/components/image.py +++ b/esphomeyaml/components/image.py @@ -3,17 +3,18 @@ import logging import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import core from esphomeyaml.components import display, font +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_FILE, CONF_ID, CONF_RESIZE -from esphomeyaml.core import HexInt -from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \ - relative_path +from esphomeyaml.core import CORE, HexInt +from esphomeyaml.cpp_generator import ArrayInitializer, MockObj, Pvariable, RawExpression, add +from esphomeyaml.cpp_types import App _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['display'] +MULTI_CONF = True Image_ = display.display_ns.class_('Image') @@ -26,40 +27,39 @@ IMAGE_SCHEMA = vol.Schema({ cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None), }) -CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, cv.ensure_list, [IMAGE_SCHEMA]) +CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, IMAGE_SCHEMA) def to_code(config): from PIL import Image - for conf in config: - path = relative_path(conf[CONF_FILE]) - try: - image = Image.open(path) - except Exception as e: - raise core.ESPHomeYAMLError(u"Could not load image file {}: {}".format(path, e)) + path = CORE.relative_path(config[CONF_FILE]) + try: + image = Image.open(path) + except Exception as e: + raise core.EsphomeyamlError(u"Could not load image file {}: {}".format(path, e)) - if CONF_RESIZE in conf: - image.thumbnail(conf[CONF_RESIZE]) + if CONF_RESIZE in config: + image.thumbnail(config[CONF_RESIZE]) - image = image.convert('1', dither=Image.NONE) - width, height = image.size - if width > 500 or height > 500: - _LOGGER.warning("The image you requested is very big. Please consider using the resize " - "parameter") - width8 = ((width + 7) // 8) * 8 - data = [0 for _ in range(height * width8 // 8)] - for y in range(height): - for x in range(width): - if image.getpixel((x, y)): - continue - pos = x + y * width8 - data[pos // 8] |= 0x80 >> (pos % 8) + image = image.convert('1', dither=Image.NONE) + width, height = image.size + if width > 500 or height > 500: + _LOGGER.warning("The image you requested is very big. Please consider using the resize " + "parameter") + width8 = ((width + 7) // 8) * 8 + data = [0 for _ in range(height * width8 // 8)] + for y in range(height): + for x in range(width): + if image.getpixel((x, y)): + continue + pos = x + y * width8 + data[pos // 8] |= 0x80 >> (pos % 8) - raw_data = MockObj(conf[CONF_RAW_DATA_ID]) - add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( - raw_data, len(data), - ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) + raw_data = MockObj(config[CONF_RAW_DATA_ID]) + add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( + raw_data, len(data), + ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) - rhs = App.make_image(raw_data, width, height) - Pvariable(conf[CONF_ID], rhs) + rhs = App.make_image(raw_data, width, height) + Pvariable(config[CONF_ID], rhs) diff --git a/esphomeyaml/components/interval.py b/esphomeyaml/components/interval.py new file mode 100644 index 0000000000..e41656ebb0 --- /dev/null +++ b/esphomeyaml/components/interval.py @@ -0,0 +1,24 @@ +import voluptuous as vol + +from esphomeyaml import automation +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_INTERVAL +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, NoArg, PollingComponent, Trigger, esphomelib_ns + +IntervalTrigger = esphomelib_ns.class_('IntervalTrigger', Trigger.template(NoArg), PollingComponent) + +CONFIG_SCHEMA = automation.validate_automation(vol.Schema({ + vol.Required(CONF_ID): cv.declare_variable_id(IntervalTrigger), + vol.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds, +}).extend(cv.COMPONENT_SCHEMA.schema)) + + +def to_code(config): + for conf in config: + rhs = App.register_component(IntervalTrigger.new(config[CONF_INTERVAL])) + trigger = Pvariable(conf[CONF_ID], rhs) + setup_component(trigger, conf) + + automation.build_automation(trigger, NoArg, conf) diff --git a/esphomeyaml/components/light/__init__.py b/esphomeyaml/components/light/__init__.py index c74376b2fb..0997d1fd5b 100644 --- a/esphomeyaml/components/light/__init__.py +++ b/esphomeyaml/components/light/__init__.py @@ -1,7 +1,8 @@ import voluptuous as vol -from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component 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, \ @@ -9,10 +10,11 @@ from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLOR CONF_NUM_LEDS, CONF_RANDOM, CONF_RED, CONF_SPEED, CONF_STATE, CONF_TRANSITION_LENGTH, \ CONF_UPDATE_INTERVAL, CONF_WHITE, CONF_WIDTH, CONF_FLASH_LENGTH, CONF_COLOR_TEMPERATURE, \ 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, Nameable, Component, \ - Action +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import process_lambda, Pvariable, add, StructInitializer, \ + ArrayInitializer, get_variable, templatable +from esphomeyaml.cpp_types import esphomelib_ns, Application, Component, Nameable, Action, uint32, \ + float_, std_string, void PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -23,7 +25,8 @@ light_ns = esphomelib_ns.namespace('light') LightState = light_ns.class_('LightState', Nameable, Component) MakeLight = Application.struct('MakeLight') LightOutput = light_ns.class_('LightOutput') -FastLEDLightOutputComponent = light_ns.class_('FastLEDLightOutputComponent', LightOutput) +AddressableLight = light_ns.class_('AddressableLight') +AddressableLightRef = AddressableLight.operator('ref') # Actions ToggleAction = light_ns.class_('ToggleAction', Action) @@ -32,7 +35,6 @@ TurnOnAction = light_ns.class_('TurnOnAction', Action) LightColorValues = light_ns.class_('LightColorValues') - MQTTJSONLightComponent = light_ns.class_('MQTTJSONLightComponent', mqtt.MQTTComponent) # Effects @@ -42,28 +44,30 @@ 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) +AddressableLightEffect = light_ns.class_('AddressableLightEffect', LightEffect) +AddressableLambdaLightEffect = light_ns.class_('AddressableLambdaLightEffect', + AddressableLightEffect) +AddressableRainbowLightEffect = light_ns.class_('AddressableRainbowLightEffect', + AddressableLightEffect) +AddressableColorWipeEffect = light_ns.class_('AddressableColorWipeEffect', AddressableLightEffect) +AddressableColorWipeEffectColor = light_ns.struct('AddressableColorWipeEffectColor') +AddressableScanEffect = light_ns.class_('AddressableScanEffect', AddressableLightEffect) +AddressableTwinkleEffect = light_ns.class_('AddressableTwinkleEffect', AddressableLightEffect) +AddressableRandomTwinkleEffect = light_ns.class_('AddressableRandomTwinkleEffect', + AddressableLightEffect) +AddressableFireworksEffect = light_ns.class_('AddressableFireworksEffect', AddressableLightEffect) +AddressableFlickerEffect = light_ns.class_('AddressableFlickerEffect', AddressableLightEffect) CONF_STROBE = 'strobe' CONF_FLICKER = 'flicker' -CONF_FASTLED_LAMBDA = 'fastled_lambda' -CONF_FASTLED_RAINBOW = 'fastled_rainbow' -CONF_FASTLED_COLOR_WIPE = 'fastled_color_wipe' -CONF_FASTLED_SCAN = 'fastled_scan' -CONF_FASTLED_TWINKLE = 'fastled_twinkle' -CONF_FASTLED_RANDOM_TWINKLE = 'fastled_random_twinkle' -CONF_FASTLED_FIREWORKS = 'fastled_fireworks' -CONF_FASTLED_FLICKER = 'fastled_flicker' +CONF_ADDRESSABLE_LAMBDA = 'addressable_lambda' +CONF_ADDRESSABLE_RAINBOW = 'addressable_rainbow' +CONF_ADDRESSABLE_COLOR_WIPE = 'addressable_color_wipe' +CONF_ADDRESSABLE_SCAN = 'addressable_scan' +CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle' +CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle' +CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks' +CONF_ADDRESSABLE_FLICKER = 'addressable_flicker' CONF_ADD_LED_INTERVAL = 'add_led_interval' CONF_REVERSE = 'reverse' @@ -78,10 +82,10 @@ CONF_INTENSITY = 'intensity' BINARY_EFFECTS = [CONF_LAMBDA, CONF_STROBE] MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + [CONF_FLICKER] RGB_EFFECTS = MONOCHROMATIC_EFFECTS + [CONF_RANDOM] -FASTLED_EFFECTS = RGB_EFFECTS + [CONF_FASTLED_LAMBDA, CONF_FASTLED_RAINBOW, CONF_FASTLED_COLOR_WIPE, - CONF_FASTLED_SCAN, CONF_FASTLED_TWINKLE, - CONF_FASTLED_RANDOM_TWINKLE, CONF_FASTLED_FIREWORKS, - CONF_FASTLED_FLICKER] +ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_RAINBOW, + CONF_ADDRESSABLE_COLOR_WIPE, CONF_ADDRESSABLE_SCAN, + CONF_ADDRESSABLE_TWINKLE, CONF_ADDRESSABLE_RANDOM_TWINKLE, + CONF_ADDRESSABLE_FIREWORKS, CONF_ADDRESSABLE_FLICKER] EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_LAMBDA): vol.Schema({ @@ -98,7 +102,7 @@ EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_STROBE): vol.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(StrobeLightEffect), vol.Optional(CONF_NAME, default="Strobe"): cv.string, - vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.All(vol.Schema({ + vol.Optional(CONF_COLORS): vol.All(cv.ensure_list(vol.Schema({ vol.Optional(CONF_STATE, default=True): cv.boolean, vol.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, vol.Optional(CONF_RED, default=1.0): cv.percentage, @@ -107,7 +111,7 @@ EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_WHITE, default=1.0): cv.percentage, vol.Required(CONF_DURATION): cv.positive_time_period_milliseconds, }), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, - CONF_WHITE))], vol.Length(min=2)), + CONF_WHITE)), vol.Length(min=2)), }), vol.Optional(CONF_FLICKER): vol.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect), @@ -115,58 +119,59 @@ EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_ALPHA): cv.percentage, vol.Optional(CONF_INTENSITY): cv.percentage, }), - vol.Optional(CONF_FASTLED_LAMBDA): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_LAMBDA): vol.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_FASTLED_RAINBOW): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRainbowLightEffect), + vol.Optional(CONF_ADDRESSABLE_RAINBOW): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRainbowLightEffect), vol.Optional(CONF_NAME, default="Rainbow"): cv.string, vol.Optional(CONF_SPEED): cv.uint32_t, vol.Optional(CONF_WIDTH): cv.uint32_t, }), - vol.Optional(CONF_FASTLED_COLOR_WIPE): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDColorWipeEffect), + vol.Optional(CONF_ADDRESSABLE_COLOR_WIPE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableColorWipeEffect), vol.Optional(CONF_NAME, default="Color Wipe"): cv.string, - vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.Schema({ + vol.Optional(CONF_COLORS): cv.ensure_list({ vol.Optional(CONF_RED, default=1.0): cv.percentage, vol.Optional(CONF_GREEN, default=1.0): cv.percentage, vol.Optional(CONF_BLUE, default=1.0): cv.percentage, + vol.Optional(CONF_WHITE, default=1.0): cv.percentage, vol.Optional(CONF_RANDOM, default=False): cv.boolean, vol.Required(CONF_NUM_LEDS): vol.All(cv.uint32_t, vol.Range(min=1)), - })]), + }), vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds, vol.Optional(CONF_REVERSE): cv.boolean, }), - vol.Optional(CONF_FASTLED_SCAN): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDScanEffect), + vol.Optional(CONF_ADDRESSABLE_SCAN): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableScanEffect), vol.Optional(CONF_NAME, default="Scan"): cv.string, vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_FASTLED_TWINKLE): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDTwinkleEffect), + vol.Optional(CONF_ADDRESSABLE_TWINKLE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableTwinkleEffect), vol.Optional(CONF_NAME, default="Twinkle"): cv.string, vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_FASTLED_RANDOM_TWINKLE): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRandomTwinkleEffect), + vol.Optional(CONF_ADDRESSABLE_RANDOM_TWINKLE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRandomTwinkleEffect), vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string, vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_FASTLED_FIREWORKS): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFireworksEffect), + vol.Optional(CONF_ADDRESSABLE_FIREWORKS): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFireworksEffect), vol.Optional(CONF_NAME, default="Fireworks"): cv.string, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, vol.Optional(CONF_SPARK_PROBABILITY): cv.percentage, vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean, vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t, }), - vol.Optional(CONF_FASTLED_FLICKER): vol.Schema({ - cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFlickerEffect), - vol.Optional(CONF_NAME, default="FastLED Flicker"): cv.string, + vol.Optional(CONF_ADDRESSABLE_FLICKER): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFlickerEffect), + vol.Optional(CONF_NAME, default="Addressable Flicker"): cv.string, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, vol.Optional(CONF_INTENSITY): cv.percentage, }), @@ -175,28 +180,64 @@ EFFECTS_SCHEMA = vol.Schema({ def validate_effects(allowed_effects): def validator(value): - value = cv.ensure_list(value) + is_list = isinstance(value, list) + if not is_list: + value = [value] names = set() ret = [] + errors = [] for i, effect in enumerate(value): + path = [i] if is_list else [] if not isinstance(effect, dict): - raise vol.Invalid("Each effect must be a dictionary, not {}".format(type(value))) + errors.append( + vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)), + path) + ) + continue if len(effect) > 1: - raise vol.Invalid("Each entry in the 'effects:' option must be a single effect.") + errors.append( + vol.Invalid("Each entry in the 'effects:' option must be a single effect.", + path) + ) + continue if not effect: - raise vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i)) + errors.append( + vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i), + path) + ) + continue key = next(iter(effect.keys())) + if key.startswith('fastled'): + errors.append( + vol.Invalid("FastLED effects have been renamed to addressable effects. " + "Please use '{}'".format(key.replace('fastled', 'addressable')), + path) + ) + continue if key not in allowed_effects: - raise vol.Invalid("The effect '{}' does not exist or is not allowed for this " - "light type".format(key)) + errors.append( + vol.Invalid("The effect '{}' does not exist or is not allowed for this " + "light type".format(key), path) + ) + continue effect[key] = effect[key] or {} - conf = EFFECTS_SCHEMA(effect) + try: + conf = EFFECTS_SCHEMA(effect) + except vol.Invalid as err: + err.prepend(path) + errors.append(err) + continue name = conf[key][CONF_NAME] if name in names: - raise vol.Invalid(u"Found the effect name '{}' twice. All effects must have " - u"unique names".format(name)) + errors.append( + vol.Invalid(u"Found the effect name '{}' twice. All effects must have " + u"unique names".format(name), [i]) + ) + continue names.add(name) ret.append(conf) + if errors: + raise vol.MultipleInvalid(errors) return ret return validator @@ -214,7 +255,7 @@ def build_effect(full_config): key, config = next(iter(full_config.items())) if key == CONF_LAMBDA: lambda_ = None - for lambda_ in process_lambda(config[CONF_LAMBDA], []): + for lambda_ in process_lambda(config[CONF_LAMBDA], [], return_type=void): yield None yield LambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]) elif key == CONF_RANDOM: @@ -248,22 +289,22 @@ def build_effect(full_config): if CONF_INTENSITY in config: add(effect.set_intensity(config[CONF_INTENSITY])) yield effect - elif key == CONF_FASTLED_LAMBDA: - lambda_ = None - args = [(RawExpression('FastLEDLightOutputComponent &'), 'it')] - for lambda_ in process_lambda(config[CONF_LAMBDA], args): + elif key == CONF_ADDRESSABLE_LAMBDA: + args = [(AddressableLightRef, 'it')] + for lambda_ in process_lambda(config[CONF_LAMBDA], args, return_type=void): yield None - yield FastLEDLambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]) - elif key == CONF_FASTLED_RAINBOW: - rhs = FastLEDRainbowLightEffect.new(config[CONF_NAME]) + yield AddressableLambdaLightEffect.new(config[CONF_NAME], lambda_, + config[CONF_UPDATE_INTERVAL]) + elif key == CONF_ADDRESSABLE_RAINBOW: + rhs = AddressableRainbowLightEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_SPEED in config: add(effect.set_speed(config[CONF_SPEED])) if CONF_WIDTH in config: add(effect.set_width(config[CONF_WIDTH])) yield effect - elif key == CONF_FASTLED_COLOR_WIPE: - rhs = FastLEDColorWipeEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_COLOR_WIPE: + rhs = AddressableColorWipeEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_ADD_LED_INTERVAL in config: add(effect.set_add_led_interval(config[CONF_ADD_LED_INTERVAL])) @@ -272,40 +313,41 @@ def build_effect(full_config): colors = [] for color in config.get(CONF_COLORS, []): colors.append(StructInitializer( - FastLEDColorWipeEffectColor, - ('r', color[CONF_RED]), - ('g', color[CONF_GREEN]), - ('b', color[CONF_BLUE]), + AddressableColorWipeEffectColor, + ('r', int(round(color[CONF_RED] * 255))), + ('g', int(round(color[CONF_GREEN] * 255))), + ('b', int(round(color[CONF_BLUE] * 255))), + ('w', int(round(color[CONF_WHITE] * 255))), ('random', color[CONF_RANDOM]), ('num_leds', color[CONF_NUM_LEDS]), )) if colors: add(effect.set_colors(ArrayInitializer(*colors))) yield effect - elif key == CONF_FASTLED_SCAN: - rhs = FastLEDScanEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_SCAN: + rhs = AddressableScanEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_MOVE_INTERVAL in config: add(effect.set_move_interval(config[CONF_MOVE_INTERVAL])) yield effect - elif key == CONF_FASTLED_TWINKLE: - rhs = FastLEDTwinkleEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_TWINKLE: + rhs = AddressableTwinkleEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_TWINKLE_PROBABILITY in config: add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) if CONF_PROGRESS_INTERVAL in config: add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) yield effect - elif key == CONF_FASTLED_RANDOM_TWINKLE: - rhs = FastLEDRandomTwinkleEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_RANDOM_TWINKLE: + rhs = AddressableRandomTwinkleEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_TWINKLE_PROBABILITY in config: add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) if CONF_PROGRESS_INTERVAL in config: add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) yield effect - elif key == CONF_FASTLED_FIREWORKS: - rhs = FastLEDFireworksEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_FIREWORKS: + rhs = AddressableFireworksEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_UPDATE_INTERVAL in config: add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) @@ -316,8 +358,8 @@ def build_effect(full_config): if CONF_FADE_OUT_RATE in config: add(effect.set_spark_probability(config[CONF_FADE_OUT_RATE])) yield effect - elif key == CONF_FASTLED_FLICKER: - rhs = FastLEDFlickerEffect.new(config[CONF_NAME]) + elif key == CONF_ADDRESSABLE_FLICKER: + rhs = AddressableFlickerEffect.new(config[CONF_NAME]) effect = Pvariable(config[CONF_EFFECT_ID], rhs) if CONF_UPDATE_INTERVAL in config: add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) @@ -349,12 +391,11 @@ def setup_light_core_(light_var, mqtt_var, config): def setup_light(light_obj, mqtt_obj, config): light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False) mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False) - add_job(setup_light_core_, light_var, mqtt_var, config) + CORE.add_job(setup_light_core_, light_var, mqtt_var, config) BUILD_FLAGS = '-DUSE_LIGHT' - CONF_LIGHT_TOGGLE = 'light.toggle' LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(LightState), @@ -363,8 +404,7 @@ LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_LIGHT_TOGGLE, LIGHT_TOGGLE_ACTION_SCHEMA) -def light_toggle_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def light_toggle_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) @@ -385,8 +425,7 @@ LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_LIGHT_TURN_OFF, LIGHT_TURN_OFF_ACTION_SCHEMA) -def light_turn_off_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def light_turn_off_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) @@ -417,8 +456,7 @@ LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_LIGHT_TURN_ON, LIGHT_TURN_ON_ACTION_SCHEMA) -def light_turn_on_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def light_turn_on_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) @@ -465,10 +503,10 @@ def light_turn_on_to_code(config, action_id, arg_type): 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') + ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True) if ret is None: return None + ret['schema'] = 'json' if brightness: ret['brightness'] = True if rgb: diff --git a/esphomeyaml/components/light/binary.py b/esphomeyaml/components/light/binary.py index 55beec807a..2f705e1d80 100644 --- a/esphomeyaml/components/light/binary.py +++ b/esphomeyaml/components/light/binary.py @@ -3,7 +3,9 @@ import voluptuous as vol from esphomeyaml.components import light, output import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT -from esphomeyaml.helpers import App, get_variable, setup_component, variable +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), diff --git a/esphomeyaml/components/light/cwww.py b/esphomeyaml/components/light/cwww.py index d21139b5a0..c8df6ee203 100644 --- a/esphomeyaml/components/light/cwww.py +++ b/esphomeyaml/components/light/cwww.py @@ -7,7 +7,9 @@ from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \ 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, setup_component +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), diff --git a/esphomeyaml/components/light/fastled_clockless.py b/esphomeyaml/components/light/fastled_clockless.py index c779f4ceed..f1d12b6d7e 100644 --- a/esphomeyaml/components/light/fastled_clockless.py +++ b/esphomeyaml/components/light/fastled_clockless.py @@ -1,14 +1,15 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import light from esphomeyaml.components.power_supply import PowerSupplyComponent -from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ - 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, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CHIPSET, CONF_COLOR_CORRECT, CONF_DEFAULT_TRANSITION_LENGTH, \ + CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, \ + CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, CONF_RGB_ORDER +from esphomeyaml.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application TYPES = [ 'NEOPIXEL', @@ -58,18 +59,18 @@ MakeFastLEDLight = Application.struct('MakeFastLEDLight') PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), - vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*TYPES)), + vol.Required(CONF_CHIPSET): cv.one_of(*TYPES, upper=True), vol.Required(CONF_PIN): pins.output_pin, vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int, vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, - vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)), + vol.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)), 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), + vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS), }).extend(cv.COMPONENT_SCHEMA.schema), validate) @@ -103,6 +104,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' +LIB_DEPS = 'FastLED@3.2.0' + def to_hass_config(data, config): return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, diff --git a/esphomeyaml/components/light/fastled_spi.py b/esphomeyaml/components/light/fastled_spi.py index bd4df644e3..957130f247 100644 --- a/esphomeyaml/components/light/fastled_spi.py +++ b/esphomeyaml/components/light/fastled_spi.py @@ -1,14 +1,15 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import light from esphomeyaml.components.power_supply import PowerSupplyComponent -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, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_COLOR_CORRECT, CONF_DATA_PIN, \ + CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \ + CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER +from esphomeyaml.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application CHIPSETS = [ 'LPD8806', @@ -35,19 +36,19 @@ MakeFastLEDLight = Application.struct('MakeFastLEDLight') PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), - vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*CHIPSETS)), + vol.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), vol.Required(CONF_DATA_PIN): pins.output_pin, vol.Required(CONF_CLOCK_PIN): pins.output_pin, vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int, - vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)), + vol.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True), vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)), 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), + vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS), }).extend(cv.COMPONENT_SCHEMA.schema)) @@ -83,6 +84,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' +LIB_DEPS = 'FastLED@3.2.0' + def to_hass_config(data, config): return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, diff --git a/esphomeyaml/components/light/monochromatic.py b/esphomeyaml/components/light/monochromatic.py index bed9f65d37..0e9a1ccb48 100644 --- a/esphomeyaml/components/light/monochromatic.py +++ b/esphomeyaml/components/light/monochromatic.py @@ -4,7 +4,9 @@ from esphomeyaml.components import light, output import esphomeyaml.config_validation as cv 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 +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), diff --git a/esphomeyaml/components/light/neopixelbus.py b/esphomeyaml/components/light/neopixelbus.py new file mode 100644 index 0000000000..01841094e7 --- /dev/null +++ b/esphomeyaml/components/light/neopixelbus.py @@ -0,0 +1,170 @@ +import voluptuous as vol + +from esphomeyaml import pins +from esphomeyaml.components import light +from esphomeyaml.components.light import AddressableLight +from esphomeyaml.components.power_supply import PowerSupplyComponent +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CLOCK_PIN, CONF_COLOR_CORRECT, CONF_DATA_PIN, \ + CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_METHOD, \ + CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, CONF_TYPE, CONF_VARIANT +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import TemplateArguments, add, get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component, global_ns + +NeoPixelBusLightOutputBase = light.light_ns.class_('NeoPixelBusLightOutputBase', Component, + AddressableLight) +ESPNeoPixelOrder = light.light_ns.namespace('ESPNeoPixelOrder') + + +def validate_type(value): + value = cv.string(value).upper() + if 'R' not in value: + raise vol.Invalid("Must have R in type") + if 'G' not in value: + raise vol.Invalid("Must have G in type") + if 'B' not in value: + raise vol.Invalid("Must have B in type") + rest = set(value) - set('RGBW') + if rest: + raise vol.Invalid("Type has invalid color: {}".format(', '.join(rest))) + if len(set(value)) != len(value): + raise vol.Invalid("Type has duplicate color!") + return value + + +def validate_variant(value): + value = cv.string(value).upper() + if value == 'WS2813': + value = 'WS2812X' + if value == 'WS2812': + value = '800KBPS' + if value == 'LC8812': + value = 'SK6812' + return cv.one_of(*VARIANTS)(value) + + +def validate_method(value): + if value is None: + if CORE.is_esp32: + return 'ESP32_I2S_1' + if CORE.is_esp8266: + return 'ESP8266_DMA' + raise NotImplementedError + + if CORE.is_esp32: + return cv.one_of(*ESP32_METHODS, upper=True, space='_')(value) + if CORE.is_esp8266: + return cv.one_of(*ESP8266_METHODS, upper=True, space='_')(value) + raise NotImplementedError + + +VARIANTS = { + 'WS2812X': 'Ws2812x', + 'SK6812': 'Sk6812', + '800KBPS': '800Kbps', + '400KBPS': '400Kbps', +} + +ESP8266_METHODS = { + 'ESP8266_DMA': 'NeoEsp8266Dma{}Method', + 'ESP8266_UART0': 'NeoEsp8266Uart0{}Method', + 'ESP8266_UART1': 'NeoEsp8266Uart1{}Method', + 'ESP8266_ASYNC_UART0': 'NeoEsp8266AsyncUart0{}Method', + 'ESP8266_ASYNC_UART1': 'NeoEsp8266AsyncUart1{}Method', + 'BIT_BANG': 'NeoEsp8266BitBang{}Method', +} +ESP32_METHODS = { + 'ESP32_I2S_0': 'NeoEsp32I2s0{}Method', + 'ESP32_I2S_1': 'NeoEsp32I2s1{}Method', + 'BIT_BANG': 'NeoEsp32BitBang{}Method', +} + + +def format_method(config): + variant = VARIANTS[config[CONF_VARIANT]] + method = config[CONF_METHOD] + if CORE.is_esp8266: + return ESP8266_METHODS[method].format(variant) + if CORE.is_esp32: + return ESP32_METHODS[method].format(variant) + raise NotImplementedError + + +def validate(config): + if CONF_PIN in config: + if CONF_CLOCK_PIN in config or CONF_DATA_PIN in config: + raise vol.Invalid("Cannot specify both 'pin' and 'clock_pin'+'data_pin'") + return config + if CONF_CLOCK_PIN in config: + if CONF_DATA_PIN not in config: + raise vol.Invalid("If you give clock_pin, you must also specify data_pin") + return config + raise vol.Invalid("Must specify at least one of 'pin' or 'clock_pin'+'data_pin'") + + +MakeNeoPixelBusLight = Application.struct('MakeNeoPixelBusLight') + +PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeNeoPixelBusLight), + + vol.Optional(CONF_TYPE, default='GRB'): validate_type, + vol.Optional(CONF_VARIANT, default='800KBPS'): validate_variant, + vol.Optional(CONF_METHOD, default=None): validate_method, + vol.Optional(CONF_PIN): pins.output_pin, + vol.Optional(CONF_CLOCK_PIN): pins.output_pin, + vol.Optional(CONF_DATA_PIN): pins.output_pin, + + vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int, + + vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, + vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=4)), + 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.ADDRESSABLE_EFFECTS), +}).extend(cv.COMPONENT_SCHEMA.schema), validate) + + +def to_code(config): + type_ = config[CONF_TYPE] + has_white = 'W' in type_ + if has_white: + func = App.make_neo_pixel_bus_rgbw_light + color_feat = global_ns.NeoRgbwFeature + else: + func = App.make_neo_pixel_bus_rgb_light + color_feat = global_ns.NeoRgbFeature + + template = TemplateArguments(getattr(global_ns, format_method(config)), color_feat) + rhs = func(template, config[CONF_NAME]) + make = variable(config[CONF_MAKE_ID], rhs, type=MakeNeoPixelBusLight.template(template)) + output = make.Poutput + + if CONF_PIN in config: + add(output.add_leds(config[CONF_NUM_LEDS], config[CONF_PIN])) + else: + add(output.add_leds(config[CONF_NUM_LEDS], config[CONF_CLOCK_PIN], config[CONF_DATA_PIN])) + + add(output.set_pixel_order(getattr(ESPNeoPixelOrder, type_))) + + if CONF_POWER_SUPPLY in config: + for power_supply in get_variable(config[CONF_POWER_SUPPLY]): + yield + add(output.set_power_supply(power_supply)) + + if CONF_COLOR_CORRECT in config: + add(output.set_correction(*config[CONF_COLOR_CORRECT])) + + light.setup_light(make.Pstate, make.Pmqtt, config) + setup_component(output, config) + + +BUILD_FLAGS = '-DUSE_NEO_PIXEL_BUS_LIGHT' + +LIB_DEPS = 'NeoPixelBus@2.4.1' + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value='W' in config[CONF_TYPE]) diff --git a/esphomeyaml/components/light/rgb.py b/esphomeyaml/components/light/rgb.py index 556ba9b241..11dde11525 100644 --- a/esphomeyaml/components/light/rgb.py +++ b/esphomeyaml/components/light/rgb.py @@ -1,10 +1,12 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv 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, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \ + CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), diff --git a/esphomeyaml/components/light/rgbw.py b/esphomeyaml/components/light/rgbw.py index 07631ca71b..21d41536c8 100644 --- a/esphomeyaml/components/light/rgbw.py +++ b/esphomeyaml/components/light/rgbw.py @@ -1,10 +1,12 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv 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, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \ + CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), diff --git a/esphomeyaml/components/light/rgbww.py b/esphomeyaml/components/light/rgbww.py index fa7a92d6ff..a6a65116f8 100644 --- a/esphomeyaml/components/light/rgbww.py +++ b/esphomeyaml/components/light/rgbww.py @@ -1,11 +1,13 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import light, output +import esphomeyaml.config_validation as cv 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, setup_component +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App def validate_color_temperature(value): diff --git a/esphomeyaml/components/logger.py b/esphomeyaml/components/logger.py index 839d371d56..ad713a4c2d 100644 --- a/esphomeyaml/components/logger.py +++ b/esphomeyaml/components/logger.py @@ -6,9 +6,11 @@ from esphomeyaml.automation import ACTION_REGISTRY, LambdaAction 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, RawExpression, TemplateArguments, add, \ - esphomelib_ns, global_ns, process_lambda, statement, Component +from esphomeyaml.core import EsphomeyamlError, Lambda, CORE +from esphomeyaml.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement +from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns, void + +from esphomeyaml.py_compat import text_type LOG_LEVELS = { 'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE, @@ -32,14 +34,14 @@ LOG_LEVEL_TO_ESP_LOG = { LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE'] # pylint: disable=invalid-name -is_log_level = vol.All(vol.Upper, cv.one_of(*LOG_LEVELS)) +is_log_level = cv.one_of(*LOG_LEVELS, upper=True) def validate_local_no_higher_than_global(value): global_level = value.get(CONF_LEVEL, 'DEBUG') - for tag, level in value.get(CONF_LOGS, {}).iteritems(): + for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): - raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the " + raise EsphomeyamlError(u"The local log level {} for {} must be less severe than the " u"global log level {}.".format(level, tag, global_level)) return value @@ -64,14 +66,37 @@ def to_code(config): add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE])) if CONF_LEVEL in config: add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]])) - for tag, level in config.get(CONF_LOGS, {}).iteritems(): + for tag, level in config.get(CONF_LOGS, {}).items(): add(log.set_log_level(tag, LOG_LEVELS[level])) def required_build_flags(config): + flags = [] if CONF_LEVEL in config: - return u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]])) - return None + flags.append(u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]]))) + this_severity = LOG_LEVEL_SEVERITY.index(config[CONF_LEVEL]) + verbose_severity = LOG_LEVEL_SEVERITY.index('VERBOSE') + is_at_least_verbose = this_severity >= verbose_severity + has_serial_logging = config.get(CONF_BAUD_RATE) != 0 + if CORE.is_esp8266 and has_serial_logging and is_at_least_verbose: + flags.append(u"-DDEBUG_ESP_PORT=Serial") + flags.append(u"-DLWIP_DEBUG") + DEBUG_COMPONENTS = { + 'HTTP_CLIENT', + 'HTTP_SERVER', + 'HTTP_UPDATE', + 'OTA', + 'SSL', + 'TLS_MEM', + 'UPDATER', + 'WIFI', + } + for comp in DEBUG_COMPONENTS: + flags.append(u"-DDEBUG_ESP_{}".format(comp)) + if CORE.is_esp32 and is_at_least_verbose: + flags.append('-DCORE_DEBUG_LEVEL=5') + + return flags def maybe_simple_message(schema): @@ -97,7 +122,7 @@ def validate_printf(value): [cCdiouxXeEfgGaAnpsSZ] # type ) | # OR %%) # literal "%%" - """ + """ # noqa matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!" @@ -108,21 +133,20 @@ def validate_printf(value): CONF_LOGGER_LOG = 'logger.log' LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({ vol.Required(CONF_FORMAT): cv.string, - vol.Optional(CONF_ARGS, default=list): vol.All(cv.ensure_list, [cv.lambda_]), - vol.Optional(CONF_LEVEL, default="DEBUG"): vol.All(vol.Upper, cv.one_of(*LOG_LEVEL_TO_ESP_LOG)), + vol.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_), + vol.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(*LOG_LEVEL_TO_ESP_LOG, upper=True), vol.Optional(CONF_TAG, default="main"): cv.string, }), validate_printf) @ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA) -def logger_log_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def logger_log_action_to_code(config, action_id, arg_type, template_arg): esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]] - args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]] + args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]] - text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args))) + text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args))) - for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]): + for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')], return_type=void): yield None rhs = LambdaAction.new(template_arg, lambda_) type = LambdaAction.template(template_arg) diff --git a/esphomeyaml/components/mqtt.py b/esphomeyaml/components/mqtt.py index 646ddc0e73..f6289ea02a 100644 --- a/esphomeyaml/components/mqtt.py +++ b/esphomeyaml/components/mqtt.py @@ -7,17 +7,18 @@ from esphomeyaml import automation from esphomeyaml.automation import ACTION_REGISTRY from esphomeyaml.components import logger import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \ - 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_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, Component, Action, Trigger +from esphomeyaml.const import CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, \ + CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, \ + CONF_ESPHOMEYAML, CONF_ID, CONF_INTERNAL, CONF_KEEPALIVE, CONF_LEVEL, CONF_LOG_TOPIC, \ + CONF_MQTT, CONF_NAME, CONF_ON_JSON_MESSAGE, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, \ + CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PORT, CONF_QOS, CONF_REBOOT_TIMEOUT, \ + CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_STATE_TOPIC, CONF_TOPIC, \ + CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE +from esphomeyaml.core import EsphomeyamlError +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, RawExpression, \ + StructInitializer, TemplateArguments, add, process_lambda, templatable +from esphomeyaml.cpp_types import Action, App, Component, JsonObjectConstRef, JsonObjectRef, \ + Trigger, bool_, esphomelib_ns, optional, std_string, uint8, void def validate_message_just_topic(value): @@ -48,12 +49,14 @@ MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger', MQTTComponent = mqtt_ns.class_('MQTTComponent', Component) -def validate_broker(value): - value = cv.string_strict(value) - if u':' in value: - raise vol.Invalid(u"Please specify the port using the port: option") - if not value: - raise vol.Invalid(u"Broker cannot be empty") +def validate_config(value): + if CONF_PORT not in value: + parts = value[CONF_BROKER].split(u':') + if len(parts) == 2: + value[CONF_BROKER] = parts[0] + value[CONF_PORT] = cv.port(parts[1]) + else: + value[CONF_PORT] = 1883 return value @@ -64,14 +67,14 @@ def validate_fingerprint(value): return value -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent), - vol.Required(CONF_BROKER): validate_broker, - vol.Optional(CONF_PORT, default=1883): cv.port, + vol.Required(CONF_BROKER): cv.string_strict, + vol.Optional(CONF_PORT): cv.port, vol.Optional(CONF_USERNAME, default=''): cv.string, vol.Optional(CONF_PASSWORD, default=''): cv.string, vol.Optional(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(max=23)), - vol.Optional(CONF_DISCOVERY): cv.boolean, + vol.Optional(CONF_DISCOVERY): vol.Any(cv.boolean, cv.one_of("CLEAN", upper=True)), vol.Optional(CONF_DISCOVERY_RETAIN): cv.boolean, vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic, vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA, @@ -82,20 +85,21 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_LEVEL): logger.is_log_level, }), validate_message_just_topic), vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266, - cv.ensure_list, [validate_fingerprint]), + cv.ensure_list(validate_fingerprint)), vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds, vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds, vol.Optional(CONF_ON_MESSAGE): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger), vol.Required(CONF_TOPIC): cv.subscribe_topic, - vol.Optional(CONF_QOS, default=0): cv.mqtt_qos, + vol.Optional(CONF_QOS): cv.mqtt_qos, + vol.Optional(CONF_PAYLOAD): cv.string_strict, }), vol.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTJsonMessageTrigger), vol.Required(CONF_TOPIC): cv.subscribe_topic, vol.Optional(CONF_QOS, default=0): cv.mqtt_qos, }), -}) +}), validate_config) def exp_mqtt_message(config): @@ -116,12 +120,17 @@ def to_code(config): config[CONF_USERNAME], config[CONF_PASSWORD]) mqtt = Pvariable(config[CONF_ID], rhs) - if not config.get(CONF_DISCOVERY, True): + discovery = config.get(CONF_DISCOVERY, True) + discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True) + discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant') + + if not discovery: add(mqtt.disable_discovery()) + elif discovery == "CLEAN": + add(mqtt.set_discovery_info(discovery_prefix, discovery_retain, True)) elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config: - discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True) - discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant') add(mqtt.set_discovery_info(discovery_prefix, discovery_retain)) + if CONF_TOPIC_PREFIX in config: add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX])) @@ -169,8 +178,12 @@ def to_code(config): add(mqtt.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) for conf in config.get(CONF_ON_MESSAGE, []): - rhs = mqtt.make_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS]) + rhs = App.register_component(mqtt.make_message_trigger(conf[CONF_TOPIC])) trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + if CONF_QOS in conf: + add(trigger.set_qos(conf[CONF_QOS])) + if CONF_PAYLOAD in conf: + add(trigger.set_payload(conf[CONF_PAYLOAD])) automation.build_automation(trigger, std_string, conf) for conf in config.get(CONF_ON_JSON_MESSAGE, []): @@ -189,8 +202,7 @@ MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({ @ACTION_REGISTRY.register(CONF_MQTT_PUBLISH, MQTT_PUBLISH_ACTION_SCHEMA) -def mqtt_publish_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def mqtt_publish_action_to_code(config, action_id, arg_type, template_arg): rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg) type = MQTTPublishAction.template(template_arg) action = Pvariable(action_id, rhs, type=type) @@ -222,8 +234,7 @@ MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({ @ACTION_REGISTRY.register(CONF_MQTT_PUBLISH_JSON, MQTT_PUBLISH_JSON_ACTION_SCHEMA) -def mqtt_publish_json_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def mqtt_publish_json_action_to_code(config, action_id, arg_type, template_arg): rhs = App.Pget_mqtt_client().Pmake_publish_json_action(template_arg) type = MQTTPublishJsonAction.template(template_arg) action = Pvariable(action_id, rhs, type=type) @@ -231,7 +242,8 @@ def mqtt_publish_json_action_to_code(config, action_id, arg_type): yield None add(action.set_topic(template_)) - for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')]): + for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')], + return_type=void): yield None add(action.set_payload(lambda_)) if CONF_QOS in config: @@ -254,12 +266,11 @@ def get_default_topic_for(data, component_type, name, suffix): sanitized_name, suffix) -def build_hass_config(data, component_type, config, include_state=True, include_command=True, - platform='mqtt'): +def build_hass_config(data, component_type, config, include_state=True, include_command=True): if config.get(CONF_INTERNAL, False): return None ret = OrderedDict() - ret['platform'] = platform + ret['platform'] = 'mqtt' ret['name'] = config[CONF_NAME] if include_state: default = get_default_topic_for(data, component_type, config[CONF_NAME], 'state') @@ -282,7 +293,7 @@ def build_hass_config(data, component_type, config, include_state=True, include_ class GenerateHassConfigData(object): def __init__(self, config): if 'mqtt' not in config: - raise ESPHomeYAMLError("Cannot generate Home Assistant MQTT config if MQTT is not " + 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]) @@ -308,3 +319,21 @@ class GenerateHassConfigData(object): CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD], CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD], } + + +def setup_mqtt_component(obj, config): + if CONF_RETAIN in config: + add(obj.set_retain(config[CONF_RETAIN])) + if not config.get(CONF_DISCOVERY, True): + add(obj.disable_discovery()) + if CONF_STATE_TOPIC in config: + add(obj.set_custom_state_topic(config[CONF_STATE_TOPIC])) + if CONF_COMMAND_TOPIC in config: + add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC])) + if CONF_AVAILABILITY in config: + availability = config[CONF_AVAILABILITY] + if not availability: + add(obj.disable_availability()) + else: + add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE], + availability[CONF_PAYLOAD_NOT_AVAILABLE])) diff --git a/esphomeyaml/components/my9231.py b/esphomeyaml/components/my9231.py index a52e1b1033..602d1f52ad 100644 --- a/esphomeyaml/components/my9231.py +++ b/esphomeyaml/components/my9231.py @@ -1,18 +1,18 @@ 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) +import esphomeyaml.config_validation as cv +from esphomeyaml.const import (CONF_BIT_DEPTH, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_ID, + CONF_NUM_CHANNELS, CONF_NUM_CHIPS, CONF_UPDATE_ON_BOOT) +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component MY9231OutputComponent = output.output_ns.class_('MY9231OutputComponent', Component) +MULTI_CONF = True - -MY9231_SCHEMA = vol.Schema({ +CONFIG_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, @@ -20,33 +20,27 @@ MY9231_SCHEMA = vol.Schema({ 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_BIT_DEPTH): cv.one_of(8, 12, 14, 16, int=True), 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) + for di in gpio_output_pin_expression(config[CONF_DATA_PIN]): + yield + for dcki in gpio_output_pin_expression(config[CONF_CLOCK_PIN]): + yield + rhs = App.make_my9231_component(di, dcki) + my9231 = Pvariable(config[CONF_ID], rhs) + if CONF_NUM_CHANNELS in config: + add(my9231.set_num_channels(config[CONF_NUM_CHANNELS])) + if CONF_NUM_CHIPS in config: + add(my9231.set_num_chips(config[CONF_NUM_CHIPS])) + if CONF_BIT_DEPTH in config: + add(my9231.set_bit_depth(config[CONF_BIT_DEPTH])) + if CONF_UPDATE_ON_BOOT in config: + add(my9231.set_update(config[CONF_UPDATE_ON_BOOT])) + setup_component(my9231, config) BUILD_FLAGS = '-DUSE_MY9231_OUTPUT' diff --git a/esphomeyaml/components/ota.py b/esphomeyaml/components/ota.py index 83b224cbed..deac3d28d8 100644 --- a/esphomeyaml/components/ota.py +++ b/esphomeyaml/components/ota.py @@ -2,12 +2,11 @@ import logging import voluptuous as vol -from esphomeyaml import core 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, Component +from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_types import App, Component, esphomelib_ns _LOGGER = logging.getLogger(__name__) @@ -35,11 +34,11 @@ def to_code(config): def get_port(config): if CONF_PORT in config[CONF_OTA]: return config[CONF_OTA][CONF_PORT] - if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if CORE.is_esp32: return 3232 - elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + if CORE.is_esp8266: return 8266 - raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.") + raise NotImplementedError def get_auth(config): @@ -51,6 +50,8 @@ REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA' def lib_deps(config): - if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: - return ['ArduinoOTA', 'Update', 'ESPmDNS'] - return ['Hash', 'ESP8266mDNS', 'ArduinoOTA'] + if CORE.is_esp32: + return ['Update', 'ESPmDNS'] + if CORE.is_esp8266: + return ['Hash', 'ESP8266mDNS'] + raise NotImplementedError diff --git a/esphomeyaml/components/output/__init__.py b/esphomeyaml/components/output/__init__.py index 6456bd0991..b5a575c6f2 100644 --- a/esphomeyaml/components/output/__init__.py +++ b/esphomeyaml/components/output/__init__.py @@ -4,8 +4,9 @@ from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY 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, float_, add_job, Action +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import add, get_variable, Pvariable, templatable +from esphomeyaml.cpp_types import esphomelib_ns, Action, float_ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -26,7 +27,9 @@ FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema output_ns = esphomelib_ns.namespace('output') BinaryOutput = output_ns.class_('BinaryOutput') +BinaryOutputPtr = BinaryOutput.operator('ptr') FloatOutput = output_ns.class_('FloatOutput', BinaryOutput) +FloatOutputPtr = FloatOutput.operator('ptr') # Actions TurnOffAction = output_ns.class_('TurnOffAction', Action) @@ -47,7 +50,12 @@ def setup_output_platform_(obj, config, skip_power_supply=False): def setup_output_platform(obj, config, skip_power_supply=False): - add_job(setup_output_platform_, obj, config, skip_power_supply) + CORE.add_job(setup_output_platform_, obj, config, skip_power_supply) + + +def register_output(var, config): + output_var = Pvariable(config[CONF_ID], var, has_side_effects=True) + CORE.add_job(setup_output_platform_, output_var, config) BUILD_FLAGS = '-DUSE_OUTPUT' @@ -60,8 +68,7 @@ OUTPUT_TURN_ON_ACTION = maybe_simple_id({ @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) +def output_turn_on_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) @@ -76,8 +83,7 @@ OUTPUT_TURN_OFF_ACTION = maybe_simple_id({ @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) +def output_turn_off_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) @@ -93,8 +99,7 @@ OUTPUT_SET_LEVEL_ACTION = vol.Schema({ @ACTION_REGISTRY.register(CONF_OUTPUT_SET_LEVEL, OUTPUT_SET_LEVEL_ACTION) -def output_set_level_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def output_set_level_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_set_level_action(template_arg) diff --git a/esphomeyaml/components/output/custom.py b/esphomeyaml/components/output/custom.py new file mode 100644 index 0000000000..6343a52853 --- /dev/null +++ b/esphomeyaml/components/output/custom.py @@ -0,0 +1,66 @@ +import voluptuous as vol + +from esphomeyaml.components import output +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_types import std_vector + +CustomBinaryOutputConstructor = output.output_ns.class_('CustomBinaryOutputConstructor') +CustomFloatOutputConstructor = output.output_ns.class_('CustomFloatOutputConstructor') + +BINARY_SCHEMA = output.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomBinaryOutputConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_OUTPUTS): + cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(output.BinaryOutput), + })), +}) + +FLOAT_SCHEMA = output.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomFloatOutputConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_OUTPUTS): + cv.ensure_list(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(output.FloatOutput), + })), +}) + + +def validate_custom_output(value): + if not isinstance(value, dict): + raise vol.Invalid("Value must be dict") + if CONF_TYPE not in value: + raise vol.Invalid("type not specified!") + type = cv.string_strict(value[CONF_TYPE]).lower() + value[CONF_TYPE] = type + if type == 'binary': + return BINARY_SCHEMA(value) + if type == 'float': + return FLOAT_SCHEMA(value) + raise vol.Invalid("type must either be binary or float, not {}!".format(type)) + + +PLATFORM_SCHEMA = validate_custom_output + + +def to_code(config): + type = config[CONF_TYPE] + if type == 'binary': + ret_type = output.BinaryOutputPtr + klass = CustomBinaryOutputConstructor + else: + ret_type = output.FloatOutputPtr + klass = CustomFloatOutputConstructor + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(ret_type)): + yield + + rhs = klass(template_) + custom = variable(config[CONF_ID], rhs) + for i, sens in enumerate(config[CONF_OUTPUTS]): + output.register_output(custom.get_output(i), sens) + + +BUILD_FLAGS = '-DUSE_CUSTOM_OUTPUT' diff --git a/esphomeyaml/components/output/esp8266_pwm.py b/esphomeyaml/components/output/esp8266_pwm.py index c28673742a..a42ff604be 100644 --- a/esphomeyaml/components/output/esp8266_pwm.py +++ b/esphomeyaml/components/output/esp8266_pwm.py @@ -3,9 +3,10 @@ import voluptuous as vol from esphomeyaml import pins from esphomeyaml.components import output 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 +from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266 +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] diff --git a/esphomeyaml/components/output/gpio.py b/esphomeyaml/components/output/gpio.py index 51e3ce27ef..fb4949c1e5 100644 --- a/esphomeyaml/components/output/gpio.py +++ b/esphomeyaml/components/output/gpio.py @@ -1,11 +1,12 @@ import voluptuous as vol from esphomeyaml import pins -import esphomeyaml.config_validation as cv from esphomeyaml.components import output +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression, setup_component, \ - Component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component GPIOBinaryOutputComponent = output.output_ns.class_('GPIOBinaryOutputComponent', output.BinaryOutput, Component) diff --git a/esphomeyaml/components/output/ledc.py b/esphomeyaml/components/output/ledc.py index 987d44a5be..bd42ba3569 100644 --- a/esphomeyaml/components/output/ledc.py +++ b/esphomeyaml/components/output/ledc.py @@ -1,11 +1,13 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import output +import esphomeyaml.config_validation as cv 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, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component ESP_PLATFORMS = [ESP_PLATFORM_ESP32] diff --git a/esphomeyaml/components/output/my9231.py b/esphomeyaml/components/output/my9231.py index 4669ed2400..ee9f7f494b 100644 --- a/esphomeyaml/components/output/my9231.py +++ b/esphomeyaml/components/output/my9231.py @@ -1,10 +1,11 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import output from esphomeyaml.components.my9231 import MY9231OutputComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY -from esphomeyaml.helpers import Pvariable, get_variable, setup_component +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_helpers import setup_component DEPENDENCIES = ['my9231'] diff --git a/esphomeyaml/components/output/pca9685.py b/esphomeyaml/components/output/pca9685.py index ba2f57ce6d..b43c0f767b 100644 --- a/esphomeyaml/components/output/pca9685.py +++ b/esphomeyaml/components/output/pca9685.py @@ -1,10 +1,10 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import output from esphomeyaml.components.pca9685 import PCA9685OutputComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY -from esphomeyaml.helpers import Pvariable, get_variable +from esphomeyaml.cpp_generator import Pvariable, get_variable DEPENDENCIES = ['pca9685'] diff --git a/esphomeyaml/components/pca9685.py b/esphomeyaml/components/pca9685.py index ac3094abaf..e30758b310 100644 --- a/esphomeyaml/components/pca9685.py +++ b/esphomeyaml/components/pca9685.py @@ -1,37 +1,32 @@ import voluptuous as vol +from esphomeyaml.components import i2c, output import esphomeyaml.config_validation as cv -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, setup_component, Component +from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component DEPENDENCIES = ['i2c'] +MULTI_CONF = True 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.") - -PCA9685_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent), vol.Required(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=23.84, max=1525.88)), 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]) - def to_code(config): - for conf in config: - rhs = App.make_pca9685_component(conf.get(CONF_FREQUENCY)) - pca9685 = Pvariable(conf[CONF_ID], rhs) - if CONF_ADDRESS in conf: - add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS]))) - setup_component(pca9685, conf) + rhs = App.make_pca9685_component(config.get(CONF_FREQUENCY)) + pca9685 = Pvariable(config[CONF_ID], rhs) + if CONF_ADDRESS in config: + add(pca9685.set_address(config[CONF_ADDRESS])) + setup_component(pca9685, config) BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT' diff --git a/esphomeyaml/components/pcf8574.py b/esphomeyaml/components/pcf8574.py index e2f92d763f..ef68413552 100644 --- a/esphomeyaml/components/pcf8574.py +++ b/esphomeyaml/components/pcf8574.py @@ -3,9 +3,12 @@ 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, GPIOInputPin, GPIOOutputPin, Pvariable, io_ns, setup_component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, GPIOInputPin, GPIOOutputPin, io_ns DEPENDENCIES = ['i2c'] +MULTI_CONF = True PCF8574GPIOMode = io_ns.enum('PCF8574GPIOMode') PCF8675_GPIO_MODES = { @@ -17,20 +20,17 @@ PCF8675_GPIO_MODES = { PCF8574GPIOInputPin = io_ns.class_('PCF8574GPIOInputPin', GPIOInputPin) PCF8574GPIOOutputPin = io_ns.class_('PCF8574GPIOOutputPin', GPIOOutputPin) -PCF8574_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ 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]) - def to_code(config): - for conf in config: - rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575]) - var = Pvariable(conf[CONF_ID], rhs) - setup_component(var, conf) + rhs = App.make_pcf8574_component(config[CONF_ADDRESS], config[CONF_PCF8575]) + var = Pvariable(config[CONF_ID], rhs) + setup_component(var, config) BUILD_FLAGS = '-DUSE_PCF8574' diff --git a/esphomeyaml/components/pn532.py b/esphomeyaml/components/pn532.py index a33ac791bd..9d2afbf0e5 100644 --- a/esphomeyaml/components/pn532.py +++ b/esphomeyaml/components/pn532.py @@ -1,21 +1,23 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml import pins, automation +from esphomeyaml import automation, pins 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, setup_component, PollingComponent, Trigger +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_ON_TAG, CONF_SPI_ID, CONF_TRIGGER_ID, \ + CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, PollingComponent, Trigger, std_string DEPENDENCIES = ['spi'] +MULTI_CONF = True 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({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(PN532Component), cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, @@ -23,23 +25,22 @@ 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)]) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for conf in config: - for spi_ in get_variable(conf[CONF_SPI_ID]): - yield - for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]): - yield - rhs = App.make_pn532_component(spi_, cs, conf.get(CONF_UPDATE_INTERVAL)) - pn532 = Pvariable(conf[CONF_ID], rhs) + 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_pn532_component(spi_, cs, config.get(CONF_UPDATE_INTERVAL)) + pn532 = Pvariable(config[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_) + for conf_ in config.get(CONF_ON_TAG, []): + trigger = Pvariable(conf_[CONF_TRIGGER_ID], pn532.make_trigger()) + automation.build_automation(trigger, std_string, conf_) - setup_component(pn532, conf) + setup_component(pn532, config) BUILD_FLAGS = '-DUSE_PN532' diff --git a/esphomeyaml/components/power_supply.py b/esphomeyaml/components/power_supply.py index 97efda4f1e..de02c708e5 100644 --- a/esphomeyaml/components/power_supply.py +++ b/esphomeyaml/components/power_supply.py @@ -1,36 +1,36 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins +import esphomeyaml.config_validation as cv 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, \ - setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns PowerSupplyComponent = esphomelib_ns.class_('PowerSupplyComponent', Component) -POWER_SUPPLY_SCHEMA = vol.Schema({ +MULTI_CONF = True + +CONFIG_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: - for pin in gpio_output_pin_expression(conf[CONF_PIN]): - yield + for pin in gpio_output_pin_expression(config[CONF_PIN]): + yield - rhs = App.make_power_supply(pin) - psu = Pvariable(conf[CONF_ID], rhs) - if CONF_ENABLE_TIME in conf: - add(psu.set_enable_time(conf[CONF_ENABLE_TIME])) - if CONF_KEEP_ON_TIME in conf: - add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME])) + rhs = App.make_power_supply(pin) + psu = Pvariable(config[CONF_ID], rhs) + if CONF_ENABLE_TIME in config: + add(psu.set_enable_time(config[CONF_ENABLE_TIME])) + if CONF_KEEP_ON_TIME in config: + add(psu.set_keep_on_time(config[CONF_KEEP_ON_TIME])) - setup_component(psu, conf) + setup_component(psu, config) BUILD_FLAGS = '-DUSE_OUTPUT' diff --git a/esphomeyaml/components/rdm6300.py b/esphomeyaml/components/rdm6300.py index 0ede8e290f..7f38895e98 100644 --- a/esphomeyaml/components/rdm6300.py +++ b/esphomeyaml/components/rdm6300.py @@ -1,28 +1,29 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor, uart +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_UART_ID -from esphomeyaml.helpers import App, Pvariable, get_variable, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component DEPENDENCIES = ['uart'] RDM6300Component = binary_sensor.binary_sensor_ns.class_('RDM6300Component', Component, uart.UARTDevice) -CONFIG_SCHEMA = vol.All(cv.ensure_list_not_empty, [vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(RDM6300Component), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(uart.UARTComponent), -}).extend(cv.COMPONENT_SCHEMA.schema)]) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for conf in config: - for uart_ in get_variable(conf[CONF_UART_ID]): - yield - rhs = App.make_rdm6300_component(uart_) - var = Pvariable(conf[CONF_ID], rhs) - setup_component(var, conf) + for uart_ in get_variable(config[CONF_UART_ID]): + yield + rhs = App.make_rdm6300_component(uart_) + var = Pvariable(config[CONF_ID], rhs) + setup_component(var, config) BUILD_FLAGS = '-DUSE_RDM6300' diff --git a/esphomeyaml/components/remote_receiver.py b/esphomeyaml/components/remote_receiver.py index 878e5a6fc5..d1d6147379 100644 --- a/esphomeyaml/components/remote_receiver.py +++ b/esphomeyaml/components/remote_receiver.py @@ -1,13 +1,16 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins +import esphomeyaml.config_validation as cv 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, \ - setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns +from esphomeyaml.py_compat import string_types remote_ns = esphomelib_ns.namespace('remote') +MULTI_CONF = True RemoteControlComponentBase = remote_ns.class_('RemoteControlComponentBase') RemoteReceiverComponent = remote_ns.class_('RemoteReceiverComponent', @@ -28,45 +31,43 @@ DUMPERS = { def validate_dumpers_all(value): - if not isinstance(value, (str, unicode)): + if not isinstance(value, string_types): raise vol.Invalid("Not valid dumpers") if value.upper() == "ALL": return list(sorted(list(DUMPERS))) raise vol.Invalid("Not valid dumpers") -CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(RemoteReceiverComponent), vol.Required(CONF_PIN): pins.gpio_input_pin_schema, vol.Optional(CONF_DUMP, default=[]): - vol.Any(validate_dumpers_all, - vol.All(cv.ensure_list, [vol.All(vol.Lower, cv.one_of(*DUMPERS))])), + vol.Any(validate_dumpers_all, cv.ensure_list(cv.one_of(*DUMPERS, lower=True))), vol.Optional(CONF_TOLERANCE): vol.All(cv.percentage_int, vol.Range(min=0)), 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)]) +}).extend(cv.COMPONENT_SCHEMA.schema) def to_code(config): - for conf in config: - 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 pin in gpio_input_pin_expression(config[CONF_PIN]): + yield + rhs = App.make_remote_receiver_component(pin) + receiver = Pvariable(config[CONF_ID], rhs) - for dumper in conf[CONF_DUMP]: - add(receiver.add_dumper(DUMPERS[dumper].new())) - if CONF_TOLERANCE in conf: - add(receiver.set_tolerance(conf[CONF_TOLERANCE])) - if CONF_BUFFER_SIZE in conf: - add(receiver.set_buffer_size(conf[CONF_BUFFER_SIZE])) - if CONF_FILTER in conf: - add(receiver.set_filter_us(conf[CONF_FILTER])) - if CONF_IDLE in conf: - add(receiver.set_idle_us(conf[CONF_IDLE])) + for dumper in config[CONF_DUMP]: + add(receiver.add_dumper(DUMPERS[dumper].new())) + if CONF_TOLERANCE in config: + add(receiver.set_tolerance(config[CONF_TOLERANCE])) + if CONF_BUFFER_SIZE in config: + add(receiver.set_buffer_size(config[CONF_BUFFER_SIZE])) + if CONF_FILTER in config: + add(receiver.set_filter_us(config[CONF_FILTER])) + if CONF_IDLE in config: + add(receiver.set_idle_us(config[CONF_IDLE])) - setup_component(receiver, conf) + setup_component(receiver, config) BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER' diff --git a/esphomeyaml/components/remote_transmitter.py b/esphomeyaml/components/remote_transmitter.py index 144ac2bfdf..f0ae3636fa 100644 --- a/esphomeyaml/components/remote_transmitter.py +++ b/esphomeyaml/components/remote_transmitter.py @@ -7,17 +7,21 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_DUTY_PERCENT, CONF_CHAN 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, Component, Pvariable, add, gpio_output_pin_expression, \ - setup_component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component +from esphomeyaml.py_compat import text_type RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent', RemoteControlComponentBase, Component) RCSwitchProtocol = remote_ns.class_('RCSwitchProtocol') rc_switch_protocols = remote_ns.rc_switch_protocols +MULTI_CONF = True + def validate_rc_switch_code(value): - if not isinstance(value, (str, unicode)): + if not isinstance(value, (str, text_type)): raise vol.Invalid("All RCSwitch codes must be in quotes ('')") for c in value: if c not in ('0', '1'): @@ -61,28 +65,26 @@ RC_SWITCH_TYPE_B_SCHEMA = vol.Schema({ vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({ - vol.Required(CONF_FAMILY): vol.All( - cv.string, vol.Lower, - cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p')), + vol.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', lower=True), vol.Required(CONF_GROUP): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) RC_SWITCH_TYPE_D_SCHEMA = vol.Schema({ - vol.Required(CONF_GROUP): vol.All(cv.string, vol.Lower, cv.one_of('a', 'b', 'c', 'd')), + vol.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True), vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=3)), vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent), 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)]) +}).extend(cv.COMPONENT_SCHEMA.schema) def build_rc_switch_protocol(config): @@ -104,16 +106,15 @@ def binary_code(value): def to_code(config): - for conf in config: - for pin in gpio_output_pin_expression(conf[CONF_PIN]): - yield - rhs = App.make_remote_transmitter_component(pin) - transmitter = Pvariable(conf[CONF_ID], rhs) + for pin in gpio_output_pin_expression(config[CONF_PIN]): + yield + rhs = App.make_remote_transmitter_component(pin) + transmitter = Pvariable(config[CONF_ID], rhs) - if CONF_CARRIER_DUTY_PERCENT in conf: - add(transmitter.set_carrier_duty_percent(conf[CONF_CARRIER_DUTY_PERCENT])) + if CONF_CARRIER_DUTY_PERCENT in config: + add(transmitter.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT])) - setup_component(transmitter, conf) + setup_component(transmitter, config) BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER' diff --git a/esphomeyaml/components/script.py b/esphomeyaml/components/script.py index 5aab19c3c8..834179b121 100644 --- a/esphomeyaml/components/script.py +++ b/esphomeyaml/components/script.py @@ -4,11 +4,12 @@ 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, \ - Trigger, Action +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_types import Action, NoArg, Trigger, esphomelib_ns Script = esphomelib_ns.class_('Script', Trigger.template(NoArg)) ScriptExecuteAction = esphomelib_ns.class_('ScriptExecuteAction', Action) +ScriptStopAction = esphomelib_ns.class_('ScriptStopAction', Action) CONFIG_SCHEMA = automation.validate_automation({ vol.Required(CONF_ID): cv.declare_variable_id(Script), @@ -28,10 +29,24 @@ SCRIPT_EXECUTE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_SCRIPT_EXECUTE, SCRIPT_EXECUTE_ACTION_SCHEMA) -def script_execute_action_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def script_execute_action_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_execute_action(template_arg) type = ScriptExecuteAction.template(arg_type) yield Pvariable(action_id, rhs, type=type) + + +CONF_SCRIPT_STOP = 'script.stop' +SCRIPT_STOP_ACTION_SCHEMA = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(Script), +}) + + +@ACTION_REGISTRY.register(CONF_SCRIPT_STOP, SCRIPT_STOP_ACTION_SCHEMA) +def script_stop_action_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_stop_action(template_arg) + type = ScriptStopAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) diff --git a/esphomeyaml/components/sensor/__init__.py b/esphomeyaml/components/sensor/__init__.py index 846c08ea81..5f8f52152f 100644 --- a/esphomeyaml/components/sensor/__init__.py +++ b/esphomeyaml/components/sensor/__init__.py @@ -1,7 +1,9 @@ import voluptuous as vol from esphomeyaml import automation +from esphomeyaml.automation import CONDITION_REGISTRY from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component 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, \ @@ -10,9 +12,11 @@ from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CO 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 +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, process_lambda, \ + templatable, get_variable +from esphomeyaml.cpp_types import App, Component, Nameable, PollingComponent, Trigger, \ + esphomelib_ns, float_, optional PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -36,32 +40,33 @@ 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] -FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ - vol.Optional(CONF_OFFSET): vol.Coerce(float), - vol.Optional(CONF_MULTIPLY): vol.Coerce(float), - vol.Optional(CONF_FILTER_OUT): vol.Coerce(float), +FILTERS_SCHEMA = cv.ensure_list({ + vol.Optional(CONF_OFFSET): cv.float_, + vol.Optional(CONF_MULTIPLY): cv.float_, + vol.Optional(CONF_FILTER_OUT): cv.float_, vol.Optional(CONF_FILTER_NAN): None, 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_WINDOW_SIZE, default=15): cv.positive_not_null_int, + vol.Optional(CONF_SEND_EVERY, default=15): 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, + vol.Optional(CONF_ALPHA, default=0.1): cv.positive_float, + vol.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, }), vol.Optional(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds, - vol.Optional(CONF_DELTA): vol.Coerce(float), + vol.Optional(CONF_DELTA): cv.float_, vol.Optional(CONF_UNIQUE): None, vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds, vol.Optional(CONF_DEBOUNCE): cv.positive_time_period_milliseconds, vol.Optional(CONF_OR): validate_recursive_filter, -}, cv.has_exactly_one_key(*FILTER_KEYS))]) +}, cv.has_exactly_one_key(*FILTER_KEYS)) # Base sensor_ns = esphomelib_ns.namespace('sensor') Sensor = sensor_ns.class_('Sensor', Nameable) +SensorPtr = Sensor.operator('ptr') MQTTSensorComponent = sensor_ns.class_('MQTTSensorComponent', mqtt.MQTTComponent) PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', PollingComponent, Sensor) @@ -71,7 +76,7 @@ EmptyPollingParentSensor = sensor_ns.class_('EmptyPollingParentSensor', EmptySen # 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_)) +ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', Trigger.template(float_), Component) # Filters Filter = sensor_ns.class_('Filter') @@ -88,6 +93,7 @@ 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) +SensorInRangeCondition = sensor_ns.class_('SensorInRangeCondition', Filter) SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent), @@ -104,8 +110,8 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ }), vol.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger), - vol.Optional(CONF_ABOVE): vol.Coerce(float), - vol.Optional(CONF_BELOW): vol.Coerce(float), + vol.Optional(CONF_ABOVE): cv.float_, + vol.Optional(CONF_BELOW): cv.float_, }, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)), }) @@ -130,7 +136,8 @@ def setup_filter(config): yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY]) elif CONF_LAMBDA in config: lambda_ = None - for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')]): + for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')], + return_type=optional.template(float_)): yield None yield LambdaFilter.new(lambda_) elif CONF_THROTTLE in config: @@ -186,13 +193,12 @@ def setup_sensor_core_(sensor_var, mqtt_var, config): for conf in config.get(CONF_ON_VALUE_RANGE, []): rhs = sensor_var.make_value_range_trigger() trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + add(App.register_component(trigger)) if CONF_ABOVE in conf: - template_ = None for template_ in templatable(conf[CONF_ABOVE], float_, float_): yield add(trigger.set_min(template_)) if CONF_BELOW in conf: - template_ = None for template_ in templatable(conf[CONF_BELOW], float_, float_): yield add(trigger.set_max(template_)) @@ -209,19 +215,43 @@ def setup_sensor_core_(sensor_var, mqtt_var, config): def setup_sensor(sensor_obj, mqtt_obj, config): sensor_var = Pvariable(config[CONF_ID], sensor_obj, has_side_effects=False) mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False) - add_job(setup_sensor_core_, sensor_var, mqtt_var, config) + CORE.add_job(setup_sensor_core_, sensor_var, mqtt_var, config) def register_sensor(var, config): sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True) rhs = App.register_sensor(sensor_var) mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True) - add_job(setup_sensor_core_, sensor_var, mqtt_var, config) + CORE.add_job(setup_sensor_core_, sensor_var, mqtt_var, config) BUILD_FLAGS = '-DUSE_SENSOR' +CONF_SENSOR_IN_RANGE = 'sensor.in_range' +SENSOR_IN_RANGE_CONDITION_SCHEMA = vol.All({ + vol.Required(CONF_ID): cv.use_variable_id(Sensor), + vol.Optional(CONF_ABOVE): cv.float_, + vol.Optional(CONF_BELOW): cv.float_, +}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) + + +@CONDITION_REGISTRY.register(CONF_SENSOR_IN_RANGE, SENSOR_IN_RANGE_CONDITION_SCHEMA) +def sensor_in_range_to_code(config, condition_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_sensor_in_range_condition(template_arg) + type = SensorInRangeCondition.template(arg_type) + cond = Pvariable(condition_id, rhs, type=type) + + if CONF_ABOVE in config: + add(cond.set_min(config[CONF_ABOVE])) + if CONF_BELOW in config: + add(cond.set_max(config[CONF_BELOW])) + + yield cond + + 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: diff --git a/esphomeyaml/components/sensor/adc.py b/esphomeyaml/components/sensor/adc.py index f629ee3d4d..7bf9ae9101 100644 --- a/esphomeyaml/components/sensor/adc.py +++ b/esphomeyaml/components/sensor/adc.py @@ -1,11 +1,13 @@ 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_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, add, global_ns, variable, setup_component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, global_ns ATTENUATION_MODES = { '0db': global_ns.ADC_0db, @@ -29,7 +31,8 @@ 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_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES, + lower=True)), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema)) @@ -59,3 +62,9 @@ def required_build_flags(config): def to_hass_config(data, config): return sensor.core_to_hass_config(data, config) + + +def includes(config): + if config[CONF_PIN] == 'VCC': + return 'ADC_MODE(ADC_VCC);' + return None diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index 5f6c296789..a5ae2eadbe 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -1,11 +1,12 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.components.ads1115 import ADS1115Component +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import get_variable +from esphomeyaml.cpp_generator import get_variable +from esphomeyaml.py_compat import string_types DEPENDENCIES = ['ads1115'] @@ -35,7 +36,7 @@ GAIN = { def validate_gain(value): if isinstance(value, float): value = u'{:0.03f}'.format(value) - elif not isinstance(value, (str, unicode)): + elif not isinstance(value, string_types): raise vol.Invalid('invalid gain "{}"'.format(value)) return cv.one_of(*GAIN)(value) @@ -59,7 +60,6 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ def to_code(config): - hub = None for hub in get_variable(config[CONF_ADS1115_ID]): yield diff --git a/esphomeyaml/components/sensor/apds9960.py b/esphomeyaml/components/sensor/apds9960.py new file mode 100644 index 0000000000..ddd9fd7d5b --- /dev/null +++ b/esphomeyaml/components/sensor/apds9960.py @@ -0,0 +1,35 @@ +import voluptuous as vol + +from esphomeyaml.components import sensor +from esphomeyaml.components.apds9960 import APDS9960, CONF_APDS9960_ID +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_NAME, CONF_TYPE +from esphomeyaml.cpp_generator import get_variable + +DEPENDENCIES = ['apds9960'] + +TYPES = { + 'CLEAR': 'make_clear_channel', + 'RED': 'make_red_channel', + 'GREEN': 'make_green_channel', + 'BLUE': 'make_blue_channel', + 'PROXIMITY': 'make_proximity', +} + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(sensor.Sensor), + vol.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), + cv.GenerateID(CONF_APDS9960_ID): cv.use_variable_id(APDS9960) +})) + + +def to_code(config): + for hub in get_variable(config[CONF_APDS9960_ID]): + yield + func = getattr(hub, TYPES[config[CONF_TYPE]]) + rhs = func(config[CONF_NAME]) + 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/bh1750.py b/esphomeyaml/components/sensor/bh1750.py index 09057e5f7f..c4f36faaea 100644 --- a/esphomeyaml/components/sensor/bh1750.py +++ b/esphomeyaml/components/sensor/bh1750.py @@ -1,10 +1,12 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -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, setup_component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/ble_rssi.py b/esphomeyaml/components/sensor/ble_rssi.py index e209b9af64..f8856c5e89 100644 --- a/esphomeyaml/components/sensor/ble_rssi.py +++ b/esphomeyaml/components/sensor/ble_rssi.py @@ -1,11 +1,12 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import 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, esphomelib_ns +from esphomeyaml.cpp_generator import get_variable +from esphomeyaml.cpp_types import esphomelib_ns DEPENDENCIES = ['esp32_ble_tracker'] diff --git a/esphomeyaml/components/sensor/bme280.py b/esphomeyaml/components/sensor/bme280.py index 23e395f6ef..b100c3579b 100644 --- a/esphomeyaml/components/sensor/bme280.py +++ b/esphomeyaml/components/sensor/bme280.py @@ -4,7 +4,9 @@ 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, setup_component +from esphomeyaml.cpp_generator import variable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, App DEPENDENCIES = ['i2c'] @@ -28,7 +30,7 @@ IIR_FILTER_OPTIONS = { } BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), + vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), }) MakeBME280Sensor = Application.struct('MakeBME280Sensor') @@ -51,7 +53,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ 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_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphomeyaml/components/sensor/bme680.py b/esphomeyaml/components/sensor/bme680.py index 71778574a1..2864200c86 100644 --- a/esphomeyaml/components/sensor/bme680.py +++ b/esphomeyaml/components/sensor/bme680.py @@ -6,7 +6,9 @@ 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, setup_component +from esphomeyaml.cpp_generator import variable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, App DEPENDENCIES = ['i2c'] @@ -33,7 +35,7 @@ IIR_FILTER_OPTIONS = { } BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), + vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), }) MakeBME680Sensor = Application.struct('MakeBME680Sensor') @@ -61,7 +63,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ 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_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), 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)), vol.Optional(CONF_DURATION, default='150ms'): vol.All( diff --git a/esphomeyaml/components/sensor/bmp085.py b/esphomeyaml/components/sensor/bmp085.py index 24b7199a10..2c622a7c8f 100644 --- a/esphomeyaml/components/sensor/bmp085.py +++ b/esphomeyaml/components/sensor/bmp085.py @@ -4,7 +4,9 @@ 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, setup_component +from esphomeyaml.cpp_generator import variable, add, HexIntLiteral +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, App DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/bmp280.py b/esphomeyaml/components/sensor/bmp280.py index 35d30403d8..2182782b93 100644 --- a/esphomeyaml/components/sensor/bmp280.py +++ b/esphomeyaml/components/sensor/bmp280.py @@ -1,10 +1,12 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv 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, setup_component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application DEPENDENCIES = ['i2c'] @@ -28,7 +30,7 @@ IIR_FILTER_OPTIONS = { } BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), + vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), }) MakeBMP280Sensor = Application.struct('MakeBMP280Sensor') @@ -46,7 +48,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ 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_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphomeyaml/components/sensor/cse7766.py b/esphomeyaml/components/sensor/cse7766.py index 091c146632..c436ac31c5 100644 --- a/esphomeyaml/components/sensor/cse7766.py +++ b/esphomeyaml/components/sensor/cse7766.py @@ -4,12 +4,14 @@ 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, setup_component, Component + CONF_UPDATE_INTERVAL, CONF_VOLTAGE +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DEPENDENCIES = ['uart'] -CSE7766Component = sensor.sensor_ns.class_('CSE7766Component', Component, uart.UARTDevice) +CSE7766Component = sensor.sensor_ns.class_('CSE7766Component', PollingComponent, uart.UARTDevice) CSE7766VoltageSensor = sensor.sensor_ns.class_('CSE7766VoltageSensor', sensor.EmptySensor) CSE7766CurrentSensor = sensor.sensor_ns.class_('CSE7766CurrentSensor', @@ -30,6 +32,7 @@ PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(CSE7766PowerSensor), })), + vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, CONF_POWER)) @@ -38,7 +41,7 @@ def to_code(config): for uart_ in get_variable(config[CONF_UART_ID]): yield - rhs = App.make_cse7766(uart_) + rhs = App.make_cse7766(uart_, config.get(CONF_UPDATE_INTERVAL)) cse = Pvariable(config[CONF_ID], rhs) if CONF_VOLTAGE in config: diff --git a/esphomeyaml/components/sensor/custom.py b/esphomeyaml/components/sensor/custom.py new file mode 100644 index 0000000000..a87fef6604 --- /dev/null +++ b/esphomeyaml/components/sensor/custom.py @@ -0,0 +1,35 @@ +import voluptuous as vol + +from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_types import std_vector + +CustomSensorConstructor = sensor.sensor_ns.class_('CustomSensorConstructor') + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomSensorConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(sensor.Sensor), + })), +}) + + +def to_code(config): + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(sensor.SensorPtr)): + yield + + rhs = CustomSensorConstructor(template_) + custom = variable(config[CONF_ID], rhs) + for i, sens in enumerate(config[CONF_SENSORS]): + sensor.register_sensor(custom.get_sensor(i), sens) + + +BUILD_FLAGS = '-DUSE_CUSTOM_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, sens) for sens in config[CONF_SENSORS]] diff --git a/esphomeyaml/components/sensor/dallas.py b/esphomeyaml/components/sensor/dallas.py index b974c8e636..c26917fced 100644 --- a/esphomeyaml/components/sensor/dallas.py +++ b/esphomeyaml/components/sensor/dallas.py @@ -1,11 +1,11 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.components.dallas import DallasComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \ CONF_RESOLUTION -from esphomeyaml.helpers import HexIntLiteral, get_variable +from esphomeyaml.cpp_generator import HexIntLiteral, get_variable DallasTemperatureSensor = sensor.sensor_ns.class_('DallasTemperatureSensor', sensor.EmptyPollingParentSensor) diff --git a/esphomeyaml/components/sensor/dht.py b/esphomeyaml/components/sensor/dht.py index a13d0f0fdd..269687be36 100644 --- a/esphomeyaml/components/sensor/dht.py +++ b/esphomeyaml/components/sensor/dht.py @@ -1,12 +1,13 @@ 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, 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 +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, \ + CONF_PIN, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, add, variable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application, PollingComponent +from esphomeyaml.pins import gpio_input_pullup_pin_schema DHTModel = sensor.sensor_ns.enum('DHTModel') DHT_MODELS = { @@ -27,14 +28,14 @@ DHTHumiditySensor = sensor.sensor_ns.class_('DHTHumiditySensor', 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_PIN): gpio_input_pullup_pin_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_MODEL): cv.one_of(*DHT_MODELS, upper=True), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphomeyaml/components/sensor/dht12.py b/esphomeyaml/components/sensor/dht12.py index 7e80ab410e..f9a97d394f 100644 --- a/esphomeyaml/components/sensor/dht12.py +++ b/esphomeyaml/components/sensor/dht12.py @@ -1,11 +1,12 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -from esphomeyaml.components import sensor, i2c -from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, CONF_ID -from esphomeyaml.helpers import App, Application, variable, setup_component, PollingComponent, \ - Pvariable +from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ + CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/duty_cycle.py b/esphomeyaml/components/sensor/duty_cycle.py index 85f8ec3bd9..13f94d231f 100644 --- a/esphomeyaml/components/sensor/duty_cycle.py +++ b/esphomeyaml/components/sensor/duty_cycle.py @@ -1,11 +1,12 @@ 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_MAKE_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, variable, \ - setup_component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application MakeDutyCycleSensor = Application.struct('MakeDutyCycleSensor') DutyCycleSensor = sensor.sensor_ns.class_('DutyCycleSensor', sensor.PollingSensorComponent) diff --git a/esphomeyaml/components/sensor/esp32_hall.py b/esphomeyaml/components/sensor/esp32_hall.py index ddf2af9b5e..dbcb540024 100644 --- a/esphomeyaml/components/sensor/esp32_hall.py +++ b/esphomeyaml/components/sensor/esp32_hall.py @@ -3,7 +3,9 @@ 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, setup_component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, App ESP_PLATFORMS = [ESP_PLATFORM_ESP32] diff --git a/esphomeyaml/components/sensor/hdc1080.py b/esphomeyaml/components/sensor/hdc1080.py index e8decc408a..08e0c2a6d3 100644 --- a/esphomeyaml/components/sensor/hdc1080.py +++ b/esphomeyaml/components/sensor/hdc1080.py @@ -4,8 +4,9 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor, i2c from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \ CONF_UPDATE_INTERVAL, CONF_ID -from esphomeyaml.helpers import App, Application, variable, setup_component, PollingComponent, \ - Pvariable +from esphomeyaml.cpp_generator import variable, Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, PollingComponent, App DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/hlw8012.py b/esphomeyaml/components/sensor/hlw8012.py index 0f2f7d0aa5..5c57c5ed24 100644 --- a/esphomeyaml/components/sensor/hlw8012.py +++ b/esphomeyaml/components/sensor/hlw8012.py @@ -6,8 +6,9 @@ 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, PollingComponent, Pvariable, add, gpio_output_pin_expression, \ - setup_component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import PollingComponent, App HLW8012Component = sensor.sensor_ns.class_('HLW8012Component', PollingComponent) HLW8012VoltageSensor = sensor.sensor_ns.class_('HLW8012VoltageSensor', sensor.EmptySensor) diff --git a/esphomeyaml/components/sensor/hmc5883l.py b/esphomeyaml/components/sensor/hmc5883l.py index a4ed04cf0a..61d9cb1c84 100644 --- a/esphomeyaml/components/sensor/hmc5883l.py +++ b/esphomeyaml/components/sensor/hmc5883l.py @@ -4,7 +4,9 @@ import voluptuous as vol import esphomeyaml.config_validation as cv 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, setup_component, PollingComponent +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import PollingComponent, App DEPENDENCIES = ['i2c'] @@ -37,7 +39,7 @@ def validate_range(value): value = cv.string(value) if value.endswith(u'µT') or value.endswith('uT'): value = value[:-2] - return cv.one_of(*HMC5883L_RANGES)(int(value)) + return cv.one_of(*HMC5883L_RANGES, int=True)(value) SENSOR_KEYS = [CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, diff --git a/esphomeyaml/components/sensor/homeassistant.py b/esphomeyaml/components/sensor/homeassistant.py new file mode 100644 index 0000000000..b3b2c1950b --- /dev/null +++ b/esphomeyaml/components/sensor/homeassistant.py @@ -0,0 +1,32 @@ +import voluptuous as vol + +from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ENTITY_ID, CONF_MAKE_ID, CONF_NAME +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_types import App, Application + +DEPENDENCIES = ['api'] + +MakeHomeassistantSensor = Application.struct('MakeHomeassistantSensor') +HomeassistantSensor = sensor.sensor_ns.class_('HomeassistantSensor', sensor.Sensor) + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HomeassistantSensor), + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHomeassistantSensor), + vol.Required(CONF_ENTITY_ID): cv.entity_id, +})) + + +def to_code(config): + rhs = App.make_homeassistant_sensor(config[CONF_NAME], config[CONF_ENTITY_ID]) + make = variable(config[CONF_MAKE_ID], rhs) + subs = make.Psensor + sensor.setup_sensor(subs, make.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_HOMEASSISTANT_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/htu21d.py b/esphomeyaml/components/sensor/htu21d.py index 6c4b077d80..641cfa5ad0 100644 --- a/esphomeyaml/components/sensor/htu21d.py +++ b/esphomeyaml/components/sensor/htu21d.py @@ -4,8 +4,9 @@ from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import 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 +from esphomeyaml.cpp_generator import variable, Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, PollingComponent, App DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/hx711.py b/esphomeyaml/components/sensor/hx711.py index 9f35e31cca..a46cf7842c 100644 --- a/esphomeyaml/components/sensor/hx711.py +++ b/esphomeyaml/components/sensor/hx711.py @@ -1,11 +1,12 @@ import voluptuous as vol -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, \ - setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_CLK_PIN, CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application MakeHX711Sensor = Application.struct('MakeHX711Sensor') HX711Sensor = sensor.sensor_ns.class_('HX711Sensor', sensor.PollingSensorComponent) @@ -24,7 +25,7 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ 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_GAIN): cv.one_of(*GAINS, int=True), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema)) diff --git a/esphomeyaml/components/sensor/ina219.py b/esphomeyaml/components/sensor/ina219.py index 647c099978..e03dff5323 100644 --- a/esphomeyaml/components/sensor/ina219.py +++ b/esphomeyaml/components/sensor/ina219.py @@ -6,7 +6,9 @@ import esphomeyaml.config_validation as cv 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 +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/ina3221.py b/esphomeyaml/components/sensor/ina3221.py index 0ac8dff1d7..d062ae9755 100644 --- a/esphomeyaml/components/sensor/ina3221.py +++ b/esphomeyaml/components/sensor/ina3221.py @@ -5,7 +5,9 @@ from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv 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, PollingComponent, Pvariable, add, setup_component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/max31855.py b/esphomeyaml/components/sensor/max31855.py new file mode 100644 index 0000000000..d656445cf1 --- /dev/null +++ b/esphomeyaml/components/sensor/max31855.py @@ -0,0 +1,43 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +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.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_types import Application, App + +MakeMAX31855Sensor = Application.struct('MakeMAX31855Sensor') +MAX31855Sensor = sensor.sensor_ns.class_('MAX31855Sensor', sensor.PollingSensorComponent, + spi.SPIDevice) + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(MAX31855Sensor), + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX31855Sensor), + 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): + 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_max31855_sensor(config[CONF_NAME], spi_, cs, + config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + max31855 = make.Pmax31855 + sensor.setup_sensor(max31855, make.Pmqtt, config) + setup_component(max31855, config) + + +BUILD_FLAGS = '-DUSE_MAX31855_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/max6675.py b/esphomeyaml/components/sensor/max6675.py index 5bf3907ebb..821e9169bf 100644 --- a/esphomeyaml/components/sensor/max6675.py +++ b/esphomeyaml/components/sensor/max6675.py @@ -1,13 +1,14 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor, spi from esphomeyaml.components.spi import SPIComponent +import esphomeyaml.config_validation as cv 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, setup_component +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application MakeMAX6675Sensor = Application.struct('MakeMAX6675Sensor') MAX6675Sensor = sensor.sensor_ns.class_('MAX6675Sensor', sensor.PollingSensorComponent, diff --git a/esphomeyaml/components/sensor/mhz19.py b/esphomeyaml/components/sensor/mhz19.py index 06de766952..30232deeef 100644 --- a/esphomeyaml/components/sensor/mhz19.py +++ b/esphomeyaml/components/sensor/mhz19.py @@ -5,8 +5,9 @@ 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, CONF_ID -from esphomeyaml.helpers import App, Application, PollingComponent, get_variable, setup_component, \ - variable, Pvariable +from esphomeyaml.cpp_generator import get_variable, variable, Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, PollingComponent, App DEPENDENCIES = ['uart'] diff --git a/esphomeyaml/components/sensor/mpu6050.py b/esphomeyaml/components/sensor/mpu6050.py index 8fce2d8280..cd2926f5a5 100644 --- a/esphomeyaml/components/sensor/mpu6050.py +++ b/esphomeyaml/components/sensor/mpu6050.py @@ -4,7 +4,9 @@ from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \ CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, PollingComponent, Pvariable, setup_component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/mqtt_subscribe.py b/esphomeyaml/components/sensor/mqtt_subscribe.py index e06cd456ae..20d5ee1fa7 100644 --- a/esphomeyaml/components/sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/sensor/mqtt_subscribe.py @@ -3,7 +3,9 @@ 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, setup_component, Component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component DEPENDENCIES = ['mqtt'] diff --git a/esphomeyaml/components/sensor/ms5611.py b/esphomeyaml/components/sensor/ms5611.py index de176b03db..7afb60470a 100644 --- a/esphomeyaml/components/sensor/ms5611.py +++ b/esphomeyaml/components/sensor/ms5611.py @@ -4,9 +4,9 @@ from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv 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, PollingComponent, Pvariable, add, \ - setup_component, \ - variable +from esphomeyaml.cpp_generator import Pvariable, add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/pmsx003.py b/esphomeyaml/components/sensor/pmsx003.py index fe4f2bc089..1d05c34da5 100644 --- a/esphomeyaml/components/sensor/pmsx003.py +++ b/esphomeyaml/components/sensor/pmsx003.py @@ -5,7 +5,9 @@ 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, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, get_variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component DEPENDENCIES = ['uart'] @@ -34,7 +36,7 @@ SENSORS_TO_TYPE = { def validate_pmsx003_sensors(value): - for key, types in SENSORS_TO_TYPE.iteritems(): + for key, types in SENSORS_TO_TYPE.items(): if key in value and value[CONF_TYPE] not in types: raise vol.Invalid(u"{} does not have {} sensor!".format(value[CONF_TYPE], key)) return value @@ -44,11 +46,10 @@ 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.Required(CONF_TYPE): cv.one_of(*PMSX003_TYPES, upper=True), vol.Optional(CONF_PM_1_0): cv.nameable(PMSX003_SENSOR_SCHEMA), vol.Optional(CONF_PM_2_5): cv.nameable(PMSX003_SENSOR_SCHEMA), diff --git a/esphomeyaml/components/sensor/pulse_counter.py b/esphomeyaml/components/sensor/pulse_counter.py index b73d62123e..1069575a30 100644 --- a/esphomeyaml/components/sensor/pulse_counter.py +++ b/esphomeyaml/components/sensor/pulse_counter.py @@ -1,13 +1,14 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv -from esphomeyaml import core, pins +from esphomeyaml import pins from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv 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, \ - setup_component + CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application PulseCounterCountMode = sensor.sensor_ns.enum('PulseCounterCountMode') COUNT_MODES = { @@ -16,7 +17,7 @@ COUNT_MODES = { 'DECREMENT': PulseCounterCountMode.PULSE_COUNTER_DECREMENT, } -COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES)) +COUNT_MODE_SCHEMA = cv.one_of(*COUNT_MODES, upper=True) PulseCounterBase = sensor.sensor_ns.class_('PulseCounterBase') MakePulseCounterSensor = Application.struct('MakePulseCounterSensor') @@ -26,7 +27,7 @@ PulseCounterSensorComponent = sensor.sensor_ns.class_('PulseCounterSensorCompone def validate_internal_filter(value): - if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if CORE.is_esp32: if isinstance(value, int): raise vol.Invalid("Please specify the internal filter in microseconds now " "(since 1.7.0). For example '17ms'") @@ -34,8 +35,8 @@ def validate_internal_filter(value): if value.total_microseconds > 13: raise vol.Invalid("Maximum internal filter value for ESP32 is 13us") return value - else: - return cv.positive_time_period_microseconds(value) + + return cv.positive_time_period_microseconds(value) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ @@ -48,9 +49,6 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ }), vol.Optional(CONF_INTERNAL_FILTER): validate_internal_filter, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, - - 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)) diff --git a/esphomeyaml/components/sensor/rotary_encoder.py b/esphomeyaml/components/sensor/rotary_encoder.py index 1be3d44803..bb3949f9b3 100644 --- a/esphomeyaml/components/sensor/rotary_encoder.py +++ b/esphomeyaml/components/sensor/rotary_encoder.py @@ -1,11 +1,12 @@ 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_MAKE_ID, CONF_NAME, CONF_RESOLUTION -from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable, \ - setup_component, Component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application, Component RotaryEncoderResolution = sensor.sensor_ns.enum('RotaryEncoderResolution') RESOLUTIONS = { @@ -27,7 +28,7 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ 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)), + vol.Optional(CONF_RESOLUTION): cv.one_of(*RESOLUTIONS, string=True), }).extend(cv.COMPONENT_SCHEMA.schema)) diff --git a/esphomeyaml/components/sensor/sht3xd.py b/esphomeyaml/components/sensor/sht3xd.py index ea6b81e75d..cd903f8f9b 100644 --- a/esphomeyaml/components/sensor/sht3xd.py +++ b/esphomeyaml/components/sensor/sht3xd.py @@ -2,10 +2,11 @@ import voluptuous as vol from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -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 +from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_NAME, \ + CONF_TEMPERATURE, CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, PollingComponent DEPENDENCIES = ['i2c'] diff --git a/esphomeyaml/components/sensor/tcs34725.py b/esphomeyaml/components/sensor/tcs34725.py index 8322e1f054..ad9eefc120 100644 --- a/esphomeyaml/components/sensor/tcs34725.py +++ b/esphomeyaml/components/sensor/tcs34725.py @@ -5,7 +5,9 @@ from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv 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, PollingComponent, Pvariable, add, setup_component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, PollingComponent DEPENDENCIES = ['i2c'] @@ -62,8 +64,8 @@ PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ 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_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES, lower=True), + vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS), upper=True), vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS)) diff --git a/esphomeyaml/components/sensor/template.py b/esphomeyaml/components/sensor/template.py index d34f96da5d..fe0592dd69 100644 --- a/esphomeyaml/components/sensor/template.py +++ b/esphomeyaml/components/sensor/template.py @@ -1,10 +1,11 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import 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, process_lambda, variable, Application, float_, optional, add, \ - setup_component +from esphomeyaml.cpp_generator import add, process_lambda, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, float_, optional MakeTemplateSensor = Application.struct('MakeTemplateSensor') TemplateSensor = sensor.sensor_ns.class_('TemplateSensor', sensor.PollingSensorComponent) diff --git a/esphomeyaml/components/sensor/total_daily_energy.py b/esphomeyaml/components/sensor/total_daily_energy.py index 86f4678a2b..bd2b17afd0 100644 --- a/esphomeyaml/components/sensor/total_daily_energy.py +++ b/esphomeyaml/components/sensor/total_daily_energy.py @@ -3,7 +3,9 @@ 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 +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import Application, Component, App DEPENDENCIES = ['time'] diff --git a/esphomeyaml/components/sensor/tsl2561.py b/esphomeyaml/components/sensor/tsl2561.py index 53112efc46..3849b901a1 100644 --- a/esphomeyaml/components/sensor/tsl2561.py +++ b/esphomeyaml/components/sensor/tsl2561.py @@ -1,10 +1,12 @@ import voluptuous as vol +from esphomeyaml.components import i2c, sensor import esphomeyaml.config_validation as cv -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, setup_component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application DEPENDENCIES = ['i2c'] @@ -40,7 +42,7 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ 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_GAIN): cv.one_of(*GAINS, upper=True), vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, }).extend(cv.COMPONENT_SCHEMA.schema)) diff --git a/esphomeyaml/components/sensor/ultrasonic.py b/esphomeyaml/components/sensor/ultrasonic.py index d12c4419fd..b653351c02 100644 --- a/esphomeyaml/components/sensor/ultrasonic.py +++ b/esphomeyaml/components/sensor/ultrasonic.py @@ -5,8 +5,10 @@ from esphomeyaml import pins 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, setup_component +from esphomeyaml.cpp_generator import variable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, gpio_input_pin_expression, \ + setup_component +from esphomeyaml.cpp_types import Application, App MakeUltrasonicSensor = Application.struct('MakeUltrasonicSensor') UltrasonicSensorComponent = sensor.sensor_ns.class_('UltrasonicSensorComponent', diff --git a/esphomeyaml/components/sensor/uptime.py b/esphomeyaml/components/sensor/uptime.py index 60bf9a990b..561d577b4a 100644 --- a/esphomeyaml/components/sensor/uptime.py +++ b/esphomeyaml/components/sensor/uptime.py @@ -1,9 +1,11 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable, setup_component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application MakeUptimeSensor = Application.struct('MakeUptimeSensor') UptimeSensor = sensor.sensor_ns.class_('UptimeSensor', sensor.PollingSensorComponent) diff --git a/esphomeyaml/components/sensor/wifi_signal.py b/esphomeyaml/components/sensor/wifi_signal.py index 564824b3aa..db44e11303 100644 --- a/esphomeyaml/components/sensor/wifi_signal.py +++ b/esphomeyaml/components/sensor/wifi_signal.py @@ -1,9 +1,11 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, variable, setup_component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application MakeWiFiSignalSensor = Application.struct('MakeWiFiSignalSensor') WiFiSignalSensor = sensor.sensor_ns.class_('WiFiSignalSensor', sensor.PollingSensorComponent) diff --git a/esphomeyaml/components/sensor/xiaomi_miflora.py b/esphomeyaml/components/sensor/xiaomi_miflora.py index 6d02fe7e84..5ec566be18 100644 --- a/esphomeyaml/components/sensor/xiaomi_miflora.py +++ b/esphomeyaml/components/sensor/xiaomi_miflora.py @@ -6,7 +6,7 @@ from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLE 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 +from esphomeyaml.cpp_generator import get_variable, Pvariable DEPENDENCIES = ['esp32_ble_tracker'] diff --git a/esphomeyaml/components/sensor/xiaomi_mijia.py b/esphomeyaml/components/sensor/xiaomi_mijia.py index 10a6573d6f..675dec3a67 100644 --- a/esphomeyaml/components/sensor/xiaomi_mijia.py +++ b/esphomeyaml/components/sensor/xiaomi_mijia.py @@ -6,7 +6,7 @@ from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLE 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, get_variable +from esphomeyaml.cpp_generator import get_variable, Pvariable DEPENDENCIES = ['esp32_ble_tracker'] diff --git a/esphomeyaml/components/spi.py b/esphomeyaml/components/spi.py index c9c98eae09..7171c6754a 100644 --- a/esphomeyaml/components/spi.py +++ b/esphomeyaml/components/spi.py @@ -1,40 +1,40 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins +import esphomeyaml.config_validation as cv 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, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_input_pin_expression, gpio_output_pin_expression, \ + setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns SPIComponent = esphomelib_ns.class_('SPIComponent', Component) SPIDevice = esphomelib_ns.class_('SPIDevice') +MULTI_CONF = True -SPI_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(SPIComponent), vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, vol.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, }), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN)) -CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA]) - def to_code(config): - for conf in config: - for clk in gpio_output_pin_expression(conf[CONF_CLK_PIN]): + for clk in gpio_output_pin_expression(config[CONF_CLK_PIN]): + yield + rhs = App.init_spi(clk) + spi = Pvariable(config[CONF_ID], rhs) + if CONF_MISO_PIN in config: + for miso in gpio_input_pin_expression(config[CONF_MISO_PIN]): yield - rhs = App.init_spi(clk) - spi = Pvariable(conf[CONF_ID], rhs) - if CONF_MISO_PIN in conf: - for miso in gpio_input_pin_expression(conf[CONF_MISO_PIN]): - yield - add(spi.set_miso(miso)) - if CONF_MOSI_PIN in conf: - for mosi in gpio_input_pin_expression(conf[CONF_MOSI_PIN]): - yield - add(spi.set_mosi(mosi)) + add(spi.set_miso(miso)) + if CONF_MOSI_PIN in config: + for mosi in gpio_input_pin_expression(config[CONF_MOSI_PIN]): + yield + add(spi.set_mosi(mosi)) - setup_component(spi, conf) + setup_component(spi, config) BUILD_FLAGS = '-DUSE_SPI' diff --git a/esphomeyaml/components/status_led.py b/esphomeyaml/components/status_led.py index e67621c0ee..1fd5da1b60 100644 --- a/esphomeyaml/components/status_led.py +++ b/esphomeyaml/components/status_led.py @@ -2,8 +2,9 @@ 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, \ - setup_component, Component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns StatusLEDComponent = esphomelib_ns.class_('StatusLEDComponent', Component) diff --git a/esphomeyaml/components/stepper/__init__.py b/esphomeyaml/components/stepper/__init__.py index ac2ce911ac..fe9ec064cc 100644 --- a/esphomeyaml/components/stepper/__init__.py +++ b/esphomeyaml/components/stepper/__init__.py @@ -4,8 +4,9 @@ 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 +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, templatable +from esphomeyaml.cpp_types import Action, esphomelib_ns, int32 PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -78,7 +79,7 @@ def setup_stepper_core_(stepper_var, config): def setup_stepper(stepper_var, config): - add_job(setup_stepper_core_, stepper_var, config) + CORE.add_job(setup_stepper_core_, stepper_var, config) BUILD_FLAGS = '-DUSE_STEPPER' @@ -91,8 +92,7 @@ STEPPER_SET_TARGET_ACTION_SCHEMA = vol.Schema({ @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) +def stepper_set_target_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_set_target_action(template_arg) @@ -112,8 +112,7 @@ STEPPER_REPORT_POSITION_ACTION_SCHEMA = vol.Schema({ @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) +def stepper_report_position_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_report_position_action(template_arg) diff --git a/esphomeyaml/components/stepper/a4988.py b/esphomeyaml/components/stepper/a4988.py index 281ede1110..acdc378afe 100644 --- a/esphomeyaml/components/stepper/a4988.py +++ b/esphomeyaml/components/stepper/a4988.py @@ -4,8 +4,9 @@ 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 +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import Component, App A4988 = stepper.stepper_ns.class_('A4988', stepper.Stepper, Component) diff --git a/esphomeyaml/components/stepper/uln2003.py b/esphomeyaml/components/stepper/uln2003.py new file mode 100644 index 0000000000..b0b2834d05 --- /dev/null +++ b/esphomeyaml/components/stepper/uln2003.py @@ -0,0 +1,55 @@ +import voluptuous as vol + +from esphomeyaml import pins +from esphomeyaml.components import stepper +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_PIN_A, CONF_PIN_B, CONF_PIN_C, CONF_PIN_D, \ + CONF_SLEEP_WHEN_DONE, CONF_STEP_MODE +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Component + +ULN2003StepMode = stepper.stepper_ns.enum('ULN2003StepMode') + +STEP_MODES = { + 'FULL_STEP': ULN2003StepMode.ULN2003_STEP_MODE_FULL_STEP, + 'HALF_STEP': ULN2003StepMode.ULN2003_STEP_MODE_HALF_STEP, + 'WAVE_DRIVE': ULN2003StepMode.ULN2003_STEP_MODE_WAVE_DRIVE, +} + +ULN2003 = stepper.stepper_ns.class_('ULN2003', stepper.Stepper, Component) + +PLATFORM_SCHEMA = stepper.STEPPER_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_ID): cv.declare_variable_id(ULN2003), + vol.Required(CONF_PIN_A): pins.gpio_output_pin_schema, + vol.Required(CONF_PIN_B): pins.gpio_output_pin_schema, + vol.Required(CONF_PIN_C): pins.gpio_output_pin_schema, + vol.Required(CONF_PIN_D): pins.gpio_output_pin_schema, + vol.Optional(CONF_SLEEP_WHEN_DONE): cv.boolean, + vol.Optional(CONF_STEP_MODE): cv.one_of(*STEP_MODES, upper=True, space='_') +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + for pin_a in gpio_output_pin_expression(config[CONF_PIN_A]): + yield + for pin_b in gpio_output_pin_expression(config[CONF_PIN_B]): + yield + for pin_c in gpio_output_pin_expression(config[CONF_PIN_C]): + yield + for pin_d in gpio_output_pin_expression(config[CONF_PIN_D]): + yield + rhs = App.make_uln2003(pin_a, pin_b, pin_c, pin_d) + uln = Pvariable(config[CONF_ID], rhs) + + if CONF_SLEEP_WHEN_DONE in config: + add(uln.set_sleep_when_done(config[CONF_SLEEP_WHEN_DONE])) + + if CONF_STEP_MODE in config: + add(uln.set_step_mode(STEP_MODES[config[CONF_STEP_MODE]])) + + stepper.setup_stepper(uln, config) + setup_component(uln, config) + + +BUILD_FLAGS = '-DUSE_ULN2003' diff --git a/esphomeyaml/components/substitutions.py b/esphomeyaml/components/substitutions.py new file mode 100644 index 0000000000..65b3e08402 --- /dev/null +++ b/esphomeyaml/components/substitutions.py @@ -0,0 +1,135 @@ +import logging +import re + +import voluptuous as vol + +from esphomeyaml import core +import esphomeyaml.config_validation as cv +from esphomeyaml.core import EsphomeyamlError + +_LOGGER = logging.getLogger(__name__) + +CONF_SUBSTITUTIONS = 'substitutions' + +VALID_SUBSTITUTIONS_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \ + '0123456789_' + + +def validate_substitution_key(value): + value = cv.string(value) + if not value: + raise vol.Invalid("Substitution key must not be empty") + if value[0] == '$': + value = value[1:] + if value[0].isdigit(): + raise vol.Invalid("First character in substitutions cannot be a digit.") + for char in value: + if char not in VALID_SUBSTITUTIONS_CHARACTERS: + raise vol.Invalid( + u"Substitution must only consist of upper/lowercase characters, the underscore " + u"and numbers. The character '{}' cannot be used".format(char)) + return value + + +CONFIG_SCHEMA = vol.Schema({ + validate_substitution_key: cv.string_strict, +}) + + +def to_code(config): + pass + + +VARIABLE_PROG = re.compile('\\$([{0}]+|\\{{[{0}]*\\}})'.format(VALID_SUBSTITUTIONS_CHARACTERS)) + + +def _expand_substitutions(substitutions, value, path): + if u'$' not in value: + return value + + orig_value = value + + i = 0 + while True: + m = VARIABLE_PROG.search(value, i) + if not m: + # Nothing more to match. Done + break + + i, j = m.span(0) + name = m.group(1) + if name.startswith(u'{') and name.endswith(u'}'): + name = name[1:-1] + if name not in substitutions: + _LOGGER.warning(u"Found '%s' (see %s) which looks like a substitution, but '%s' was " + u"not declared", orig_value, u'->'.join(str(x) for x in path), name) + i = j + continue + + sub = substitutions[name] + tail = value[j:] + value = value[:i] + sub + i = len(value) + value += tail + return value + + +def _substitute_item(substitutions, item, path): + if isinstance(item, list): + for i, it in enumerate(item): + sub = _substitute_item(substitutions, it, path + [i]) + if sub is not None: + item[i] = sub + elif isinstance(item, dict): + replace_keys = [] + for k, v in item.items(): + if path or k != CONF_SUBSTITUTIONS: + sub = _substitute_item(substitutions, k, path + [k]) + if sub is not None: + replace_keys.append((k, sub)) + sub = _substitute_item(substitutions, v, path + [k]) + if sub is not None: + item[k] = sub + for old, new in replace_keys: + item[new] = item[old] + del item[old] + elif isinstance(item, str): + sub = _expand_substitutions(substitutions, item, path) + if sub != item: + return sub + elif isinstance(item, core.Lambda): + sub = _expand_substitutions(substitutions, item.value, path) + if sub != item: + item.value = sub + return None + + +def do_substitution_pass(config): + if CONF_SUBSTITUTIONS not in config: + return config + + substitutions = config[CONF_SUBSTITUTIONS] + if not isinstance(substitutions, dict): + raise EsphomeyamlError(u"Substitutions must be a key to value mapping, got {}" + u"".format(type(substitutions))) + + key = '' + try: + replace_keys = [] + for key, value in substitutions.items(): + sub = validate_substitution_key(key) + if sub != key: + replace_keys.append((key, sub)) + substitutions[key] = cv.string_strict(value) + for old, new in replace_keys: + substitutions[new] = substitutions[old] + del substitutions[old] + except vol.Invalid as err: + err.path.append(key) + + raise EsphomeyamlError(u"Error while parsing substitutions: {}".format(err)) + + config[CONF_SUBSTITUTIONS] = substitutions + _substitute_item(substitutions, config, []) + + return config diff --git a/esphomeyaml/components/switch/__init__.py b/esphomeyaml/components/switch/__init__.py index d1f36cdba8..3febc06966 100644 --- a/esphomeyaml/components/switch/__init__.py +++ b/esphomeyaml/components/switch/__init__.py @@ -1,12 +1,13 @@ import voluptuous as vol -from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY, CONDITION_REGISTRY, Condition from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component import esphomeyaml.config_validation as cv 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, Nameable, Action +from esphomeyaml.cpp_generator import add, Pvariable, get_variable +from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Action, App PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -14,12 +15,15 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ switch_ns = esphomelib_ns.namespace('switch_') Switch = switch_ns.class_('Switch', Nameable) +SwitchPtr = Switch.operator('ptr') 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) +SwitchCondition = switch_ns.class_('SwitchCondition', Condition) + SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent), vol.Optional(CONF_ICON): cv.icon, @@ -63,8 +67,7 @@ SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_SWITCH_TOGGLE, SWITCH_TOGGLE_ACTION_SCHEMA) -def switch_toggle_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def switch_toggle_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) @@ -79,8 +82,7 @@ SWITCH_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_SWITCH_TURN_OFF, SWITCH_TURN_OFF_ACTION_SCHEMA) -def switch_turn_off_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def switch_turn_off_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) @@ -95,8 +97,7 @@ SWITCH_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ @ACTION_REGISTRY.register(CONF_SWITCH_TURN_ON, SWITCH_TURN_ON_ACTION_SCHEMA) -def switch_turn_on_to_code(config, action_id, arg_type): - template_arg = TemplateArguments(arg_type) +def switch_turn_on_to_code(config, action_id, arg_type, template_arg): for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) @@ -104,6 +105,36 @@ def switch_turn_on_to_code(config, action_id, arg_type): yield Pvariable(action_id, rhs, type=type) +CONF_SWITCH_IS_ON = 'switch.is_on' +SWITCH_IS_ON_CONDITION_SCHEMA = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(Switch), +}) + + +@CONDITION_REGISTRY.register(CONF_SWITCH_IS_ON, SWITCH_IS_ON_CONDITION_SCHEMA) +def switch_is_on_to_code(config, condition_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_switch_is_on_condition(template_arg) + type = SwitchCondition.template(arg_type) + yield Pvariable(condition_id, rhs, type=type) + + +CONF_SWITCH_IS_OFF = 'switch.is_off' +SWITCH_IS_OFF_CONDITION_SCHEMA = maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(Switch), +}) + + +@CONDITION_REGISTRY.register(CONF_SWITCH_IS_OFF, SWITCH_IS_OFF_CONDITION_SCHEMA) +def switch_is_off_to_code(config, condition_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_switch_is_off_condition(template_arg) + type = SwitchCondition.template(arg_type) + yield Pvariable(condition_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: diff --git a/esphomeyaml/components/switch/custom.py b/esphomeyaml/components/switch/custom.py new file mode 100644 index 0000000000..a763744ea9 --- /dev/null +++ b/esphomeyaml/components/switch/custom.py @@ -0,0 +1,36 @@ +import voluptuous as vol + +from esphomeyaml.components import switch +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_types import std_vector + +CustomSwitchConstructor = switch.switch_ns.class_('CustomSwitchConstructor') + +PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomSwitchConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_SWITCHES): + cv.ensure_list(switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(switch.Switch), + })), +}) + + +def to_code(config): + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(switch.SwitchPtr)): + yield + + rhs = CustomSwitchConstructor(template_) + custom = variable(config[CONF_ID], rhs) + for i, sens in enumerate(config[CONF_SWITCHES]): + switch.register_switch(custom.get_switch(i), sens) + + +BUILD_FLAGS = '-DUSE_CUSTOM_SWITCH' + + +def to_hass_config(data, config): + return [switch.core_to_hass_config(data, swi) for swi in config[CONF_SWITCHES]] diff --git a/esphomeyaml/components/switch/gpio.py b/esphomeyaml/components/switch/gpio.py index 039bfcb4cb..b8b8efa021 100644 --- a/esphomeyaml/components/switch/gpio.py +++ b/esphomeyaml/components/switch/gpio.py @@ -3,17 +3,27 @@ import voluptuous as vol from esphomeyaml import pins from esphomeyaml.components import switch 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 +from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_RESTORE_MODE +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component +from esphomeyaml.cpp_types import App, Application, Component MakeGPIOSwitch = Application.struct('MakeGPIOSwitch') GPIOSwitch = switch.switch_ns.class_('GPIOSwitch', switch.Switch, Component) +GPIOSwitchRestoreMode = switch.switch_ns.enum('GPIOSwitchRestoreMode') + +RESTORE_MODES = { + 'RESTORE_DEFAULT_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF, + 'RESTORE_DEFAULT_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON, + 'ALWAYS_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF, + 'ALWAYS_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON, +} 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_RESTORE_MODE): cv.one_of(*RESTORE_MODES, upper=True, space='_'), }).extend(cv.COMPONENT_SCHEMA.schema)) @@ -24,6 +34,9 @@ def to_code(config): make = variable(config[CONF_MAKE_ID], rhs) gpio = make.Pswitch_ + if CONF_RESTORE_MODE in config: + add(gpio.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]])) + switch.setup_switch(gpio, make.Pmqtt, config) setup_component(gpio, config) diff --git a/esphomeyaml/components/switch/output.py b/esphomeyaml/components/switch/output.py index 4616c2127e..79764a7fcc 100644 --- a/esphomeyaml/components/switch/output.py +++ b/esphomeyaml/components/switch/output.py @@ -3,7 +3,9 @@ import voluptuous as vol from esphomeyaml.components import output, switch import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT -from esphomeyaml.helpers import App, Application, Component, get_variable, setup_component, variable +from esphomeyaml.cpp_generator import get_variable, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component MakeOutputSwitch = Application.struct('MakeOutputSwitch') OutputSwitch = switch.switch_ns.class_('OutputSwitch', switch.Switch, Component) diff --git a/esphomeyaml/components/switch/remote_transmitter.py b/esphomeyaml/components/switch/remote_transmitter.py index effa4121d5..5afc018bda 100644 --- a/esphomeyaml/components/switch/remote_transmitter.py +++ b/esphomeyaml/components/switch/remote_transmitter.py @@ -12,7 +12,7 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL 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 ArrayInitializer, Pvariable, add, get_variable +from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, get_variable DEPENDENCIES = ['remote_transmitter'] @@ -42,7 +42,7 @@ 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)), + vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), }), vol.Optional(CONF_NEC): vol.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, @@ -53,7 +53,7 @@ PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ }), vol.Optional(CONF_SONY): vol.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, - vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)), + vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), }), vol.Optional(CONF_PANASONIC): vol.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, @@ -85,15 +85,15 @@ def transmitter_base(full_config): if key == CONF_LG: return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_NEC: + if key == CONF_NEC: return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_PANASONIC: + if key == CONF_PANASONIC: return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_SAMSUNG: + if key == CONF_SAMSUNG: return SamsungTransmitter.new(name, config[CONF_DATA]) - elif key == CONF_SONY: + if key == CONF_SONY: return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_RAW: + if key == CONF_RAW: if isinstance(config, dict): data = config[CONF_DATA] carrier_frequency = config.get(CONF_CARRIER_FREQUENCY) @@ -102,29 +102,29 @@ def transmitter_base(full_config): carrier_frequency = None return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False), carrier_frequency) - elif key == CONF_RC_SWITCH_RAW: + if key == CONF_RC_SWITCH_RAW: return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_CODE]), len(config[CONF_CODE])) - elif key == CONF_RC_SWITCH_TYPE_A: + if key == CONF_RC_SWITCH_TYPE_A: return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_GROUP]), binary_code(config[CONF_DEVICE]), config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_B: + if key == CONF_RC_SWITCH_TYPE_B: return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_C: + if key == CONF_RC_SWITCH_TYPE_C: return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_FAMILY][0]) - ord('a'), config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_D: + if key == CONF_RC_SWITCH_TYPE_D: return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_GROUP][0]) - ord('a'), config[CONF_DEVICE], config[CONF_STATE]) - else: - raise NotImplementedError("Unknown transmitter type {}".format(config)) + + raise NotImplementedError("Unknown transmitter type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/switch/restart.py b/esphomeyaml/components/switch/restart.py index c81f486dd2..f45236f896 100644 --- a/esphomeyaml/components/switch/restart.py +++ b/esphomeyaml/components/switch/restart.py @@ -1,9 +1,10 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import switch +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_types import App, Application MakeRestartSwitch = Application.struct('MakeRestartSwitch') RestartSwitch = switch.switch_ns.class_('RestartSwitch', switch.Switch) diff --git a/esphomeyaml/components/switch/shutdown.py b/esphomeyaml/components/switch/shutdown.py index cca5f7184c..a784022544 100644 --- a/esphomeyaml/components/switch/shutdown.py +++ b/esphomeyaml/components/switch/shutdown.py @@ -3,7 +3,8 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import switch from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, variable +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_types import Application, App MakeShutdownSwitch = Application.struct('MakeShutdownSwitch') ShutdownSwitch = switch.switch_ns.class_('ShutdownSwitch', switch.Switch) diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index 464d90feb7..f1dd46dcfa 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -1,12 +1,13 @@ import voluptuous as vol -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_RESTORE_STATE -from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add, bool_, \ - optional, setup_component, Component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_OPTIMISTIC, \ + CONF_RESTORE_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION +from esphomeyaml.cpp_generator import add, process_lambda, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component, NoArg, bool_, optional MakeTemplateSwitch = Application.struct('MakeTemplateSwitch') TemplateSwitch = switch.switch_ns.class_('TemplateSwitch', switch.Switch, Component) diff --git a/esphomeyaml/components/switch/uart.py b/esphomeyaml/components/switch/uart.py index 3cbe26dc36..6a5dfdeba4 100644 --- a/esphomeyaml/components/switch/uart.py +++ b/esphomeyaml/components/switch/uart.py @@ -1,11 +1,13 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml.components import switch, uart from esphomeyaml.components.uart import UARTComponent +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID from esphomeyaml.core import HexInt -from esphomeyaml.helpers import App, Application, ArrayInitializer, get_variable, variable +from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, variable +from esphomeyaml.cpp_types import App, Application +from esphomeyaml.py_compat import text_type DEPENDENCIES = ['uart'] @@ -14,11 +16,11 @@ UARTSwitch = switch.switch_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevic def validate_data(value): - if isinstance(value, unicode): + if isinstance(value, text_type): return value.encode('utf-8') - elif isinstance(value, str): + if isinstance(value, str): return value - elif isinstance(value, list): + if isinstance(value, list): return vol.Schema([cv.hex_uint8_t])(value) raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes") diff --git a/esphomeyaml/components/text_sensor/__init__.py b/esphomeyaml/components/text_sensor/__init__.py index 0348b07fed..91b56177df 100644 --- a/esphomeyaml/components/text_sensor/__init__.py +++ b/esphomeyaml/components/text_sensor/__init__.py @@ -2,11 +2,13 @@ import voluptuous as vol from esphomeyaml import automation from esphomeyaml.components import mqtt +from esphomeyaml.components.mqtt import setup_mqtt_component 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, Nameable, Trigger +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, std_string, App PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -15,6 +17,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ # pylint: disable=invalid-name text_sensor_ns = esphomelib_ns.namespace('text_sensor') TextSensor = text_sensor_ns.class_('TextSensor', Nameable) +TextSensorPtr = TextSensor.operator('ptr') MQTTTextSensor = text_sensor_ns.class_('MQTTTextSensor', mqtt.MQTTComponent) TextSensorStateTrigger = text_sensor_ns.class_('TextSensorStateTrigger', @@ -48,14 +51,14 @@ def setup_text_sensor_core_(text_sensor_var, mqtt_var, config): def setup_text_sensor(text_sensor_obj, mqtt_obj, config): sensor_var = Pvariable(config[CONF_ID], text_sensor_obj, has_side_effects=False) mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False) - add_job(setup_text_sensor_core_, sensor_var, mqtt_var, config) + CORE.add_job(setup_text_sensor_core_, sensor_var, mqtt_var, config) def register_text_sensor(var, config): text_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True) rhs = App.register_text_sensor(text_sensor_var) mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True) - add_job(setup_text_sensor_core_, text_sensor_var, mqtt_var, config) + CORE.add_job(setup_text_sensor_core_, text_sensor_var, mqtt_var, config) BUILD_FLAGS = '-DUSE_TEXT_SENSOR' diff --git a/esphomeyaml/components/text_sensor/custom.py b/esphomeyaml/components/text_sensor/custom.py new file mode 100644 index 0000000000..468e971ef5 --- /dev/null +++ b/esphomeyaml/components/text_sensor/custom.py @@ -0,0 +1,36 @@ +import voluptuous as vol + +from esphomeyaml.components import text_sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS +from esphomeyaml.cpp_generator import process_lambda, variable +from esphomeyaml.cpp_types import std_vector + +CustomTextSensorConstructor = text_sensor.text_sensor_ns.class_('CustomTextSensorConstructor') + +PLATFORM_SCHEMA = text_sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(CustomTextSensorConstructor), + vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Required(CONF_TEXT_SENSORS): + cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(text_sensor.TextSensor), + })), +}) + + +def to_code(config): + for template_ in process_lambda(config[CONF_LAMBDA], [], + return_type=std_vector.template(text_sensor.TextSensorPtr)): + yield + + rhs = CustomTextSensorConstructor(template_) + custom = variable(config[CONF_ID], rhs) + for i, sens in enumerate(config[CONF_TEXT_SENSORS]): + text_sensor.register_text_sensor(custom.get_text_sensor(i), sens) + + +BUILD_FLAGS = '-DUSE_CUSTOM_TEXT_SENSOR' + + +def to_hass_config(data, config): + return [text_sensor.core_to_hass_config(data, sens) for sens in config[CONF_TEXT_SENSORS]] diff --git a/esphomeyaml/components/text_sensor/homeassistant.py b/esphomeyaml/components/text_sensor/homeassistant.py new file mode 100644 index 0000000000..0c6a0d04d2 --- /dev/null +++ b/esphomeyaml/components/text_sensor/homeassistant.py @@ -0,0 +1,33 @@ +import voluptuous as vol + +from esphomeyaml.components import text_sensor +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ENTITY_ID, CONF_MAKE_ID, CONF_NAME +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_types import App, Application, Component + +DEPENDENCIES = ['api'] + +MakeHomeassistantTextSensor = Application.struct('MakeHomeassistantTextSensor') +HomeassistantTextSensor = text_sensor.text_sensor_ns.class_('HomeassistantTextSensor', + text_sensor.TextSensor, Component) + +PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HomeassistantTextSensor), + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHomeassistantTextSensor), + vol.Required(CONF_ENTITY_ID): cv.entity_id, +})) + + +def to_code(config): + rhs = App.make_homeassistant_text_sensor(config[CONF_NAME], config[CONF_ENTITY_ID]) + make = variable(config[CONF_MAKE_ID], rhs) + sensor_ = make.Psensor + text_sensor.setup_text_sensor(sensor_, make.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_HOMEASSISTANT_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/mqtt_subscribe.py b/esphomeyaml/components/text_sensor/mqtt_subscribe.py index c4dafadee7..826b03ebdf 100644 --- a/esphomeyaml/components/text_sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/text_sensor/mqtt_subscribe.py @@ -3,7 +3,9 @@ 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, setup_component, Component +from esphomeyaml.cpp_generator import add, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component DEPENDENCIES = ['mqtt'] diff --git a/esphomeyaml/components/text_sensor/template.py b/esphomeyaml/components/text_sensor/template.py index 264841ea4b..b482af2135 100644 --- a/esphomeyaml/components/text_sensor/template.py +++ b/esphomeyaml/components/text_sensor/template.py @@ -3,8 +3,9 @@ import voluptuous as vol 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, setup_component, PollingComponent +from esphomeyaml.cpp_generator import add, process_lambda, variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, PollingComponent, optional, std_string MakeTemplateTextSensor = Application.struct('MakeTemplateTextSensor') TemplateTextSensor = text_sensor.text_sensor_ns.class_('TemplateTextSensor', diff --git a/esphomeyaml/components/text_sensor/version.py b/esphomeyaml/components/text_sensor/version.py index 1bfbac7fe6..6c64575dd4 100644 --- a/esphomeyaml/components/text_sensor/version.py +++ b/esphomeyaml/components/text_sensor/version.py @@ -1,7 +1,9 @@ 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, setup_component, Component +from esphomeyaml.cpp_generator import variable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Application, Component MakeVersionTextSensor = Application.struct('MakeVersionTextSensor') VersionTextSensor = text_sensor.text_sensor_ns.class_('VersionTextSensor', diff --git a/esphomeyaml/components/time/__init__.py b/esphomeyaml/components/time/__init__.py index 30a4f5e7d7..8b78b0dfaa 100644 --- a/esphomeyaml/components/time/__init__.py +++ b/esphomeyaml/components/time/__init__.py @@ -8,8 +8,10 @@ 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, \ - ArrayInitializer, Component, Trigger +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import add, Pvariable, ArrayInitializer +from esphomeyaml.cpp_types import esphomelib_ns, Component, NoArg, Trigger, App +from esphomeyaml.py_compat import string_types _LOGGER = logging.getLogger(__name__) @@ -29,9 +31,9 @@ def _tz_timedelta(td): offset_second = int(abs(td.total_seconds())) % 60 if offset_hour == 0 and offset_minute == 0 and offset_second == 0: return '0' - elif offset_minute == 0 and offset_second == 0: + if offset_minute == 0 and offset_second == 0: return '{}'.format(offset_hour) - elif offset_second == 0: + if offset_second == 0: return '{}:{}'.format(offset_hour, offset_minute) return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second) @@ -118,7 +120,7 @@ def detect_tz(): 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)") + "detect the timezone please install tzlocal (pip install tzlocal)") try: tz = tzlocal.get_localzone() except pytz.exceptions.UnknownTimeZoneError: @@ -130,7 +132,7 @@ def detect_tz(): def _parse_cron_int(value, special_mapping, message): special_mapping = special_mapping or {} - if isinstance(value, (str, unicode)) and value in special_mapping: + if isinstance(value, string_types) and value in special_mapping: return special_mapping[value] try: return int(value) @@ -139,7 +141,7 @@ def _parse_cron_int(value, special_mapping, message): def _parse_cron_part(part, min_value, max_value, special_mapping): - if part == '*' or part == '?': + if part in ('*', '?'): return set(x for x in range(min_value, max_value + 1)) if '/' in part: data = part.split('/') @@ -297,7 +299,7 @@ def setup_time_core_(time_var, config): def setup_time(time_var, config): - add_job(setup_time_core_, time_var, config) + CORE.add_job(setup_time_core_, time_var, config) BUILD_FLAGS = '-DUSE_TIME' diff --git a/esphomeyaml/components/time/homeassistant.py b/esphomeyaml/components/time/homeassistant.py new file mode 100644 index 0000000000..3ebe3ca8d4 --- /dev/null +++ b/esphomeyaml/components/time/homeassistant.py @@ -0,0 +1,25 @@ +from esphomeyaml.components import time as time_ +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App + + +DEPENDENCIES = ['api'] + +HomeAssistantTime = time_.time_ns.class_('HomeAssistantTime', time_.RealTimeClockComponent) + +PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HomeAssistantTime), +}).extend(cv.COMPONENT_SCHEMA.schema) + + +def to_code(config): + rhs = App.make_homeassistant_time_component() + ha_time = Pvariable(config[CONF_ID], rhs) + time_.setup_time(ha_time, config) + setup_component(ha_time, config) + + +BUILD_FLAGS = '-DUSE_HOMEASSISTANT_TIME' diff --git a/esphomeyaml/components/time/sntp.py b/esphomeyaml/components/time/sntp.py index 0422c8b6fa..15d74f3dfd 100644 --- a/esphomeyaml/components/time/sntp.py +++ b/esphomeyaml/components/time/sntp.py @@ -1,16 +1,17 @@ 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, add, setup_component +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_ID, CONF_SERVERS +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App 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_, + vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list(cv.domain), vol.Length(min=1, max=3)), }).extend(cv.COMPONENT_SCHEMA.schema) @@ -18,7 +19,9 @@ def to_code(config): rhs = App.make_sntp_component() sntp = Pvariable(config[CONF_ID], rhs) if CONF_SERVERS in config: - add(sntp.set_servers(*config[CONF_SERVERS])) + servers = config[CONF_SERVERS] + servers += [''] * (3 - len(servers)) + add(sntp.set_servers(*servers)) time_.setup_time(sntp, config) setup_component(sntp, config) diff --git a/esphomeyaml/components/uart.py b/esphomeyaml/components/uart.py index 8f5b9463fb..2e4d5b7704 100644 --- a/esphomeyaml/components/uart.py +++ b/esphomeyaml/components/uart.py @@ -1,31 +1,31 @@ import voluptuous as vol -import esphomeyaml.config_validation as cv from esphomeyaml import pins +import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN -from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, setup_component, Component +from esphomeyaml.cpp_generator import Pvariable +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import App, Component, esphomelib_ns UARTComponent = esphomelib_ns.class_('UARTComponent', Component) UARTDevice = esphomelib_ns.class_('UARTDevice') +MULTI_CONF = True -UART_SCHEMA = vol.All(vol.Schema({ +CONFIG_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, }).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) -CONFIG_SCHEMA = vol.All(cv.ensure_list, [UART_SCHEMA]) - def to_code(config): - for conf in 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]) - var = Pvariable(conf[CONF_ID], rhs) + tx = config.get(CONF_TX_PIN, -1) + rx = config.get(CONF_RX_PIN, -1) + rhs = App.init_uart(tx, rx, config[CONF_BAUD_RATE]) + var = Pvariable(config[CONF_ID], rhs) - setup_component(var, conf) + setup_component(var, config) BUILD_FLAGS = '-DUSE_UART' diff --git a/esphomeyaml/components/web_server.py b/esphomeyaml/components/web_server.py index 8a3a06436d..d70bc25cda 100644 --- a/esphomeyaml/components/web_server.py +++ b/esphomeyaml/components/web_server.py @@ -1,10 +1,11 @@ import voluptuous as vol -from esphomeyaml import core 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 +from esphomeyaml.const import CONF_CSS_URL, CONF_ID, CONF_JS_URL, CONF_PORT +from esphomeyaml.core import CORE +from esphomeyaml.cpp_generator import Pvariable, add +from esphomeyaml.cpp_helpers import setup_component +from esphomeyaml.cpp_types import esphomelib_ns, StoringController, Component, App WebServer = esphomelib_ns.class_('WebServer', Component, StoringController) @@ -31,6 +32,8 @@ BUILD_FLAGS = '-DUSE_WEB_SERVER' def lib_deps(config): - if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: - return 'FS' - return '' + deps = [] + if CORE.is_esp32: + deps.append('FS') + deps.append('ESP Async WebServer@1.1.1') + return deps diff --git a/esphomeyaml/components/wifi.py b/esphomeyaml/components/wifi.py index a627f9bb73..eaf5f8a849 100644 --- a/esphomeyaml/components/wifi.py +++ b/esphomeyaml/components/wifi.py @@ -1,12 +1,25 @@ import voluptuous as vol -from esphomeyaml import core 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, \ - Component + 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, CONF_NETWORKS, CONF_BSSID +from esphomeyaml.core import CORE, HexInt +from esphomeyaml.cpp_generator import Pvariable, StructInitializer, add, variable, ArrayInitializer +from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns + + +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': WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE, + 'LIGHT': WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, + 'HIGH': WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, +} def validate_password(value): @@ -41,52 +54,54 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({ }) WIFI_NETWORK_BASE = vol.Schema({ - vol.Required(CONF_SSID): cv.ssid, + cv.GenerateID(): cv.declare_variable_id(WiFiAP), + vol.Optional(CONF_SSID): cv.ssid, vol.Optional(CONF_PASSWORD): validate_password, vol.Optional(CONF_CHANNEL): validate_channel, - vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA, + vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, }) WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend({ - vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA, + }) WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({ - vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, + vol.Optional(CONF_BSSID): cv.mac_address, }) def validate(config): if CONF_PASSWORD in config and CONF_SSID not in config: raise vol.Invalid("Cannot have WiFi password without SSID!") - if CONF_SSID not in config and CONF_AP not in config: + + if CONF_SSID in config: + network = {CONF_SSID: config.pop(CONF_SSID)} + if CONF_PASSWORD in config: + network[CONF_PASSWORD] = config.pop(CONF_PASSWORD) + if CONF_NETWORKS in config: + raise vol.Invalid("You cannot use the 'ssid:' option together with 'networks:'. Please " + "copy your network into the 'networks:' key") + config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network) + + if (CONF_NETWORKS not in config) and (CONF_AP not in config): raise vol.Invalid("Please specify at least an SSID or an Access Point " "to create.") return config -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': WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE, - 'LIGHT': WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, - 'HIGH': WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, -} - CONFIG_SCHEMA = vol.All(vol.Schema({ cv.GenerateID(): cv.declare_variable_id(WiFiComponent), + vol.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), + vol.Optional(CONF_SSID): cv.ssid, vol.Optional(CONF_PASSWORD): validate_password, vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, + vol.Optional(CONF_AP): WIFI_NETWORK_AP, vol.Optional(CONF_HOSTNAME): cv.hostname, vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds, - vol.Optional(CONF_POWER_SAVE_MODE): vol.All(vol.Upper, cv.one_of(*WIFI_POWER_SAVE_MODES)), + vol.Optional(CONF_POWER_SAVE_MODE): cv.one_of(*WIFI_POWER_SAVE_MODES, upper=True), }), validate) @@ -109,25 +124,39 @@ def manual_ip(config): ) -def wifi_network(config): - return StructInitializer( - WiFiAp, - ('ssid', config.get(CONF_SSID, "")), - ('password', config.get(CONF_PASSWORD, "")), - ('channel', config.get(CONF_CHANNEL, -1)), - ('manual_ip', manual_ip(config.get(CONF_MANUAL_IP))), - ) +def wifi_network(config, static_ip): + ap = variable(config[CONF_ID], WiFiAP()) + if CONF_SSID in config: + add(ap.set_ssid(config[CONF_SSID])) + if CONF_PASSWORD in config: + add(ap.set_password(config[CONF_PASSWORD])) + if CONF_BSSID in config: + bssid = [HexInt(i) for i in config[CONF_BSSID].parts] + add(ap.set_bssid(ArrayInitializer(*bssid, multiline=False))) + if CONF_CHANNEL in config: + add(ap.set_channel(config[CONF_CHANNEL])) + if static_ip is not None: + add(ap.set_manual_ip(manual_ip(static_ip))) + + return ap + + +def get_upload_host(config): + if CONF_MANUAL_IP in config: + return str(config[CONF_MANUAL_IP][CONF_STATIC_IP]) + hostname = config.get(CONF_HOSTNAME) or CORE.name + return hostname + config[CONF_DOMAIN] def to_code(config): rhs = App.init_wifi() wifi = Pvariable(config[CONF_ID], rhs) - if CONF_SSID in config: - add(wifi.set_sta(wifi_network(config))) + for network in config.get(CONF_NETWORKS, []): + add(wifi.add_sta(wifi_network(network, config.get(CONF_MANUAL_IP)))) if CONF_AP in config: - add(wifi.set_ap(wifi_network(config[CONF_AP]))) + add(wifi.set_ap(wifi_network(config[CONF_AP], config.get(CONF_MANUAL_IP)))) if CONF_HOSTNAME in config: add(wifi.set_hostname(config[CONF_HOSTNAME])) @@ -140,6 +169,8 @@ def to_code(config): def lib_deps(config): - if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + if CORE.is_esp8266: return 'ESP8266WiFi' - return None + if CORE.is_esp32: + return None + raise NotImplementedError diff --git a/esphomeyaml/config.json b/esphomeyaml/config.json index bfa362fb5e..5ba6b85fc4 100644 --- a/esphomeyaml/config.json +++ b/esphomeyaml/config.json @@ -1,8 +1,8 @@ { "name": "esphomeyaml", - "version": "1.9.0b1", + "version": "1.9.3", "slug": "esphomeyaml", - "description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.", + "description": "esphomeyaml Hass.io add-on for intelligently managing all your ESP8266/ESP32 devices.", "url": "https://esphomelib.com/esphomeyaml/index.html", "startup": "application", "webui": "http://[HOST]:[PORT:6052]", diff --git a/esphomeyaml/config.py b/esphomeyaml/config.py index 9aaefbe860..bac42aaf63 100644 --- a/esphomeyaml/config.py +++ b/esphomeyaml/config.py @@ -1,25 +1,28 @@ from __future__ import print_function -import importlib -import logging from collections import OrderedDict +import importlib +import json +import logging +import re import voluptuous as vol -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, MockObjClass +from esphomeyaml import core, core_config, yaml_util +from esphomeyaml.components import substitutions +from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, ESP_PLATFORMS +from esphomeyaml.core import CORE, EsphomeyamlError +from esphomeyaml.helpers import color, indent +from esphomeyaml.py_compat import text_type from esphomeyaml.util import safe_print +# pylint: disable=unused-import, wrong-import-order +from typing import List, Optional, Tuple, Union # noqa +from esphomeyaml.core import ConfigType # noqa + _LOGGER = logging.getLogger(__name__) -REQUIRED_COMPONENTS = [ - CONF_ESPHOMEYAML, CONF_WIFI -] _COMPONENT_CACHE = {} -_ALL_COMPONENTS = [] def get_component(domain): @@ -48,11 +51,16 @@ def is_platform_component(component): def iter_components(config): - for domain, conf in config.iteritems(): + for domain, conf in config.items(): if domain == CONF_ESPHOMEYAML: + yield CONF_ESPHOMEYAML, core_config, conf continue component = get_component(domain) - yield domain, component, conf + if getattr(component, 'MULTI_CONF', False): + for conf_ in conf: + yield domain, component, conf_ + else: + yield domain, component, conf if is_platform_component(component): for p_config in conf: p_name = u"{}.{}".format(domain, p_config[CONF_PLATFORM]) @@ -60,65 +68,133 @@ def iter_components(config): yield p_name, platform, p_config +ConfigPath = List[Union[str, int]] + + +def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool + if len(path) < len(other): + return False + return path[:len(other)] == other + + +def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool + ret = _path_begins_with_(path, other) + return ret + + class Config(OrderedDict): def __init__(self): super(Config, self).__init__() - self.errors = [] + self.errors = [] # type: List[Tuple[basestring, ConfigPath]] + self.domains = [] # type: List[Tuple[ConfigPath, basestring]] - def add_error(self, message, domain=None, config=None): - if not isinstance(message, unicode): - message = unicode(message) - self.errors.append((message, domain, config)) + def add_error(self, message, path): + # type: (basestring, ConfigPath) -> None + if not isinstance(message, text_type): + message = text_type(message) + self.errors.append((message, path)) + + def add_domain(self, path, name): + # type: (ConfigPath, basestring) -> None + self.domains.append((path, name)) + + def remove_domain(self, path, name): + self.domains.remove((path, name)) + + def lookup_domain(self, path): + # type: (ConfigPath) -> Optional[basestring] + best_len = 0 + best_domain = None + for d_path, domain in self.domains: + if len(d_path) < best_len: + continue + if _path_begins_with(path, d_path): + best_len = len(d_path) + best_domain = domain + return best_domain + + def is_in_error_path(self, path): + for _, p in self.errors: + if _path_begins_with(p, path): + return True + return False + + def get_error_for_path(self, path): + for msg, p in self.errors: + if self.nested_item_path(p) == path: + return msg + return None + + def nested_item(self, path): + data = self + for item_index in path: + try: + data = data[item_index] + except (KeyError, IndexError, TypeError): + return {} + return data + + def nested_item_path(self, path): + data = self + part = [] + for item_index in path: + try: + data = data[item_index] + except (KeyError, IndexError, TypeError): + return part + part.append(item_index) + return part -def iter_ids(config, prefix=None, parent=None): - prefix = prefix or [] - parent = parent or {} +def iter_ids(config, path=None): + path = path or [] if isinstance(config, core.ID): - yield config, prefix, parent + yield config, path elif isinstance(config, core.Lambda): for id in config.requires_ids: - yield id, prefix, parent + yield id, path elif isinstance(config, list): for i, item in enumerate(config): - for result in iter_ids(item, prefix + [str(i)], config): + for result in iter_ids(item, path + [i]): yield result elif isinstance(config, dict): - for key, value in config.iteritems(): - for result in iter_ids(value, prefix + [str(key)], config): + for key, value in config.items(): + for result in iter_ids(value, path + [key]): yield result -def do_id_pass(result): - declare_ids = [] - searching_ids = [] - for id, prefix, config in iter_ids(result): +def do_id_pass(result): # type: (Config) -> None + from esphomeyaml.cpp_generator import MockObjClass + + declare_ids = [] # type: List[Tuple[core.ID, ConfigPath]] + searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] + for id, path in iter_ids(result): if id.is_declaration: if id.id is not None and any(v[0].id == id.id for v in declare_ids): - result.add_error("ID {} redefined!".format(id.id), '.'.join(prefix), config) + result.add_error(u"ID {} redefined!".format(id.id), path) continue - declare_ids.append((id, prefix, config)) + declare_ids.append((id, path)) else: - searching_ids.append((id, prefix, config)) + searching_ids.append((id, path)) # Resolve default ids after manual IDs - for id, _, _ in declare_ids: + for id, _ in declare_ids: id.resolve([v[0].id for v in declare_ids]) # Check searched IDs - for id, prefix, config in searching_ids: + for id, path in searching_ids: 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) + result.add_error("Couldn't find ID '{}'".format(id.id), path) 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)) + "".format(id.id, match.type, id.type), path) if id.id is None and id.type is not None: for v in declare_ids: @@ -129,173 +205,247 @@ def do_id_pass(result): id.id = v[0].id break else: - result.add_error("Couldn't resolve ID for type {}".format(id.type), - '.'.join(prefix), config) + result.add_error("Couldn't resolve ID for type '{}'".format(id.type), path) def validate_config(config): - global _ALL_COMPONENTS - - for req in REQUIRED_COMPONENTS: - if req not in config: - raise ESPHomeYAMLError("Component {} is required for esphomeyaml.".format(req)) - - _ALL_COMPONENTS = list(config.keys()) - result = Config() - def _comp_error(ex, domain, config): - result.add_error(_format_config_error(ex, domain, config), domain, config) + def _comp_error(ex, path): + # type: (vol.Invalid, List[basestring]) -> None + if isinstance(ex, vol.MultipleInvalid): + errors = ex.errors + else: + errors = [ex] + + for e in errors: + path_ = path + e.path + domain = result.lookup_domain(path_) or '' + result.add_error(_format_vol_invalid(e, config, path, domain), path_) + + skip_paths = list() # type: List[ConfigPath] # Step 1: Load everything - for domain, conf in config.iteritems(): + result.add_domain([CONF_ESPHOMEYAML], CONF_ESPHOMEYAML) + result[CONF_ESPHOMEYAML] = config[CONF_ESPHOMEYAML] + + for domain, conf in config.items(): domain = str(domain) - if domain == CONF_ESPHOMEYAML or domain.startswith('.'): + if domain == CONF_ESPHOMEYAML or domain.startswith(u'.'): + skip_paths.append([domain]) continue + result.add_domain([domain], domain) + result[domain] = conf if conf is None: - conf = {} + result[domain] = conf = {} component = get_component(domain) if component is None: - result.add_error(u"Component not found: {}".format(domain), domain, conf) + result.add_error(u"Component not found: {}".format(domain), [domain]) + skip_paths.append([domain]) continue - if not hasattr(component, 'PLATFORM_SCHEMA'): - continue - - for p_config in conf: - if not isinstance(p_config, dict): - result.add_error(u"Platform schemas must have 'platform:' key", ) - continue - p_name = p_config.get(u'platform') - if p_name is None: - result.add_error(u"No platform specified for {}".format(domain)) - continue - p_domain = u'{}.{}'.format(domain, p_name) - platform = get_platform(domain, p_name) - if platform is None: - result.add_error(u"Platform not found: '{}'".format(p_domain), p_domain, p_config) - continue - - # Step 2: Validate configuration - try: - result[CONF_ESPHOMEYAML] = core_config.CONFIG_SCHEMA(config[CONF_ESPHOMEYAML]) - except vol.Invalid as ex: - _comp_error(ex, CONF_ESPHOMEYAML, config[CONF_ESPHOMEYAML]) - - for domain, conf in config.iteritems(): - if domain == CONF_ESPHOMEYAML or domain.startswith('.'): - continue - if conf is None: - conf = {} - domain = str(domain) - component = get_component(domain) - if component is None: - continue - - esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS) - if core.ESP_PLATFORM not in esp_platforms: - result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM), - domain, conf) - continue + if not isinstance(conf, list) and getattr(component, 'MULTI_CONF', False): + result[domain] = conf = [conf] success = True dependencies = getattr(component, 'DEPENDENCIES', []) for dependency in dependencies: - if dependency not in _ALL_COMPONENTS: + if dependency not in config: result.add_error(u"Component {} requires component {}".format(domain, dependency), - domain, conf) + [domain]) success = False if not success: + skip_paths.append([domain]) continue - if hasattr(component, 'CONFIG_SCHEMA'): - try: - validated = component.CONFIG_SCHEMA(conf) - result[domain] = validated - except vol.Invalid as ex: - _comp_error(ex, domain, conf) - continue + success = True + conflicts_with = getattr(component, 'CONFLICTS_WITH', []) + for conflict in conflicts_with: + if conflict in config: + result.add_error(u"Component {} cannot be used together with component {}" + u"".format(domain, conflict), [domain]) + success = False + if not success: + skip_paths.append([domain]) + continue + + esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS) + if CORE.esp_platform not in esp_platforms: + result.add_error(u"Component {} doesn't support {}.".format(domain, CORE.esp_platform), + [domain]) + skip_paths.append([domain]) + continue if not hasattr(component, 'PLATFORM_SCHEMA'): continue - platforms = [] - for p_config in conf: + result.remove_domain([domain], domain) + + if not isinstance(conf, list) and conf: + result[domain] = conf = [conf] + + for i, p_config in enumerate(conf): if not isinstance(p_config, dict): + result.add_error(u"Platform schemas must have 'platform:' key", [domain, i]) + skip_paths.append([domain, i]) continue - p_name = p_config.get(u'platform') + p_name = p_config.get('platform') if p_name is None: + result.add_error(u"No platform specified for {}".format(domain), [domain, i]) + skip_paths.append([domain, i]) continue p_domain = u'{}.{}'.format(domain, p_name) + result.add_domain([domain, i], p_domain) platform = get_platform(domain, p_name) if platform is None: + result.add_error(u"Platform not found: '{}'".format(p_domain), [domain, i]) + skip_paths.append([domain, i]) continue success = True dependencies = getattr(platform, 'DEPENDENCIES', []) for dependency in dependencies: - if dependency not in _ALL_COMPONENTS: - result.add_error( - u"Platform {} requires component {}".format(p_domain, dependency), - p_domain, p_config) + if dependency not in config: + result.add_error(u"Platform {} requires component {}" + u"".format(p_domain, dependency), [domain, i]) success = False if not success: + skip_paths.append([domain, i]) + continue + + success = True + conflicts_with = getattr(platform, 'CONFLICTS_WITH', []) + for conflict in conflicts_with: + if conflict in config: + result.add_error(u"Platform {} cannot be used together with component {}" + u"".format(p_domain, conflict), [domain, i]) + success = False + if not success: + skip_paths.append([domain, i]) continue esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS) - if core.ESP_PLATFORM not in esp_platforms: - result.add_error( - u"Platform {} doesn't support {}.".format(p_domain, core.ESP_PLATFORM), - p_domain, p_config) + if CORE.esp_platform not in esp_platforms: + result.add_error(u"Platform {} doesn't support {}." + u"".format(p_domain, CORE.esp_platform), [domain, i]) + skip_paths.append([domain, i]) continue - if hasattr(platform, u'PLATFORM_SCHEMA'): + # Step 2: Validate configuration + try: + result[CONF_ESPHOMEYAML] = core_config.CONFIG_SCHEMA(result[CONF_ESPHOMEYAML]) + except vol.Invalid as ex: + _comp_error(ex, [CONF_ESPHOMEYAML]) + + for domain, conf in result.items(): + domain = str(domain) + if [domain] in skip_paths: + continue + component = get_component(domain) + + if hasattr(component, 'CONFIG_SCHEMA'): + multi_conf = getattr(component, 'MULTI_CONF', False) + + if multi_conf: + for i, conf_ in enumerate(conf): + try: + validated = component.CONFIG_SCHEMA(conf_) + result[domain][i] = validated + except vol.Invalid as ex: + _comp_error(ex, [domain, i]) + else: + try: + validated = component.CONFIG_SCHEMA(conf) + result[domain] = validated + except vol.Invalid as ex: + _comp_error(ex, [domain]) + continue + + if not hasattr(component, 'PLATFORM_SCHEMA'): + continue + + for i, p_config in enumerate(conf): + if [domain, i] in skip_paths: + continue + p_name = p_config['platform'] + platform = get_platform(domain, p_name) + + if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: - _comp_error(ex, p_domain, p_config) + _comp_error(ex, [domain, i]) continue - platforms.append(p_validated) - result[domain] = platforms + result[domain][i] = p_validated - do_id_pass(result) + if not result.errors: + # Only parse IDs if no validation error. Otherwise + # user gets confusing messages + do_id_pass(result) return result -REQUIRED = ['esphomeyaml', 'wifi'] +def _nested_getitem(data, path): + for item_index in path: + try: + data = data[item_index] + except (KeyError, IndexError, TypeError): + return None + return data -def _format_config_error(ex, domain, config): - message = u"Invalid config for [{}]: ".format(domain) +def humanize_error(config, validation_error): + offending_item_summary = _nested_getitem(config, validation_error.path) + if isinstance(offending_item_summary, dict): + try: + offending_item_summary = json.dumps(offending_item_summary) + except (TypeError, ValueError): + pass + validation_error = text_type(validation_error) + m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error) + if m is not None: + validation_error = m.group(1) + validation_error = validation_error.strip() + if not validation_error.endswith(u'.'): + validation_error += u'.' + if offending_item_summary is None: + return validation_error + return u"{} Got '{}'".format(validation_error, offending_item_summary) + + +def _format_vol_invalid(ex, config, path, domain): + # type: (vol.Invalid, ConfigType, ConfigPath, basestring) -> unicode + message = u'' if u'extra keys not allowed' in ex.error_message: - message += u'[{}] is an invalid option for [{}]. Check: {}->{}.' \ - .format(ex.path[-1], domain, domain, - u'->'.join(str(m) for m in ex.path)) + try: + paren = ex.path[-2] + except IndexError: + paren = domain + message += u'[{}] is an invalid option for [{}].'.format(ex.path[-1], paren) + elif u'required key not provided' in ex.error_message: + try: + paren = ex.path[-2] + except IndexError: + paren = domain + message += u"'{}' is a required option for [{}].".format(ex.path[-1], paren) else: - message += u'{}.'.format(humanize_error(config, ex)) - - if isinstance(config, list): - return message - - domain_config = config.get(domain, config) - message += u" (See {}, line {}). ".format( - getattr(domain_config, '__config_file__', '?'), - getattr(domain_config, '__line__', '?')) + message += humanize_error(_nested_getitem(config, path), ex) return message -def load_config(path): +def load_config(): try: - config = yaml_util.load_yaml(path) + config = yaml_util.load_yaml(CORE.config_path) except OSError: - raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path)) - core.RAW_CONFIG = config + raise EsphomeyamlError(u"Invalid YAML at {}".format(CORE.config_path)) + CORE.raw_config = config + config = substitutions.do_substitution_pass(config) core_config.preload_core_config(config) try: result = validate_config(config) - except ESPHomeYAMLError: + except EsphomeyamlError: raise except Exception: _LOGGER.error(u"Unexpected exception while reading configuration:") @@ -304,60 +454,151 @@ def load_config(path): return result -def line_info(obj, **kwargs): +def line_info(obj, highlight=True): """Display line config source.""" + if not highlight: + return None if hasattr(obj, '__config_file__'): return color('cyan', "[source {}:{}]" - .format(obj.__config_file__, obj.__line__ or '?'), - **kwargs) - return '?' + .format(obj.__config_file__, obj.__line__ or '?')) + return None -def dump_dict(layer, indent_count=3, listi=False, **kwargs): - def sort_dict_key(val): - """Return the dict key for sorting.""" - key = str.lower(val[0]) - return '0' if key == 'platform' else key - - indent_str = indent_count * ' ' - if listi or isinstance(layer, list): - indent_str = indent_str[:-1] + '-' - if isinstance(layer, dict): - for key, value in sorted(layer.items(), key=sort_dict_key): - if isinstance(value, (dict, list)): - safe_print(u"{} {}: {}".format(indent_str, key, line_info(value, **kwargs))) - dump_dict(value, indent_count + 2) - else: - safe_print(u"{} {}: {}".format(indent_str, key, value)) - indent_str = indent_count * ' ' - if isinstance(layer, (list, tuple)): - for i in layer: - if isinstance(i, dict): - dump_dict(i, indent_count + 2, True) - else: - safe_print(u" {} {}".format(indent_str, i)) +def _print_on_next_line(obj): + if isinstance(obj, (list, tuple, dict)): + return True + if isinstance(obj, str): + return len(obj) > 80 + if isinstance(obj, core.Lambda): + return len(obj.value) > 80 + return False -def read_config(path): +def dump_dict(config, path, at_root=True): + # type: (Config, ConfigPath, bool) -> Tuple[unicode, bool] + conf = config.nested_item(path) + ret = u'' + multiline = False + + if at_root: + error = config.get_error_for_path(path) + if error is not None: + ret += u'\n' + color('bold_red', error) + u'\n' + + if isinstance(conf, (list, tuple)): + multiline = True + if not conf: + ret += u'[]' + multiline = False + + for i in range(len(conf)): + path_ = path + [i] + error = config.get_error_for_path(path_) + if error is not None: + ret += u'\n' + color('bold_red', error) + u'\n' + + sep = u'- ' + if config.is_in_error_path(path_): + sep = color('red', sep) + msg, _ = dump_dict(config, path_, at_root=False) + msg = indent(msg) + inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_)) + if inf is not None: + msg = inf + u'\n' + msg + elif msg: + msg = msg[2:] + ret += sep + msg + u'\n' + elif isinstance(conf, dict): + multiline = True + if not conf: + ret += u'{}' + multiline = False + + for k in conf.keys(): + path_ = path + [k] + error = config.get_error_for_path(path_) + if error is not None: + ret += u'\n' + color('bold_red', error) + u'\n' + + st = u'{}: '.format(k) + if config.is_in_error_path(path_): + st = color('red', st) + msg, m = dump_dict(config, path_, at_root=False) + + inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_)) + if m: + msg = u'\n' + indent(msg) + + if inf is not None: + if m: + msg = u' ' + inf + msg + else: + msg = msg + u' ' + inf + ret += st + msg + u'\n' + elif isinstance(conf, str): + if not conf: + conf += u"''" + + if len(conf) > 80: + conf = u'|-\n' + indent(conf) + error = config.get_error_for_path(path) + col = 'bold_red' if error else 'white' + ret += color(col, text_type(conf)) + elif isinstance(conf, core.Lambda): + conf = u'!lambda |-\n' + indent(text_type(conf.value)) + error = config.get_error_for_path(path) + col = 'bold_red' if error else 'white' + ret += color(col, conf) + elif conf is None: + pass + else: + error = config.get_error_for_path(path) + col = 'bold_red' if error else 'white' + ret += color(col, text_type(conf)) + multiline = u'\n' in ret + + return ret, multiline + + +def strip_default_ids(config): + if isinstance(config, list): + to_remove = [] + for i, x in enumerate(config): + x = config[i] = strip_default_ids(x) + if isinstance(x, core.ID) and not x.is_manual: + to_remove.append(x) + for x in to_remove: + config.remove(x) + elif isinstance(config, dict): + to_remove = [] + for k, v in config.items(): + v = config[k] = strip_default_ids(v) + if isinstance(v, core.ID) and not v.is_manual: + to_remove.append(k) + for k in to_remove: + config.pop(k) + return config + + +def read_config(verbose): _LOGGER.info("Reading configuration...") try: - res = load_config(path) - except ESPHomeYAMLError as err: + res = load_config() + except EsphomeyamlError as err: _LOGGER.error(u"Error while reading config: %s", err) return None - excepts = {} - for message, domain, config in res.errors: - domain = domain or u"General Error" - excepts.setdefault(domain, []).append(message) - if config is not None: - excepts[domain].append(config) + if res.errors: + if not verbose: + res = strip_default_ids(res) - if excepts: - safe_print(color('bold_white', u"Failed config")) - for domain, config in excepts.iteritems(): - safe_print(u' {} {}'.format(color('bold_red', domain + u':'), - color('red', '', reset='red'))) - dump_dict(config, reset='red') - safe_print(color('reset')) + safe_print(color('bold_red', u"Failed config")) + safe_print('') + for path, domain in res.domains: + if not res.is_in_error_path(path): + continue + + safe_print(color('bold_red', u'{}:'.format(domain)) + u' ' + + (line_info(res.nested_item(path)) or u'')) + safe_print(indent(dump_dict(res, path)[0])) return None return OrderedDict(res) diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index dac218c76b..68e96c5b44 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -9,21 +9,23 @@ import uuid as uuid_ import voluptuous as vol -from esphomeyaml import core, helpers +from esphomeyaml import core 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, CONF_SETUP_PRIORITY -from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ + CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, \ + CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, ESP_PLATFORM_ESP32, \ + ESP_PLATFORM_ESP8266 +from esphomeyaml.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds +from esphomeyaml.py_compat import text_type, string_types, integer_types _LOGGER = logging.getLogger(__name__) # pylint: disable=invalid-name port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) -positive_float = vol.All(vol.Coerce(float), vol.Range(min=0)) -zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1)) +float_ = vol.Coerce(float) +positive_float = vol.All(float_, vol.Range(min=0)) +zero_to_one_float = vol.All(float_, vol.Range(min=0, max=1)) positive_int = vol.All(vol.Coerce(int), vol.Range(min=0)) positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False)) @@ -50,7 +52,7 @@ RESERVED_IDS = [ def alphanumeric(value): if value is None: raise vol.Invalid("string value is None") - value = unicode(value) + value = text_type(value) if not value.isalnum(): raise vol.Invalid("string value is not alphanumeric") return value @@ -69,16 +71,16 @@ def string(value): if isinstance(value, (dict, list)): raise vol.Invalid("string value cannot be dictionary or list.") if value is not None: - return unicode(value) + return text_type(value) raise vol.Invalid("string value is None") def string_strict(value): """Strictly only allow strings.""" - if isinstance(value, (str, unicode)): + if isinstance(value, string_types): return value - raise vol.Invalid("Must be string, did you forget putting quotes " - "around the value?") + raise vol.Invalid("Must be string, got {}. did you forget putting quotes " + "around the value?".format(type(value))) def icon(value): @@ -101,13 +103,25 @@ def boolean(value): return bool(value) -def ensure_list(value): +def ensure_list(*validators): """Wrap value in list if it is not one.""" - if value is None or (isinstance(value, dict) and not value): - return [] - if isinstance(value, list): - return value - return [value] + user = vol.All(*validators) + + def validator(value): + if value is None or (isinstance(value, dict) and not value): + return [] + if not isinstance(value, list): + return [user(value)] + ret = [] + for i, val in enumerate(value): + try: + ret.append(user(val)) + except vol.Invalid as err: + err.prepend([i]) + raise err + return ret + + return validator def ensure_list_not_empty(value): @@ -125,7 +139,7 @@ def ensure_dict(value): def hex_int_(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return HexInt(value) value = string_strict(value).lower() if value.startswith('0x'): @@ -134,7 +148,7 @@ def hex_int_(value): def int_(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return value value = string_strict(value).lower() if value.startswith('0x'): @@ -155,8 +169,9 @@ def variable_id_str_(value): raise vol.Invalid("Dashes are not supported in IDs, please use underscores instead.") for char in value: if char != '_' and not char.isalnum(): - raise vol.Invalid(u"IDs must only consist of upper/lowercase characters and numbers." - u"The character '{}' cannot be used".format(char)) + raise vol.Invalid(u"IDs must only consist of upper/lowercase characters, the underscore" + u"character and numbers. The character '{}' cannot be used" + u"".format(char)) if value in RESERVED_IDS: raise vol.Invalid(u"ID {} is reserved internally and cannot be used".format(value)) return value @@ -198,7 +213,7 @@ def only_on(platforms): platforms = [platforms] def validator_(obj): - if core.ESP_PLATFORM not in platforms: + if CORE.esp_platform not in platforms: raise vol.Invalid(u"This feature is only available on {}".format(platforms)) return obj @@ -258,12 +273,12 @@ TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example time_period_dict = vol.All( dict, vol.Schema({ - 'days': vol.Coerce(float), - 'hours': vol.Coerce(float), - 'minutes': vol.Coerce(float), - 'seconds': vol.Coerce(float), - 'milliseconds': vol.Coerce(float), - 'microseconds': vol.Coerce(float), + 'days': float_, + 'hours': float_, + 'minutes': float_, + 'seconds': float_, + 'milliseconds': float_, + 'microseconds': float_, }), has_at_least_one_key('days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'), @@ -296,9 +311,9 @@ 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): - 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("Don't know what '{0}' means as it has no time *unit*! Did you mean " + "'{0}s'?".format(value)) + elif not isinstance(value, string_types): raise vol.Invalid("Expected string for time period with unit.") unit_to_kwarg = { @@ -319,11 +334,11 @@ def time_period_str_unit(value): match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value) - if match is None or match.group(2) not in unit_to_kwarg: + if match is None: raise vol.Invalid(u"Expected time period with unit, " u"got {}".format(value)) + kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))] - kwarg = unit_to_kwarg[match.group(2)] return TimePeriod(**{kwarg: float(match.group(1))}) @@ -441,6 +456,13 @@ def hostname(value): return value +def domain(value): + value = string(value) + if re.match(vol.DOMAIN_REGEX, value) is None: + raise vol.Invalid("Invalid domain: {}".format(value)) + return value + + def domain_name(value): value = string(value) if not value.startswith('.'): @@ -460,16 +482,18 @@ def ssid(value): raise vol.Invalid("SSID must be a string. Did you wrap it in quotes?") if not value: raise vol.Invalid("SSID can't be empty.") - if len(value) > 31: - raise vol.Invalid("SSID can't be longer than 31 characters") + if len(value) > 32: + raise vol.Invalid("SSID can't be longer than 32 characters") return value def ipv4(value): if isinstance(value, list): parts = value - elif isinstance(value, str): + elif isinstance(value, string_types): parts = value.split('.') + elif isinstance(value, IPAddress): + return value else: raise vol.Invalid("IPv4 address must consist of either string or " "integer list") @@ -546,6 +570,14 @@ def mqtt_qos(value): return one_of(0, 1, 2)(value) +def requires_component(comp): + def validator(value): + if comp not in CORE.raw_config: + raise vol.Invalid("This option requires component {}".format(comp)) + return value + return validator + + uint8_t = vol.All(int_, vol.Range(min=0, max=255)) uint16_t = vol.All(int_, vol.Range(min=0, max=65535)) uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295)) @@ -556,7 +588,7 @@ i2c_address = hex_uint8_t def percentage(value): - has_percent_sign = isinstance(value, (str, unicode)) and value.endswith('%') + has_percent_sign = isinstance(value, string_types) and value.endswith('%') if has_percent_sign: value = float(value[:-1].rstrip()) / 100.0 if value > 1: @@ -568,7 +600,7 @@ def percentage(value): def percentage_int(value): - if isinstance(value, (str, unicode)) and value.endswith('%'): + if isinstance(value, string_types) and value.endswith('%'): value = int(value[:-1].rstrip()) return value @@ -584,10 +616,24 @@ def valid(value): return value -def one_of(*values): +def one_of(*values, **kwargs): options = u', '.join(u"'{}'".format(x) for x in values) + lower = kwargs.get('lower', False) + upper = kwargs.get('upper', False) + string_ = kwargs.get('string', False) or lower or upper + to_int = kwargs.get('int', False) + space = kwargs.get('space', ' ') def validator(value): + if string_: + value = string(value) + value = value.replace(' ', space) + if to_int: + value = int_(value) + if lower: + value = vol.Lower(value) + if upper: + value = vol.Upper(value) if value not in values: raise vol.Invalid(u"Unknown value '{}', must be one of {}".format(value, options)) return value @@ -621,7 +667,7 @@ def dimensions(value): def directory(value): value = string(value) - path = helpers.relative_path(value) + path = CORE.relative_path(value) if not os.path.exists(path): raise vol.Invalid(u"Could not find directory '{}'. Please make sure it exists.".format( path)) @@ -632,7 +678,7 @@ def directory(value): def file_(value): value = string(value) - path = helpers.relative_path(value) + path = CORE.relative_path(value) if not os.path.exists(path): raise vol.Invalid(u"Could not find file '{}'. Please make sure it exists.".format( path)) @@ -641,6 +687,20 @@ def file_(value): return value +ENTITY_ID_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789_' + + +def entity_id(value): + value = string_strict(value).lower() + if value.count('.') != 1: + raise vol.Invalid("Entity ID must have exactly one dot in it") + for x in value.split('.'): + for c in x: + if c not in ENTITY_ID_CHARACTERS: + raise vol.Invalid("Invalid character for entity ID: {}".format(c)) + return value + + class GenerateID(vol.Optional): def __init__(self, key=CONF_ID): super(GenerateID, self).__init__(key, default=lambda: None) @@ -675,17 +735,18 @@ MQTT_COMPONENT_AVAILABILITY_SCHEMA = vol.Schema({ MQTT_COMPONENT_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME): string, - vol.Optional(CONF_RETAIN): boolean, - vol.Optional(CONF_DISCOVERY): boolean, - vol.Optional(CONF_STATE_TOPIC): publish_topic, - vol.Optional(CONF_AVAILABILITY): vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA), + vol.Optional(CONF_RETAIN): vol.All(requires_component('mqtt'), boolean), + vol.Optional(CONF_DISCOVERY): vol.All(requires_component('mqtt'), boolean), + vol.Optional(CONF_STATE_TOPIC): vol.All(requires_component('mqtt'), publish_topic), + vol.Optional(CONF_AVAILABILITY): vol.All(requires_component('mqtt'), + vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA)), vol.Optional(CONF_INTERNAL): boolean, }) MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ - vol.Optional(CONF_COMMAND_TOPIC): subscribe_topic, + vol.Optional(CONF_COMMAND_TOPIC): vol.All(requires_component('mqtt'), subscribe_topic), }) COMPONENT_SCHEMA = vol.Schema({ - vol.Optional(CONF_SETUP_PRIORITY): vol.Coerce(float) + vol.Optional(CONF_SETUP_PRIORITY): float_ }) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index 25d198dc0a..38e665ea93 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -1,11 +1,11 @@ """Constants used by esphomeyaml.""" MAJOR_VERSION = 1 -MINOR_VERSION = 9 -PATCH_VERSION = '0b6' +MINOR_VERSION = 10 +PATCH_VERSION = '0b1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) -ESPHOMELIB_VERSION = '1.9.0b6' +ESPHOMELIB_VERSION = '1.10.0b1' ESP_PLATFORM_ESP32 = 'ESP32' ESP_PLATFORM_ESP8266 = 'ESP8266' @@ -28,6 +28,7 @@ CONF_BRANCH = 'branch' CONF_LOGGER = 'logger' CONF_WIFI = 'wifi' CONF_SSID = 'ssid' +CONF_BSSID = 'bssid' CONF_PASSWORD = 'password' CONF_MANUAL_IP = 'manual_ip' CONF_STATIC_IP = 'static_ip' @@ -91,6 +92,7 @@ CONF_ABOVE = 'above' CONF_BELOW = 'below' CONF_ON = 'on' CONF_IF = 'if' +CONF_WHILE = 'while' CONF_THEN = 'then' CONF_BINARY = 'binary' CONF_WHITE = 'white' @@ -221,6 +223,7 @@ CONF_ACCURACY = 'accuracy' CONF_BOARD_FLASH_MODE = 'board_flash_mode' CONF_ON_PRESS = 'on_press' CONF_ON_RELEASE = 'on_release' +CONF_ON_STATE = 'on_state' CONF_ON_CLICK = 'on_click' CONF_ON_DOUBLE_CLICK = 'on_double_click' CONF_ON_MULTI_CLICK = 'on_multi_click' @@ -252,6 +255,7 @@ CONF_IDLE = 'idle' CONF_NETWORKS = 'networks' CONF_INTERNAL = 'internal' CONF_BUILD_PATH = 'build_path' +CONF_PLATFORMIO_OPTIONS = 'platformio_options' CONF_REBOOT_TIMEOUT = 'reboot_timeout' CONF_INVERT = 'invert' CONF_DELAYED_ON = 'delayed_on' @@ -371,7 +375,29 @@ CONF_UPDATE_ON_BOOT = 'update_on_boot' CONF_INITIAL_VALUE = 'initial_value' CONF_RESTORE_VALUE = 'restore_value' CONF_PINS = 'pins' - +CONF_SENSORS = 'sensors' +CONF_BINARY_SENSORS = 'binary_sensors' +CONF_OUTPUTS = 'outputs' +CONF_SWITCHES = 'switches' +CONF_TEXT_SENSORS = 'text_sensors' +CONF_INCLUDES = 'includes' +CONF_LIBRARIES = 'libraries' +CONF_PIN_A = 'pin_a' +CONF_PIN_B = 'pin_b' +CONF_PIN_C = 'pin_c' +CONF_PIN_D = 'pin_d' +CONF_SLEEP_WHEN_DONE = 'sleep_when_done' +CONF_STEP_MODE = 'step_mode' +CONF_COMPONENTS = 'components' +CONF_DATA_TEMPLATE = 'data_template' +CONF_VARIABLES = 'variables' +CONF_SERVICE = 'service' +CONF_ENTITY_ID = 'entity_id' +CONF_RESTORE_MODE = 'restore_mode' +CONF_INTERVAL = 'interval' +CONF_DIRECTION = 'direction' +CONF_VARIANT = 'variant' +CONF_METHOD = 'method' ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_' ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage' diff --git a/esphomeyaml/core.py b/esphomeyaml/core.py index e59159471a..bc2cee62fe 100644 --- a/esphomeyaml/core.py +++ b/esphomeyaml/core.py @@ -1,14 +1,34 @@ -import math -import re +import collections from collections import OrderedDict +import inspect +import logging +import math +import os +import re + +from esphomeyaml.const import CONF_ARDUINO_VERSION, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, \ + CONF_LOCAL, CONF_WIFI, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 +from esphomeyaml.helpers import ensure_unique_string + +# pylint: disable=unused-import, wrong-import-order +from typing import Any, Dict, List # noqa + +from esphomeyaml.py_compat import integer_types, IS_PY2 + +_LOGGER = logging.getLogger(__name__) -class ESPHomeYAMLError(Exception): +class EsphomeyamlError(Exception): """General esphomeyaml exception occurred.""" - pass -class HexInt(long): +if IS_PY2: + base_int = long +else: + base_int = int + + +class HexInt(base_int): def __str__(self): if 0 <= self <= 255: return "0x{:02X}".format(self) @@ -35,14 +55,14 @@ class MACAddress(object): return ':'.join('{:02X}'.format(part) for part in self.parts) def as_hex(self): - import esphomeyaml.helpers + from esphomeyaml.cpp_generator import RawExpression num = ''.join('{:02X}'.format(part) for part in self.parts) - return esphomeyaml.helpers.RawExpression('0x{}ULL'.format(num)) + return RawExpression('0x{}ULL'.format(num)) def is_approximately_integer(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return True return abs(value - round(value)) < 0.001 @@ -195,11 +215,36 @@ class TimePeriodSeconds(TimePeriod): pass +LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') + + class Lambda(object): def __init__(self, value): - self.value = value - self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)', value) - self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)] + self._value = value + self._parts = None + self._requires_ids = None + + @property + def parts(self): + if self._parts is None: + self._parts = re.split(LAMBDA_PROG, self._value) + return self._parts + + @property + def requires_ids(self): + if self._requires_ids is None: + self._requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)] + return self._requires_ids + + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + self._value = value + self._parts = None + self._requires_ids = None def __str__(self): return self.value @@ -208,19 +253,6 @@ class Lambda(object): return u'Lambda<{}>'.format(self.value) -def ensure_unique_string(preferred_string, current_strings): - test_string = preferred_string - current_strings_set = set(current_strings) - - tries = 1 - - while test_string in current_strings_set: - tries += 1 - test_string = u"{}_{}".format(preferred_string, tries) - - return test_string - - class ID(object): def __init__(self, id, is_declaration=False, type=None): self.id = id @@ -253,8 +285,151 @@ class ID(object): return hash(self.id) -CONFIG_PATH = None -ESP_PLATFORM = '' -BOARD = '' -RAW_CONFIG = None -NAME = '' +# pylint: disable=too-many-instance-attributes +class EsphomeyamlCore(object): + def __init__(self): + # True if command is run from dashboard + self.dashboard = False + # The name of the node + self.name = None # type: str + # The relative path to the configuration YAML + self.config_path = None # type: str + # The relative path to where all build files are stored + self.build_path = None # type: str + # The platform (ESP8266, ESP32) of this device + self.esp_platform = None # type: str + # The board that's used (for example nodemcuv2) + self.board = None # type: str + # The full raw configuration + self.raw_config = {} # type: ConfigType + # The validated configuration, this is None until the config has been validated + self.config = {} # type: ConfigType + # The pending tasks in the task queue (mostly for C++ generation) + self.pending_tasks = collections.deque() + # The variable cache, for each ID this holds a MockObj of the variable obj + self.variables = {} # type: Dict[str, MockObj] + # The list of expressions for the C++ generation + self.expressions = [] # type: List[Expression] + + @property + def address(self): # type: () -> str + from esphomeyaml.components import wifi + + if 'wifi' in self.config: + return wifi.get_upload_host(self.config[CONF_WIFI]) + + if 'ethernet' in self.config: + return wifi.get_upload_host(self.config['ethernet']) + + return None + + @property + def esphomelib_version(self): # type: () -> Dict[str, str] + return self.config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION] + + @property + def is_local_esphomelib_copy(self): + return CONF_LOCAL in self.esphomelib_version + + @property + def arduino_version(self): # type: () -> str + return self.config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION] + + @property + def config_dir(self): + return os.path.dirname(self.config_path) + + @property + def config_filename(self): + return os.path.basename(self.config_path) + + def relative_path(self, *path): + path_ = os.path.expanduser(os.path.join(*path)) + return os.path.join(self.config_dir, path_) + + def relative_build_path(self, *path): + path_ = os.path.expanduser(os.path.join(*path)) + return os.path.join(self.build_path, path_) + + @property + def firmware_bin(self): + return self.relative_build_path('.pioenvs', self.name, 'firmware.bin') + + @property + def is_esp8266(self): + if self.esp_platform is None: + raise ValueError + return self.esp_platform == ESP_PLATFORM_ESP8266 + + @property + def is_esp32(self): + if self.esp_platform is None: + raise ValueError + return self.esp_platform == ESP_PLATFORM_ESP32 + + def add_job(self, func, *args, **kwargs): + domain = kwargs.get('domain') + if inspect.isgeneratorfunction(func): + def func_(): + yield + for _ in func(*args): + yield + else: + def func_(): + yield + func(*args) + gen = func_() + self.pending_tasks.append((gen, domain)) + return gen + + def flush_tasks(self): + i = 0 + while self.pending_tasks: + i += 1 + if i > 1000000: + raise EsphomeyamlError("Circular dependency detected!") + + task, domain = self.pending_tasks.popleft() + _LOGGER.debug("Executing task for domain=%s", domain) + try: + next(task) + self.pending_tasks.append((task, domain)) + except StopIteration: + _LOGGER.debug(" -> %s finished", domain) + + def add(self, expression, require=True): + from esphomeyaml.cpp_generator import Expression + + if require and isinstance(expression, Expression): + expression.require() + self.expressions.append(expression) + _LOGGER.debug("Adding: %s", expression) + return expression + + def get_variable(self, id): + while True: + if id in self.variables: + yield self.variables[id] + return + _LOGGER.debug("Waiting for variable %s", id) + yield None + + def get_variable_with_full_id(self, id): + while True: + if id in self.variables: + for k, v in self.variables.items(): + if k == id: + yield (k, v) + return + _LOGGER.debug("Waiting for variable %s", id) + yield None, None + + def register_variable(self, id, obj): + _LOGGER.debug("Registered variable %s of type %s", id.id, id.type) + self.variables[id] = obj + + +CORE = EsphomeyamlCore() + +ConfigType = Dict[str, Any] +CoreType = EsphomeyamlCore diff --git a/esphomeyaml/core_config.py b/esphomeyaml/core_config.py index 16405c5fc1..6c9bcbf05f 100644 --- a/esphomeyaml/core_config.py +++ b/esphomeyaml/core_config.py @@ -1,39 +1,39 @@ import logging import os import re -import subprocess import voluptuous as vol -from esphomeyaml import automation, core, pins +from esphomeyaml import automation, pins import esphomeyaml.config_validation as cv from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266_DEV, \ CONF_ARDUINO_VERSION, CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_BRANCH, CONF_BUILD_PATH, \ CONF_COMMIT, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, CONF_LOCAL, CONF_NAME, CONF_ON_BOOT, \ CONF_ON_LOOP, CONF_ON_SHUTDOWN, CONF_PLATFORM, CONF_PRIORITY, CONF_REPOSITORY, CONF_TAG, \ CONF_TRIGGER_ID, CONF_USE_CUSTOM_CODE, ESPHOMELIB_VERSION, ESP_PLATFORM_ESP32, \ - ESP_PLATFORM_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 + ESP_PLATFORM_ESP8266, CONF_LIBRARIES, CONF_INCLUDES, CONF_PLATFORMIO_OPTIONS +from esphomeyaml.core import CORE, EsphomeyamlError +from esphomeyaml.cpp_generator import Pvariable, RawExpression, add +from esphomeyaml.cpp_types import App, NoArg, const_char_ptr, esphomelib_ns +from esphomeyaml.py_compat import text_type _LOGGER = logging.getLogger(__name__) LIBRARY_URI_REPO = u'https://github.com/OttoWinter/esphomelib.git' +GITHUB_ARCHIVE_ZIP = u'https://github.com/OttoWinter/esphomelib/archive/{}.zip' BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout'] StartupTrigger = esphomelib_ns.StartupTrigger ShutdownTrigger = esphomelib_ns.ShutdownTrigger LoopTrigger = esphomelib_ns.LoopTrigger -VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:-beta)?(?:-alpha)?$') +VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$') def validate_board(value): - if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + if CORE.is_esp8266: board_pins = pins.ESP8266_BOARD_PINS - elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32: + elif CORE.is_esp32: board_pins = pins.ESP32_BOARD_PINS else: raise NotImplementedError @@ -53,12 +53,12 @@ def validate_simple_esphomelib_version(value): CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_TAG: 'v' + ESPHOMELIB_VERSION, } - elif value.upper() == 'DEV': + if value.upper() == 'DEV': return { CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_BRANCH: 'dev' } - elif VERSION_REGEX.match(value) is not None: + if VERSION_REGEX.match(value) is not None: return { CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_TAG: 'v' + value, @@ -68,7 +68,7 @@ def validate_simple_esphomelib_version(value): def validate_local_esphomelib_version(value): value = cv.directory(value) - path = relative_path(value) + path = CORE.relative_path(value) library_json = os.path.join(path, 'library.json') if not os.path.exists(library_json): raise vol.Invalid(u"Could not find '{}' file. '{}' does not seem to point to an " @@ -116,14 +116,14 @@ PLATFORMIO_ESP8266_LUT = { '2.4.1': 'espressif8266@1.7.3', '2.4.0': 'espressif8266@1.6.0', '2.3.0': 'espressif8266@1.5.0', - 'RECOMMENDED': 'espressif8266@>=1.8.0', + 'RECOMMENDED': 'espressif8266@1.8.0', 'LATEST': 'espressif8266', 'DEV': ARDUINO_VERSION_ESP8266_DEV, } PLATFORMIO_ESP32_LUT = { '1.0.0': 'espressif32@1.4.0', - 'RECOMMENDED': 'espressif32@>=1.4.0', + 'RECOMMENDED': 'espressif32@1.5.0', 'LATEST': 'espressif32', 'DEV': ARDUINO_VERSION_ESP32_DEV, } @@ -132,7 +132,7 @@ PLATFORMIO_ESP32_LUT = { def validate_arduino_version(value): value = cv.string_strict(value) value_ = value.upper() - if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + if CORE.is_esp8266: if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP8266_LUT: raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported " "at this time. You can override this by manually using " @@ -140,7 +140,7 @@ def validate_arduino_version(value): if value_ in PLATFORMIO_ESP8266_LUT: return PLATFORMIO_ESP8266_LUT[value_] return value - elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if CORE.is_esp32: if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT: raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported " "at this time. You can override this by manually using " @@ -148,28 +148,30 @@ def validate_arduino_version(value): if value_ in PLATFORMIO_ESP32_LUT: return PLATFORMIO_ESP32_LUT[value_] return value - else: - raise NotImplementedError + raise NotImplementedError def default_build_path(): - return core.NAME + return CORE.name CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.valid_name, - vol.Required(CONF_PLATFORM): vol.All(vol.Upper, cv.one_of('ESP8266', 'ESPRESSIF8266', - 'ESP32', 'ESPRESSIF32')), + vol.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESPRESSIF8266', 'ESP32', 'ESPRESSIF32', + upper=True), vol.Required(CONF_BOARD): validate_board, vol.Optional(CONF_ESPHOMELIB_VERSION, default='latest'): ESPHOMELIB_VERSION_SCHEMA, vol.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version, vol.Optional(CONF_USE_CUSTOM_CODE, default=False): cv.boolean, vol.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, + vol.Optional(CONF_PLATFORMIO_OPTIONS): vol.Schema({ + cv.string_strict: vol.Any([cv.string], cv.string), + }), - vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, cv.one_of(*BUILD_FLASH_MODES)), + vol.Optional(CONF_BOARD_FLASH_MODE): cv.one_of(*BUILD_FLASH_MODES, lower=True), vol.Optional(CONF_ON_BOOT): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StartupTrigger), - vol.Optional(CONF_PRIORITY): vol.Coerce(float), + vol.Optional(CONF_PRIORITY): cv.float_, }), vol.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger), @@ -177,6 +179,8 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_ON_LOOP): automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(LoopTrigger), }), + vol.Optional(CONF_INCLUDES): cv.ensure_list(cv.file_), + vol.Optional(CONF_LIBRARIES): cv.ensure_list(cv.string_strict), vol.Optional('library_uri'): cv.invalid("The library_uri option has been removed in 1.8.0 and " "was moved into the esphomelib_version option."), @@ -187,62 +191,23 @@ CONFIG_SCHEMA = vol.Schema({ def preload_core_config(config): if CONF_ESPHOMEYAML not in config: - raise ESPHomeYAMLError(u"No esphomeyaml section in config") + raise EsphomeyamlError(u"No esphomeyaml section in config") core_conf = config[CONF_ESPHOMEYAML] if CONF_PLATFORM not in core_conf: - raise ESPHomeYAMLError("esphomeyaml.platform not specified.") + raise EsphomeyamlError("esphomeyaml.platform not specified.") if CONF_BOARD not in core_conf: - raise ESPHomeYAMLError("esphomeyaml.board not specified.") + raise EsphomeyamlError("esphomeyaml.board not specified.") if CONF_NAME not in core_conf: - raise ESPHomeYAMLError("esphomeyaml.name not specified.") + raise EsphomeyamlError("esphomeyaml.name not specified.") try: - core.ESP_PLATFORM = validate_platform(core_conf[CONF_PLATFORM]) - core.BOARD = validate_board(core_conf[CONF_BOARD]) - core.NAME = cv.valid_name(core_conf[CONF_NAME]) + CORE.esp_platform = validate_platform(core_conf[CONF_PLATFORM]) + CORE.board = validate_board(core_conf[CONF_BOARD]) + CORE.name = cv.valid_name(core_conf[CONF_NAME]) + CORE.build_path = CORE.relative_path( + cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path()))) except vol.Invalid as e: - raise ESPHomeYAMLError(unicode(e)) - - -def run_command(*args): - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - rc = p.returncode - return rc, stdout, stderr - - -def update_esphomelib_repo(config): - esphomelib_version = config[CONF_ESPHOMELIB_VERSION] - if CONF_REPOSITORY not in esphomelib_version: - return - - build_path = relative_path(config[CONF_BUILD_PATH]) - esphomelib_path = os.path.join(build_path, '.piolibdeps', 'esphomelib') - is_default_branch = all(x not in esphomelib_version - for x in (CONF_BRANCH, CONF_TAG, CONF_COMMIT)) - if not (CONF_BRANCH in esphomelib_version or is_default_branch): - # Git commit hash or tag cannot be updated - return - - rc, _, _ = run_command('git', '-C', esphomelib_path, '--help') - if rc != 0: - # git not installed or repo not downloaded yet - return - rc, _, _ = run_command('git', '-C', esphomelib_path, 'diff-index', '--quiet', 'HEAD', '--') - if rc != 0: - # local changes, cannot update - _LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.") - return - _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) + raise EsphomeyamlError(text_type(e)) def to_code(config): @@ -255,13 +220,24 @@ def to_code(config): for conf in config.get(CONF_ON_SHUTDOWN, []): trigger = Pvariable(conf[CONF_TRIGGER_ID], ShutdownTrigger.new()) - automation.build_automation(trigger, const_char_p, conf) + automation.build_automation(trigger, const_char_ptr, conf) for conf in config.get(CONF_ON_LOOP, []): rhs = App.register_component(LoopTrigger.new()) trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) automation.build_automation(trigger, NoArg, conf) - update_esphomelib_repo(config) - add(App.set_compilation_datetime(RawExpression('__DATE__ ", " __TIME__'))) + + +def lib_deps(config): + return set(config.get(CONF_LIBRARIES, [])) + + +def includes(config): + ret = [] + for include in config.get(CONF_INCLUDES, []): + path = CORE.relative_path(include) + res = os.path.relpath(path, CORE.relative_build_path('src', 'main.cpp')) + ret.append(u'#include "{}"'.format(res)) + return ret diff --git a/esphomeyaml/cpp_generator.py b/esphomeyaml/cpp_generator.py new file mode 100644 index 0000000000..12844a9153 --- /dev/null +++ b/esphomeyaml/cpp_generator.py @@ -0,0 +1,540 @@ +from collections import OrderedDict + +from esphomeyaml.core import CORE, HexInt, Lambda, TimePeriod, TimePeriodMicroseconds, \ + TimePeriodMilliseconds, TimePeriodSeconds +from esphomeyaml.helpers import cpp_string_escape, indent_all_but_first_and_last + +# pylint: disable=unused-import, wrong-import-order +from typing import Any, Generator, List, Optional, Tuple, Union # noqa +from esphomeyaml.core import ID # noqa +from esphomeyaml.py_compat import text_type, string_types, integer_types + + +class Expression(object): + def __init__(self): + self.requires = [] + self.required = False + + def __str__(self): + raise NotImplementedError + + def require(self): + self.required = True + for require in self.requires: + if require.required: + continue + require.require() + + def has_side_effects(self): + return self.required + + +SafeExpType = Union[Expression, bool, str, text_type, int, float, TimePeriod] + + +class RawExpression(Expression): + def __init__(self, text): # type: (Union[str, unicode]) -> None + super(RawExpression, self).__init__() + self.text = text + + def __str__(self): + return str(self.text) + + +# pylint: disable=redefined-builtin +class AssignmentExpression(Expression): + def __init__(self, type, modifier, name, rhs, obj): + super(AssignmentExpression, self).__init__() + self.type = type + self.modifier = modifier + self.name = name + self.rhs = safe_exp(rhs) + self.requires.append(self.rhs) + self.obj = obj + + def __str__(self): + type_ = self.type + return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs) + + def has_side_effects(self): + return self.rhs.has_side_effects() + + +class ExpressionList(Expression): + def __init__(self, *args): + super(ExpressionList, self).__init__() + # Remove every None on end + args = list(args) + while args and args[-1] is None: + args.pop() + self.args = [] + for arg in args: + exp = safe_exp(arg) + self.requires.append(exp) + self.args.append(exp) + + def __str__(self): + text = u", ".join(text_type(x) for x in self.args) + return indent_all_but_first_and_last(text) + + +class TemplateArguments(Expression): + def __init__(self, *args): # type: (*SafeExpType) -> None + super(TemplateArguments, self).__init__() + self.args = ExpressionList(*args) + self.requires.append(self.args) + + def __str__(self): + return u'<{}>'.format(self.args) + + +class CallExpression(Expression): + def __init__(self, base, *args): # type: (Expression, *SafeExpType) -> None + super(CallExpression, self).__init__() + self.base = base + if args and isinstance(args[0], TemplateArguments): + self.template_args = args[0] + self.requires.append(self.template_args) + args = args[1:] + else: + self.template_args = None + self.args = ExpressionList(*args) + self.requires.append(self.args) + + def __str__(self): + if self.template_args is not None: + return u'{}{}({})'.format(self.base, self.template_args, self.args) + return u'{}({})'.format(self.base, self.args) + + +class StructInitializer(Expression): + def __init__(self, base, *args): # type: (Expression, *Tuple[str, SafeExpType]) -> None + super(StructInitializer, self).__init__() + self.base = base + if isinstance(base, Expression): + self.requires.append(base) + if not isinstance(args, OrderedDict): + args = OrderedDict(args) + self.args = OrderedDict() + for key, value in args.items(): + if value is None: + continue + exp = safe_exp(value) + self.args[key] = exp + self.requires.append(exp) + + def __str__(self): + cpp = u'{}{{\n'.format(self.base) + for key, value in self.args.items(): + cpp += u' .{} = {},\n'.format(key, value) + cpp += u'}' + return cpp + + +class ArrayInitializer(Expression): + def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None + super(ArrayInitializer, self).__init__() + self.multiline = kwargs.get('multiline', True) + self.args = [] + for arg in args: + if arg is None: + continue + exp = safe_exp(arg) + self.args.append(exp) + self.requires.append(exp) + + def __str__(self): + if not self.args: + return u'{}' + if self.multiline: + cpp = u'{\n' + for arg in self.args: + cpp += u' {},\n'.format(arg) + cpp += u'}' + else: + cpp = u'{' + u', '.join(str(arg) for arg in self.args) + u'}' + return cpp + + +class ParameterExpression(Expression): + def __init__(self, type, id): + super(ParameterExpression, self).__init__() + self.type = type + self.id = id + + def __str__(self): + return u"{} {}".format(self.type, self.id) + + +class ParameterListExpression(Expression): + def __init__(self, *parameters): + super(ParameterListExpression, self).__init__() + self.parameters = [] + for parameter in parameters: + if not isinstance(parameter, ParameterExpression): + parameter = ParameterExpression(*parameter) + self.parameters.append(parameter) + self.requires.append(parameter) + + def __str__(self): + return u", ".join(text_type(x) for x in self.parameters) + + +class LambdaExpression(Expression): + def __init__(self, parts, parameters, capture='=', return_type=None): + super(LambdaExpression, self).__init__() + self.parts = parts + if not isinstance(parameters, ParameterListExpression): + parameters = ParameterListExpression(*parameters) + self.parameters = parameters + self.requires.append(self.parameters) + self.capture = capture + self.return_type = return_type + if return_type is not None: + self.requires.append(return_type) + for i in range(1, len(parts), 3): + self.requires.append(parts[i]) + + def __str__(self): + cpp = u'[{}]({})'.format(self.capture, self.parameters) + if self.return_type is not None: + cpp += u' -> {}'.format(self.return_type) + cpp += u' {{\n{}\n}}'.format(self.content) + return indent_all_but_first_and_last(cpp) + + @property + def content(self): + return u''.join(text_type(part) for part in self.parts) + + +class Literal(Expression): + def __str__(self): + raise NotImplementedError + + +class StringLiteral(Literal): + def __init__(self, string): # type: (Union[str, unicode]) -> None + super(StringLiteral, self).__init__() + self.string = string + + def __str__(self): + return u'{}'.format(cpp_string_escape(self.string)) + + +class IntLiteral(Literal): + def __init__(self, i): # type: (Union[int, long]) -> None + super(IntLiteral, self).__init__() + self.i = i + + def __str__(self): + if self.i > 4294967295: + return u'{}ULL'.format(self.i) + if self.i > 2147483647: + return u'{}UL'.format(self.i) + if self.i < -2147483648: + return u'{}LL'.format(self.i) + return text_type(self.i) + + +class BoolLiteral(Literal): + def __init__(self, binary): # type: (bool) -> None + super(BoolLiteral, self).__init__() + self.binary = binary + + def __str__(self): + return u"true" if self.binary else u"false" + + +class HexIntLiteral(Literal): + def __init__(self, i): # type: (int) -> None + super(HexIntLiteral, self).__init__() + self.i = HexInt(i) + + def __str__(self): + return str(self.i) + + +class FloatLiteral(Literal): + def __init__(self, value): # type: (float) -> None + super(FloatLiteral, self).__init__() + self.float_ = value + + def __str__(self): + return u"{:f}f".format(self.float_) + + +# pylint: disable=bad-continuation +def safe_exp(obj # type: Union[Expression, bool, str, unicode, int, long, float, TimePeriod] + ): + # type: (...) -> Expression + if isinstance(obj, Expression): + return obj + if isinstance(obj, bool): + return BoolLiteral(obj) + if isinstance(obj, string_types): + return StringLiteral(obj) + if isinstance(obj, HexInt): + return HexIntLiteral(obj) + if isinstance(obj, integer_types): + return IntLiteral(obj) + if isinstance(obj, float): + return FloatLiteral(obj) + if isinstance(obj, TimePeriodMicroseconds): + return IntLiteral(int(obj.total_microseconds)) + if isinstance(obj, TimePeriodMilliseconds): + return IntLiteral(int(obj.total_milliseconds)) + if isinstance(obj, TimePeriodSeconds): + return IntLiteral(int(obj.total_seconds)) + raise ValueError(u"Object is not an expression", obj) + + +class Statement(object): + def __init__(self): + pass + + def __str__(self): + raise NotImplementedError + + +class RawStatement(Statement): + def __init__(self, text): + super(RawStatement, self).__init__() + self.text = text + + def __str__(self): + return self.text + + +class ExpressionStatement(Statement): + def __init__(self, expression): + super(ExpressionStatement, self).__init__() + self.expression = safe_exp(expression) + + def __str__(self): + return u"{};".format(self.expression) + + +def statement(expression): # type: (Union[Expression, Statement]) -> Statement + if isinstance(expression, Statement): + return expression + return ExpressionStatement(expression) + + +def variable(id, # type: ID + rhs, # type: Expression + type=None # type: MockObj + ): + # type: (...) -> MockObj + rhs = safe_exp(rhs) + obj = MockObj(id, u'.') + id.type = type or id.type + assignment = AssignmentExpression(id.type, '', id, rhs, obj) + CORE.add(assignment) + CORE.register_variable(id, obj) + obj.requires.append(assignment) + return obj + + +def Pvariable(id, # type: ID + rhs, # type: Expression + has_side_effects=True, # type: bool + type=None # type: MockObj + ): + # type: (...) -> MockObj + rhs = safe_exp(rhs) + if not has_side_effects and hasattr(rhs, '_has_side_effects'): + # pylint: disable=attribute-defined-outside-init, protected-access + rhs._has_side_effects = False + obj = MockObj(id, u'->', has_side_effects=has_side_effects) + id.type = type or id.type + assignment = AssignmentExpression(id.type, '*', id, rhs, obj) + CORE.add(assignment) + CORE.register_variable(id, obj) + obj.requires.append(assignment) + return obj + + +def add(expression, # type: Union[Expression, Statement] + require=True # type: bool + ): + # type: (...) -> None + CORE.add(expression, require=require) + + +def get_variable(id): # type: (ID) -> Generator[MockObj] + for var in CORE.get_variable(id): + yield None + yield var + + +def process_lambda(value, # type: Lambda + parameters, # type: List[Tuple[Expression, str]] + capture='=', # type: str + return_type=None # type: Optional[Expression] + ): + # type: (...) -> Generator[LambdaExpression] + from esphomeyaml.components.globals import GlobalVariableComponent + + if value is None: + yield + return + parts = value.parts[:] + for i, id in enumerate(value.requires_ids): + for full_id, var in CORE.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: + parts[i * 3 + 1] = var + parts[i * 3 + 2] = '' + yield LambdaExpression(parts, parameters, capture, return_type) + + +def templatable(value, # type: Any + input_type, # type: Expression + output_type # type: Optional[Expression] + ): + if isinstance(value, Lambda): + lambda_ = None + for lambda_ in process_lambda(value, [(input_type, 'x')], return_type=output_type): + yield None + yield lambda_ + else: + yield value + + +class MockObj(Expression): + def __init__(self, base, op=u'.', has_side_effects=True): + self.base = base + self.op = op + self._has_side_effects = has_side_effects + super(MockObj, self).__init__() + + def __getattr__(self, attr): # type: (str) -> MockObj + if attr == u'_': + obj = MockObj(u'{}{}'.format(self.base, self.op)) + obj.requires.append(self) + return obj + if attr == u'new': + obj = MockObj(u'new {}'.format(self.base), u'->') + obj.requires.append(self) + return obj + next_op = u'.' + if attr.startswith(u'P') and self.op not in ['::', '']: + attr = attr[1:] + next_op = u'->' + if attr.startswith(u'_'): + attr = attr[1:] + obj = MockObj(u'{}{}{}'.format(self.base, self.op, attr), next_op) + obj.requires.append(self) + return obj + + def __call__(self, *args, **kwargs): # type: (*Any, **Any) -> MockObj + call = CallExpression(self.base, *args) + obj = MockObj(call, self.op) + obj.requires.append(self) + obj.requires.append(call) + return obj + + def __str__(self): # type: () -> unicode + return text_type(self.base) + + def require(self): # type: () -> None + self.required = True + for require in self.requires: + if require.required: + continue + require.require() + + def template(self, args): # type: (Union[TemplateArguments, Expression]) -> MockObj + if not isinstance(args, TemplateArguments): + args = TemplateArguments(args) + obj = MockObj(u'{}{}'.format(self.base, args)) + obj.requires.append(self) + obj.requires.append(args) + return obj + + def namespace(self, name): # type: (str) -> MockObj + obj = MockObj(u'{}{}{}'.format(self.base, self.op, name), u'::') + obj.requires.append(self) + return obj + + def class_(self, name, *parents): # type: (str, *MockObjClass) -> MockObjClass + op = '' if self.op == '' else '::' + obj = MockObjClass(u'{}{}{}'.format(self.base, op, name), u'.', parents=parents) + obj.requires.append(self) + return obj + + def struct(self, name): # type: (str) -> MockObjClass + return self.class_(name) + + def enum(self, name, is_class=False): # type: (str, bool) -> MockObj + if is_class: + return self.namespace(name) + + return self + + def operator(self, name): # type: (str) -> MockObj + if name == 'ref': + obj = MockObj(u'{} &'.format(self.base), u'') + obj.requires.append(self) + return obj + if name == 'ptr': + obj = MockObj(u'{} *'.format(self.base), u'') + obj.requires.append(self) + return obj + if name == "const": + obj = MockObj(u'const {}'.format(self.base), u'') + obj.requires.append(self) + return obj + raise NotImplementedError + + def has_side_effects(self): # type: () -> bool + return self._has_side_effects + + def __getitem__(self, item): # type: (Union[str, Expression]) -> MockObj + next_op = u'.' + if isinstance(item, str) and item.startswith(u'P'): + item = item[1:] + next_op = u'->' + obj = MockObj(u'{}[{}]'.format(self.base, item), next_op) + obj.requires.append(self) + if isinstance(item, Expression): + obj.requires.append(item) + 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): # type: (MockObjClass) -> bool + if self == other: + return True + for parent in self._parents: + if parent == other: + return True + return False + + def template(self, args): # type: (Union[TemplateArguments, Expression]) -> MockObjClass + 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 diff --git a/esphomeyaml/cpp_helpers.py b/esphomeyaml/cpp_helpers.py new file mode 100644 index 0000000000..c96dce6977 --- /dev/null +++ b/esphomeyaml/cpp_helpers.py @@ -0,0 +1,49 @@ +from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \ + CONF_SETUP_PRIORITY +from esphomeyaml.core import CORE, EsphomeyamlError +from esphomeyaml.cpp_generator import IntLiteral, RawExpression +from esphomeyaml.cpp_types import GPIOInputPin, GPIOOutputPin + + +def generic_gpio_pin_expression_(conf, mock_obj, default_mode): + if conf is None: + return + number = conf[CONF_NUMBER] + inverted = conf.get(CONF_INVERTED) + if CONF_PCF8574 in conf: + from esphomeyaml.components import pcf8574 + + for hub in CORE.get_variable(conf[CONF_PCF8574]): + yield None + + if default_mode == u'INPUT': + mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')] + yield hub.make_input_pin(number, mode, inverted) + return + if default_mode == u'OUTPUT': + yield hub.make_output_pin(number, inverted) + return + + raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode)) + if len(conf) == 1: + yield IntLiteral(number) + return + mode = RawExpression(conf.get(CONF_MODE, default_mode)) + yield mock_obj(number, mode, inverted) + + +def gpio_output_pin_expression(conf): + for exp in generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT'): + yield None + yield exp + + +def gpio_input_pin_expression(conf): + for exp in generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT'): + yield None + yield exp + + +def setup_component(obj, config): + if CONF_SETUP_PRIORITY in config: + CORE.add(obj.set_setup_priority(config[CONF_SETUP_PRIORITY])) diff --git a/esphomeyaml/cpp_types.py b/esphomeyaml/cpp_types.py new file mode 100644 index 0000000000..c07e0c6436 --- /dev/null +++ b/esphomeyaml/cpp_types.py @@ -0,0 +1,37 @@ +from esphomeyaml.cpp_generator import MockObj + +global_ns = MockObj('', '') +void = global_ns.namespace('void') +float_ = global_ns.namespace('float') +bool_ = global_ns.namespace('bool') +std_ns = global_ns.namespace('std') +std_string = std_ns.class_('string') +std_vector = std_ns.class_('vector') +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_ptr = global_ns.namespace('const char *') +NAN = global_ns.namespace('NAN') +esphomelib_ns = global_ns # using namespace esphomelib; +NoArg = esphomelib_ns.class_('NoArg') +App = esphomelib_ns.App +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') +ComponentPtr = Component.operator('ptr') +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.class_('JsonObject') +JsonObjectRef = JsonObject.operator('ref') +JsonObjectConstRef = JsonObjectRef.operator('const') +Controller = esphomelib_ns.class_('Controller') +StoringController = esphomelib_ns.class_('StoringController', Controller) + +GPIOPin = esphomelib_ns.class_('GPIOPin') +GPIOOutputPin = esphomelib_ns.class_('GPIOOutputPin', GPIOPin) +GPIOInputPin = esphomelib_ns.class_('GPIOInputPin', GPIOPin) diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index d12dc0fec8..3c01e6fdcf 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -2,41 +2,55 @@ from __future__ import print_function import codecs +import collections import hmac import json import logging +import multiprocessing import os -import random import subprocess +import threading -from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_BUILD_PATH -from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml import const, core, __main__ +import tornado +import tornado.concurrent +import tornado.httpserver +import tornado.netutil +import tornado.gen +import tornado.ioloop +import tornado.iostream +from tornado.log import access_log +import tornado.process +import tornado.web +import tornado.websocket + +from esphomeyaml import const from esphomeyaml.__main__ import get_serial_ports -from esphomeyaml.helpers import relative_path +from esphomeyaml.helpers import mkdir_p, run_system_command +from esphomeyaml.py_compat import IS_PY2 +from esphomeyaml.storage_json import EsphomeyamlStorageJSON, StorageJSON, \ + esphomeyaml_storage_path, ext_storage_path from esphomeyaml.util import shlex_quote -try: - import tornado - import tornado.gen - import tornado.ioloop - import tornado.iostream - import tornado.process - import tornado.web - import tornado.websocket - import tornado.concurrent -except ImportError as err: - tornado = None +# pylint: disable=unused-import, wrong-import-order +from typing import Optional # noqa _LOGGER = logging.getLogger(__name__) CONFIG_DIR = '' -PASSWORD = '' +PASSWORD_DIGEST = '' +COOKIE_SECRET = None +USING_PASSWORD = False +ON_HASSIO = False +USING_HASSIO_AUTH = True +HASSIO_MQTT_CONFIG = None # pylint: disable=abstract-method class BaseHandler(tornado.web.RequestHandler): def is_authenticated(self): - return not PASSWORD or self.get_secure_cookie('authenticated') == 'yes' + if USING_HASSIO_AUTH or USING_PASSWORD: + return self.get_secure_cookie('authenticated') == 'yes' + + return True # pylint: disable=abstract-method, arguments-differ @@ -47,12 +61,13 @@ class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler): self.closed = False def on_message(self, message): - if PASSWORD and self.get_secure_cookie('authenticated') != 'yes': - return + if USING_HASSIO_AUTH or USING_PASSWORD: + if self.get_secure_cookie('authenticated') != 'yes': + return if self.proc is not None: return command = self.build_command(message) - _LOGGER.debug(u"WebSocket opened for command %s", [shlex_quote(x) for x in command]) + _LOGGER.info(u"Running command '%s'", ' '.join(shlex_quote(x) for x in command)) self.proc = tornado.process.Subprocess(command, stdout=tornado.process.Subprocess.STREAM, stderr=subprocess.STDOUT) @@ -63,16 +78,20 @@ class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler): def redirect_stream(self): while True: try: - data = yield self.proc.stdout.read_until_regex('[\n\r]') + if IS_PY2: + reg = '[\n\r]' + else: + reg = b'[\n\r]' + data = yield self.proc.stdout.read_until_regex(reg) + if not IS_PY2: + data = data.decode('utf-8', 'backslashreplace') except tornado.iostream.StreamClosedError: break - if data.endswith('\r') and random.randrange(100) < 90: - continue try: - data = data.replace('\033', '\\033') + self.write_message({'event': 'line', 'data': data}) except UnicodeDecodeError: - data = data.encode('ascii', 'backslashreplace') - self.write_message({'event': 'line', 'data': data}) + data = codecs.decode(data, 'utf8', 'replace') + self.write_message({'event': 'line', 'data': data}) def proc_on_exit(self, returncode): if not self.closed: @@ -93,50 +112,49 @@ class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = CONFIG_DIR + '/' + js['configuration'] - return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape'] + return ["esphomeyaml", "--dashboard", config_file, "logs", '--serial-port', js["port"]] class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphomeyaml", config_file, "run", '--upload-port', js["port"], - '--escape', '--use-esptoolpy'] + return ["esphomeyaml", "--dashboard", config_file, "run", '--upload-port', js["port"]] class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphomeyaml", config_file, "compile"] + return ["esphomeyaml", "--dashboard", config_file, "compile"] class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphomeyaml", config_file, "config"] + return ["esphomeyaml", "--dashboard", config_file, "config"] class EsphomeyamlCleanMqttHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphomeyaml", config_file, "clean-mqtt"] + return ["esphomeyaml", "--dashboard", config_file, "clean-mqtt"] class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket): def build_command(self, message): js = json.loads(message) config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphomeyaml", config_file, "clean"] + return ["esphomeyaml", "--dashboard", 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"] + return ["esphomeyaml", "--dashboard", config_file, "hass-config"] class SerialPortRequestHandler(BaseHandler): @@ -155,7 +173,8 @@ class SerialPortRequestHandler(BaseHandler): desc = split_desc[0] data.append({'port': port, 'desc': desc}) data.append({'port': 'OTA', 'desc': 'Over-The-Air'}) - self.write(json.dumps(sorted(data, reverse=True))) + data.sort(key=lambda x: x['port'], reverse=True) + self.write(json.dumps(data)) class WizardRequestHandler(BaseHandler): @@ -165,12 +184,9 @@ class WizardRequestHandler(BaseHandler): if not self.is_authenticated(): self.redirect('/login') return - kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()} - config = wizard.wizard_file(**kwargs) + kwargs = {k: ''.join(v) for k, v in self.request.arguments.items()} destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml') - with codecs.open(destination, 'w') as f_handle: - f_handle.write(config) - + wizard.wizard_write(path=destination, **kwargs) self.redirect('/?begin=True') @@ -181,13 +197,16 @@ class DownloadBinaryRequestHandler(BaseHandler): return configuration = self.get_argument('configuration') - config_file = os.path.join(CONFIG_DIR, configuration) - core.CONFIG_PATH = config_file - config = __main__.read_config(core.CONFIG_PATH) - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') + storage_path = ext_storage_path(CONFIG_DIR, configuration) + storage_json = StorageJSON.load(storage_path) + if storage_json is None: + self.send_error() + return + + path = storage_json.firmware_bin_path self.set_header('Content-Type', 'application/octet-stream') - self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(core.NAME)) + filename = '{}.bin'.format(storage_json.name) + self.set_header("Content-Disposition", 'attachment; filename="{}"'.format(filename)) with open(path, 'rb') as f: while 1: data = f.read(16384) # or some other nice-sized chunk @@ -197,6 +216,83 @@ class DownloadBinaryRequestHandler(BaseHandler): self.finish() +def _list_yaml_files(): + files = [] + for file in os.listdir(CONFIG_DIR): + if not file.endswith('.yaml'): + continue + if file.startswith('.'): + continue + if file == 'secrets.yaml': + continue + files.append(file) + files.sort() + return files + + +def _list_dashboard_entries(): + files = _list_yaml_files() + return [DashboardEntry(file) for file in files] + + +class DashboardEntry(object): + def __init__(self, filename): + self.filename = filename + self._storage = None + self._loaded_storage = False + + @property + def full_path(self): # type: () -> str + return os.path.join(CONFIG_DIR, self.filename) + + @property + def storage(self): # type: () -> Optional[StorageJSON] + if not self._loaded_storage: + self._storage = StorageJSON.load(ext_storage_path(CONFIG_DIR, self.filename)) + self._loaded_storage = True + return self._storage + + @property + def address(self): + if self.storage is None: + return None + return self.storage.address + + @property + def name(self): + if self.storage is None: + return self.filename[:-len('.yaml')] + return self.storage.name + + @property + def esp_platform(self): + if self.storage is None: + return None + return self.storage.esp_platform + + @property + def board(self): + if self.storage is None: + return None + return self.storage.board + + @property + def update_available(self): + if self.storage is None: + return True + return self.update_old != self.update_new + + @property + def update_old(self): + if self.storage is None: + return '' + return self.storage.esphomeyaml_version or '' + + @property + def update_new(self): + return const.__version__ + + class MainRequestHandler(BaseHandler): def get(self): if not self.is_authenticated(): @@ -204,31 +300,210 @@ class MainRequestHandler(BaseHandler): return begin = bool(self.get_argument('begin', False)) - files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and - not f.startswith('.')]) - full_path_files = [os.path.join(CONFIG_DIR, f) for f in files] - self.render("templates/index.html", files=files, full_path_files=full_path_files, - version=const.__version__, begin=begin) + entries = _list_dashboard_entries() + version = const.__version__ + docs_link = 'https://beta.esphomelib.com/esphomeyaml/' if 'b' in version else \ + 'https://esphomelib.com/esphomeyaml/' + + self.render("templates/index.html", entries=entries, + version=version, begin=begin, docs_link=docs_link, + get_static_file_url=get_static_file_url) + + +def _ping_func(filename, address): + if os.name == 'nt': + command = ['ping', '-n', '1', address] + else: + command = ['ping', '-c', '1', address] + rc, _, _ = run_system_command(*command) + return filename, rc == 0 + + +class PingThread(threading.Thread): + def run(self): + pool = multiprocessing.Pool(processes=8) + + while not STOP_EVENT.is_set(): + # Only do pings if somebody has the dashboard open + PING_REQUEST.wait() + PING_REQUEST.clear() + + def callback(ret): + PING_RESULT[ret[0]] = ret[1] + + entries = _list_dashboard_entries() + queue = collections.deque() + for entry in entries: + if entry.address is None: + PING_RESULT[entry.filename] = None + continue + + result = pool.apply_async(_ping_func, (entry.filename, entry.address), + callback=callback) + queue.append(result) + + while queue: + item = queue[0] + if item.ready(): + queue.popleft() + continue + + try: + item.get(0.1) + except OSError: + # ping not installed + pass + except multiprocessing.TimeoutError: + pass + + if STOP_EVENT.is_set(): + pool.terminate() + return + + +class PingRequestHandler(BaseHandler): + def get(self): + if not self.is_authenticated(): + self.redirect('/login') + return + + PING_REQUEST.set() + self.write(json.dumps(PING_RESULT)) + + +def is_allowed(configuration): + return os.path.sep not in configuration + + +class EditRequestHandler(BaseHandler): + def get(self): + if not self.is_authenticated(): + self.redirect('/login') + return + configuration = self.get_argument('configuration') + if not is_allowed(configuration): + self.set_status(401) + return + + with open(os.path.join(CONFIG_DIR, configuration), 'r') as f: + content = f.read() + self.write(content) + + def post(self): + if not self.is_authenticated(): + self.redirect('/login') + return + configuration = self.get_argument('configuration') + if not is_allowed(configuration): + self.set_status(401) + return + + with open(os.path.join(CONFIG_DIR, configuration), 'wb') as f: + f.write(self.request.body) + self.set_status(200) + return + + +PING_RESULT = {} # type: dict +STOP_EVENT = threading.Event() +PING_REQUEST = threading.Event() class LoginHandler(BaseHandler): def get(self): + if USING_HASSIO_AUTH: + self.render_hassio_login() + return self.write('
' 'Password: ' '' '
') + def render_hassio_login(self, error=None): + version = const.__version__ + docs_link = 'https://beta.esphomelib.com/esphomeyaml/' if 'b' in version else \ + 'https://esphomelib.com/esphomeyaml/' + + self.render("templates/login.html", version=version, docs_link=docs_link, error=error, + get_static_file_url=get_static_file_url) + + def post_hassio_login(self): + import requests + + headers = { + 'X-HASSIO-KEY': os.getenv('HASSIO_TOKEN'), + } + data = { + 'username': str(self.get_argument('username', '')), + 'password': str(self.get_argument('password', '')) + } + try: + req = requests.post('http://hassio/auth', headers=headers, data=data) + if req.status_code == 200: + self.set_secure_cookie("authenticated", "yes") + self.redirect('/') + return + except Exception as err: # pylint: disable=broad-except + _LOGGER.warning("Error during Hass.io auth request: %s", err) + self.set_status(500) + self.render_hassio_login(error="Internal server error") + return + self.set_status(401) + self.render_hassio_login(error="Invalid username or password") + def post(self): + if USING_HASSIO_AUTH: + self.post_hassio_login() + return + password = str(self.get_argument("password", '')) password = hmac.new(password).digest() - if hmac.compare_digest(PASSWORD, password): + if hmac.compare_digest(PASSWORD_DIGEST, password): self.set_secure_cookie("authenticated", "yes") self.redirect("/") -def make_app(debug=False): +_STATIC_FILE_HASHES = {} + + +def get_static_file_url(name): static_path = os.path.join(os.path.dirname(__file__), 'static') - return tornado.web.Application([ + if name in _STATIC_FILE_HASHES: + hash_ = _STATIC_FILE_HASHES[name] + else: + path = os.path.join(static_path, name) + with open(path, 'rb') as f_handle: + hash_ = hash(f_handle.read()) + _STATIC_FILE_HASHES[name] = hash_ + return u'/static/{}?hash={}'.format(name, hash_) + + +def make_app(debug=False): + def log_function(handler): + if handler.get_status() < 400: + log_method = access_log.info + + if isinstance(handler, SerialPortRequestHandler) and not debug: + return + if isinstance(handler, PingRequestHandler) and not debug: + return + elif handler.get_status() < 500: + log_method = access_log.warning + else: + log_method = access_log.error + + request_time = 1000.0 * handler.request.request_time() + # pylint: disable=protected-access + log_method("%d %s %.2fms", handler.get_status(), + handler._request_summary(), request_time) + + class StaticFileHandler(tornado.web.StaticFileHandler): + def set_extra_headers(self, path): + if debug: + self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + + static_path = os.path.join(os.path.dirname(__file__), 'static') + app = tornado.web.Application([ (r"/", MainRequestHandler), (r"/login", LoginHandler), (r"/logs", EsphomeyamlLogsHandler), @@ -238,51 +513,76 @@ def make_app(debug=False): (r"/clean-mqtt", EsphomeyamlCleanMqttHandler), (r"/clean", EsphomeyamlCleanHandler), (r"/hass-config", EsphomeyamlHassConfigHandler), + (r"/edit", EditRequestHandler), (r"/download.bin", DownloadBinaryRequestHandler), (r"/serial-ports", SerialPortRequestHandler), + (r"/ping", PingRequestHandler), (r"/wizard.html", WizardRequestHandler), - (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}), - ], debug=debug, cookie_secret=PASSWORD) + (r'/static/(.*)', StaticFileHandler, {'path': static_path}), + ], debug=debug, cookie_secret=COOKIE_SECRET, log_function=log_function) + + if debug: + _STATIC_FILE_HASHES.clear() + + return app def start_web_server(args): global CONFIG_DIR - global PASSWORD - - if tornado is None: - raise ESPHomeYAMLError("Attempted to load dashboard, but tornado is not installed! " - "Please run \"pip2 install tornado esptool\" in your terminal.") + global PASSWORD_DIGEST + global USING_PASSWORD + global ON_HASSIO + global USING_HASSIO_AUTH + global COOKIE_SECRET CONFIG_DIR = args.configuration - if not os.path.exists(CONFIG_DIR): - os.makedirs(CONFIG_DIR) + mkdir_p(CONFIG_DIR) + mkdir_p(os.path.join(CONFIG_DIR, ".esphomeyaml")) - # HassIO options storage - PASSWORD = args.password - if os.path.isfile('/data/options.json'): - with open('/data/options.json') as f: - js = json.load(f) - PASSWORD = js.get('password') or PASSWORD + ON_HASSIO = args.hassio + if ON_HASSIO: + USING_HASSIO_AUTH = not bool(os.getenv('DISABLE_HA_AUTHENTICATION')) + USING_PASSWORD = False + else: + USING_HASSIO_AUTH = False + USING_PASSWORD = args.password - if PASSWORD: - PASSWORD = hmac.new(str(PASSWORD)).digest() - # Use the digest of the password as our cookie secret. This makes sure the cookie - # isn't too short. It, of course, enables local hash brute forcing (because the cookie - # secret can be brute forced without making requests). But the hashing algorithm used - # by tornado is apparently strong enough to make brute forcing even a short string pretty - # hard. + if USING_PASSWORD: + PASSWORD_DIGEST = hmac.new(args.password).digest() + + if USING_HASSIO_AUTH or USING_PASSWORD: + path = esphomeyaml_storage_path(CONFIG_DIR) + storage = EsphomeyamlStorageJSON.load(path) + if storage is None: + storage = EsphomeyamlStorageJSON.get_default() + storage.save(path) + COOKIE_SECRET = storage.cookie_secret - _LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...", - args.port, CONFIG_DIR) app = make_app(args.verbose) - app.listen(args.port) + if args.socket is not None: + _LOGGER.info("Starting dashboard web server on unix socket %s and configuration dir %s...", + args.socket, CONFIG_DIR) + server = tornado.httpserver.HTTPServer(app) + socket = tornado.netutil.bind_unix_socket(args.socket, mode=0o666) + server.add_socket(socket) + else: + _LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...", + args.port, CONFIG_DIR) + app.listen(args.port) - if args.open_ui: - import webbrowser + if args.open_ui: + import webbrowser - webbrowser.open('localhost:{}'.format(args.port)) + webbrowser.open('localhost:{}'.format(args.port)) + ping_thread = PingThread() + ping_thread.start() try: tornado.ioloop.IOLoop.current().start() except KeyboardInterrupt: _LOGGER.info("Shutting down...") + STOP_EVENT.set() + PING_REQUEST.set() + ping_thread.join() + if args.socket is not None: + os.remove(args.socket) diff --git a/esphomeyaml/dashboard/static/ace.js b/esphomeyaml/dashboard/static/ace.js new file mode 100644 index 0000000000..58119de349 --- /dev/null +++ b/esphomeyaml/dashboard/static/ace.js @@ -0,0 +1,17 @@ +(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=i.match(/ Gecko\/\d+/),t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(i.split(" Edge/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isAndroid=i.indexOf("Android")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(i)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIPad||t.isAndroid}),ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function o(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0;if(typeof document!="undefined"){var s=document.createElement("div");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){var r,i;t.addListener(e,"touchstart",function(e){var t=e.touches,n=t[0];r=n.clientX,i=n.clientY}),t.addListener(e,"touchmove",function(e){var t=e.touches;if(t.length>1)return;var s=t[0];e.wheelX=r-s.clientX,e.wheelY=i-s.clientY,r=s.clientX,i=s.clientY,n(e)})},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l++,i=function(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())};t.addListener(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n63,l=400,c=e("../lib/keys"),h=c.KEY_MODS,p=i.isIOS,d=p?/\s/:/\n/,v=function(e,t){function W(){x=!0,n.blur(),n.focus(),x=!1}function V(e){e.keyCode==27&&n.value.lengthC&&T[s]=="\n")o=c.end;else if(rC&&T.slice(0,s).split("\n").length>2)o=c.down;else if(s>C&&T[s-1]==" ")o=c.right,u=h.option;else if(s>C||s==C&&C!=N&&r==s)o=c.right;r!==s&&(u|=h.shift),o&&(t.onCommandKey(null,u,o),N=r,C=s,A(""))};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var v=!1,m=!1,g=!1,y=!1,b="",w=!0,E=!1;i.isMobile||(n.style.fontSize="1px");var S=!1,x=!1,T="",N=0,C=0;try{var k=document.activeElement===n}catch(L){}r.addListener(n,"blur",function(e){if(x)return;t.onBlur(e),k=!1}),r.addListener(n,"focus",function(e){if(x)return;k=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(A):A()}),this.$focusScroll=!1,this.focus=function(){if(b||f||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return k},t.on("beforeEndOperation",function(){if(t.curOp&&t.curOp.command.name=="insertstring")return;g&&(T=n.value="",z()),A()});var A=p?function(e){if(!k||v&&!e)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=T=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(N!=i||C!=s)&&n.setSelectionRange(i,s),N=i,C=s}:function(){if(g||y)return;if(!k&&!D)return;g=!0;var e=t.selection,r=e.getRange(),i=e.cursor.row,s=r.start.column,o=r.end.column,u=t.session.getLine(i);if(r.start.row!=i){var a=t.session.getLine(i-1);s=r.start.rowi+1?f.length:o,o+=u.length+1,u=u+"\n"+f}u.length>l&&(s=T.length&&e.value===T&&T&&e.selectionEnd!==C},M=function(e){if(g)return;v?v=!1:O(n)&&(t.selectAll(),A())},_=null;this.setInputHandler=function(e){_=e},this.getInputHandler=function(){return _};var D=!1,P=function(e,r){D&&(D=!1);if(m)return A(),e&&t.onPaste(e),m=!1,"";var i=n.selectionStart,s=n.selectionEnd,o=N,u=T.length-C,a=e,f=e.length-i,l=e.length-s,c=0;while(o>0&&T[c]==e[c])c++,o--;a=a.slice(c),c=1;while(u>0&&T.length-c>N-1&&T[T.length-c]==e[e.length-c])c++,u--;return f-=c-1,l-=c-1,a=a.slice(0,a.length-c+1),!r&&f==a.length&&!o&&!u&&!l?"":(y=!0,a&&!o&&!u&&!f&&!l||S?t.onTextInput(a):t.onTextInput(a,{extendLeft:o,extendRight:u,restoreStart:f,restoreEnd:l}),y=!1,T=e,N=i,C=s,a)},H=function(e){if(g)return U();var t=n.value,r=P(t,!0);(t.length>l+100||d.test(r))&&A()},B=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||u)return;var i=a||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return B(e,t,!0)}},j=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);B(e,s)?(p&&(A(s),v=s,setTimeout(function(){v=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(v=!0,n.value=s,n.select(),setTimeout(function(){v=!1,A(),i?t.onCut():t.onCopy()}))},F=function(e){j(e,!0)},I=function(e){j(e,!1)},q=function(e){var s=B(e);typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(A),r.preventDefault(e)):(n.value="",m=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",M),r.addListener(n,"input",H),r.addListener(n,"cut",F),r.addListener(n,"copy",I),r.addListener(n,"paste",q),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:I(e);break;case 86:q(e);break;case 88:F(e)}});var R=function(e){if(g||!t.onCompositionStart||t.$readOnly)return;g={};if(S)return;setTimeout(U,0),t.on("mousedown",W);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,g.markerRange=r,g.selectionStart=N,t.onCompositionStart(g),g.useTextareaForIME?(n.value="",T="",N=0,C=0):(n.msGetInputContext&&(g.context=n.msGetInputContext()),n.getInputContext&&(g.context=n.getInputContext()))},U=function(){if(!g||!t.onCompositionUpdate||t.$readOnly)return;if(S)return W();if(g.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;P(e),g.markerRange&&(g.context&&(g.markerRange.start.column=g.selectionStart=g.context.compositionStartOffset),g.markerRange.end.column=g.markerRange.start.column+C-g.selectionStart)}},z=function(e){if(!t.onCompositionEnd||t.$readOnly)return;g=!1,t.onCompositionEnd(),t.off("mousedown",W),e&&H()},X=o.delayedCall(U,50).schedule.bind(null,null);r.addListener(n,"compositionstart",R),r.addListener(n,"compositionupdate",U),r.addListener(n,"keyup",V),r.addListener(n,"keydown",X),r.addListener(n,"compositionend",z),this.getElement=function(){return n},this.setCommandMode=function(e){S=e,n.readOnly=!1},this.setReadOnly=function(e){S||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){E=e},this.onContextMenu=function(e){D=!0,A(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){b||(b=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+(i.isIE?"opacity:0.1;":"")+"text-indent: -"+(N+C)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout($),i.isWin&&r.capture(t.container,h,J)},this.onContextMenuClose=J;var $,K=function(e){t.textInput.onContextMenu(e),J()};r.addListener(n,"mouseup",K),r.addListener(n,"mousedown",function(e){e.preventDefault(),J()}),r.addListener(t.renderer.scroller,"contextmenu",K),r.addListener(n,"contextmenu",K),p&&Q(e,t,n)};t.TextInput=v}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e)),t.setDefaultHandler("touchmove",this.onTouchMove.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedt.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.init=l}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$keepTextAreaAtCursor&&(s.$keepTextAreaAtCursor=null);var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off("beforeEndOperation",c),clearInterval(h),l(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",s.$keepTextAreaAtCursor==null&&(s.$keepTextAreaAtCursor=!0,s.$moveTextAreaToCursor()),o.isMousePressed=!1,o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+"End"]&&o[o.state+"End"](),o.state="",o.releaseMouse())};n.on("beforeEndOperation",c),n.startOperation({command:{name:"mouse"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define("ace/mouse/fold_handler",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,"ace_inline_button")&&r.hasCssClass(o,"ace_toggle_wrap")&&(i.setOption("wrap",!0),e.renderer.scrollCursorIntoView())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e("../lib/dom");t.FoldHandler=i}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/lib/bidiutil",["require","exports","module"],function(e,t,n){"use strict";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var i=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&i.row===this.lead.row&&i.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[i.row]&&(i.row>0||e>0)&&i.row++,this.moveCursorTo(i.row,i.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length-t+1,i=e.start.row,s=e.start.column;for(var o=0,u=0;o20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column!=t.column||!this.$insertRight)&&(a.start.column+=l,a.start.row+=f);if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$insertRight)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;fr-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w>2)),f-1);while(w>E&&e[w]E&&e[w]E&&e[w]==a)w--}else while(w>E&&e[w]E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()},this.isFullWidth=m}.call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t!="number"&&(t=parseInt(prompt("Enter line number:"),10)),isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;is.length||i.length<2||!i[1])return this.commands.exec("insertstring",this,t);for(var o=s.length;o--;){var u=s[o];u.isEmpty()||r.remove(u),r.insert(u.start,i[o])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.inVirtualSelectionMode||(this.session.mergeUndoDeltas=!1,this.mergeNextCommand=!1)),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()&&e.indexOf("\n")==-1){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;hp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}.call(w.prototype),g.defineOptions(w.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=w}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)}}).call(r.prototype);var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLength(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v="ace_gutter-cell ";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=d&&this.$cursorRow<=n.end.row)&&(v+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v="ace_fold-widget ace_"+m;m=="start"&&i==d&&in.right-t.right)return"foldWidgets"}}).call(a.prototype),t.Gutter=a}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+e.width+(i||0)+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1);var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var o=this,u=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,a=this.dom.createFragment(this.element),f,l=0;while(f=u.exec(r)){var c=f[1],h=f[2],p=f[3],d=f[4],v=f[5];if(!o.showInvisibles&&h)continue;var m=l!=f.index?r.slice(l,f.index):"";l=f.index+f[0].length,m&&a.appendChild(this.dom.createTextNode(m,this.element));if(c){var g=o.session.getScreenTabSize(t+f.index);a.appendChild(o.$tabStrings[g].cloneNode(!0)),t+=g-1}else if(h)if(o.showInvisibles){var y=this.dom.createElement("span");y.className="ace_invisible ace_invisible_space",y.textContent=s.stringRepeat(o.SPACE_CHAR,h.length),a.appendChild(y)}else a.appendChild(this.com.createTextNode(h,this.element));else if(p){var y=this.dom.createElement("span");y.className="ace_invisible ace_invisible_space ace_invalid",y.textContent=s.stringRepeat(o.SPACE_CHAR,p.length),a.appendChild(y)}else if(d){var b=o.showInvisibles?o.SPACE_CHAR:"";t+=1;var y=this.dom.createElement("span");y.style.width=o.config.characterWidth*2+"px",y.className=o.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",y.textContent=o.showInvisibles?o.SPACE_CHAR:"",a.appendChild(y)}else if(v){t+=1;var y=i.createElement("span");y.style.width=o.config.characterWidth*2+"px",y.className="ace_cjk",y.textContent=v,a.appendChild(y)}}a.appendChild(this.dom.createTextNode(l?r.slice(l):r,this.element));if(!this.$textToken[n.type]){var w="ace_"+n.type.replace(/\./g," ace_"),y=this.dom.createElement("span");n.type=="fold"&&(y.style.width=n.value.length*this.config.characterWidth+"px"),y.className=w,y.appendChild(a),e.appendChild(y)}else e.appendChild(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r){this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var i=this.dom.createElement("span");i.className="ace_inline_button ace_keyword ace_toggle_wrap",i.style.position="absolute",i.style.right="0",i.textContent="",e.appendChild(i)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showInvisibles&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,"ace_animate-blinking")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=256,l=typeof ResizeObserver=="function",c=200,h=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.innerHTML=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){var n=t[0].contentRect;e.checkForSizeChanges({height:n.height,width:n.width/f})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return t?(window.getComputedStyle(t).zoom||1)*e(t.parentElement):1},this.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./layer/gutter").Gutter,u=e("./layer/marker").Marker,a=e("./layer/text").Text,f=e("./layer/cursor").Cursor,l=e("./scrollbar").HScrollBar,c=e("./scrollbar").VScrollBar,h=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,d=e("./lib/event_emitter").EventEmitter,v='.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;perspective: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}',m=e("./lib/useragent"),g=m.isIE;i.importCssString(v,"ace_editor.css");var y=function(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){var e=this.textarea.style;if(!this.$keepTextAreaAtCursor){i.translate(this.textarea,-100,0);return}var t=this.$cursorLayer.$pixelPos;if(!t)return;var n=this.$composition;n&&n.markerRange&&(t=this.$cursorLayer.getPixelPosition(n.markerRange.start,!0));var r=this.layerConfig,s=t.top,o=t.left;s-=r.offset;var u=n&&n.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1;if(!n)s+=this.lineHeight;else if(n.useTextareaForIME){var f=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(f)[0],u+=2}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,this.$size.height-u))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender"),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText,e.keepTextAreaAtCursor=this.$keepTextAreaAtCursor),e.useTextareaForIME=this.$useTextareaForIME,this.$useTextareaForIME?(this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null,this.$cursorLayer.element.style.display=""},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};l.prototype=f.prototype,t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./range").Range,o=e("./editor").Editor,u=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,f=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e,n){if(typeof e=="string"){var s=e;e=document.getElementById(s);if(!e)throw new Error("ace.edit can't find div #"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML="");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,"resize",h.onResize),c.on("destroy",function(){i.removeListener(window,"resize",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version="1.4.2"}); (function() { + ace.require(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = ace.define; + } + if (!window.ace) + window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) + window.ace[key] = a[key]; + window.ace["default"] = window.ace; + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = window.ace; + } + }); + })(); + \ No newline at end of file diff --git a/esphomeyaml/dashboard/static/esphomeyaml.css b/esphomeyaml/dashboard/static/esphomeyaml.css new file mode 100644 index 0000000000..bfd8d3b893 --- /dev/null +++ b/esphomeyaml/dashboard/static/esphomeyaml.css @@ -0,0 +1,218 @@ +nav .brand-logo { + margin-left: 48px; + font-size: 20px; +} + +main .container { + margin-top: -12vh; + flex-shrink: 0; +} + +.ribbon { + width: 100%; + height: 17vh; + background-color: #3F51B5; + flex-shrink: 0; +} + +.ribbon-fab:not(.tap-target-origin) { + position: absolute; + right: 24px; + top: calc(17vh + 34px); +} + +i.very-large { + font-size: 8rem; + padding-top: 2px; + color: #424242; +} + +.card .card-content { + padding-left: 18px; + padding-bottom: 10px; +} + +.card-action a, .card-dropdown-action a { + cursor: pointer; +} + +.inlinecode { + box-sizing: border-box; + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(27,31,35,0.05); + border-radius: 3px; + font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace; +} + +.autoscroll { + display: flex; + flex-direction: column-reverse; + flex-basis: auto; +} + +.autoscroll div { + flex-basis: 100%; +} + +.log { + background-color: #1c1c1c; + margin-top: 0; + margin-bottom: 0; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; + padding: 16px; + overflow: auto; + line-height: 1.45; + border-radius: 3px; + white-space: pre-wrap; + overflow-wrap: break-word; + color: #DDD; +} + +.log-bold { font-weight: bold; } +.log-italic { font-style: italic; } +.log-underline { text-decoration: underline; } +.log-strikethrough { text-decoration: line-through; } +.log-underline.log-strikethrough { text-decoration: underline line-through; } +.log-fg-black { color: rgb(128,128,128); } +.log-fg-red { color: rgb(255,0,0); } +.log-fg-green { color: rgb(0,255,0); } +.log-fg-yellow { color: rgb(255,255,0); } +.log-fg-blue { color: rgb(0,0,255); } +.log-fg-magenta { color: rgb(255,0,255); } +.log-fg-cyan { color: rgb(0,255,255); } +.log-fg-white { color: rgb(187,187,187); } +.log-bg-black { background-color: rgb(0,0,0); } +.log-bg-red { background-color: rgb(255,0,0); } +.log-bg-green { background-color: rgb(0,255,0); } +.log-bg-yellow { background-color: rgb(255,255,0); } +.log-bg-blue { background-color: rgb(0,0,255); } +.log-bg-magenta { background-color: rgb(255,0,255); } +.log-bg-cyan { background-color: rgb(0,255,255); } +.log-bg-white { background-color: rgb(255,255,255); } + +.modal { + width: 95%; + max-height: 90%; + height: 85% !important; +} + +.page-footer { + padding-top: 0; +} + +body { + display: flex; + min-height: 100vh; + flex-direction: column; +} + +main { + flex: 1 0 auto; +} + +ul.browser-default { + padding-left: 30px; + margin-top: 10px; + margin-bottom: 15px; +} + +ul.browser-default li { + list-style-type: initial; +} + +ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before { + background-color: #3f51b5 !important; +} + +.select-port-container { + margin-top: 8px; + margin-right: 24px; + width: 350px; +} + +.dropdown-trigger { + cursor: pointer; +} + +/* https://github.com/tnhu/status-indicator/blob/master/styles.css */ +.status-indicator .status-indicator-icon { + display: inline-block; + border-radius: 50%; + width: 10px; + height: 10px; +} + +.status-indicator.unknown .status-indicator-icon { + background-color: rgb(216, 226, 233); +} + +.status-indicator.unknown .status-indicator-text::after { + content: "Unknown status"; +} + +.status-indicator.offline .status-indicator-icon { + background-color: rgb(255, 77, 77); +} + +.status-indicator.offline .status-indicator-text::after { + content: "Offline"; +} + +.status-indicator.not-responding .status-indicator-icon { + background-color: rgb(255, 170, 0); +} + +.status-indicator.not-responding .status-indicator-text::after { + content: "Not Responding"; +} + +@keyframes status-indicator-pulse-online { + 0% { + box-shadow: 0 0 0 0 rgba(75, 210, 143, .5); + } + 25% { + box-shadow: 0 0 0 10px rgba(75, 210, 143, 0); + } + 30% { + box-shadow: 0 0 0 0 rgba(75, 210, 143, 0); + } +} + +.status-indicator.online .status-indicator-icon { + background-color: rgb(75, 210, 143); + animation-duration: 5s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-direction: normal; + animation-delay: 0s; + animation-fill-mode: none; + animation-name: status-indicator-pulse-online; +} + +.status-indicator.online .status-indicator-text::after { + content: "Online"; +} + +#editor { + margin-top: 0; + margin-bottom: 0; + padding: 16px; + border-radius: 3px; + height: 100% +} + +.update-available i { + vertical-align: bottom; + font-size: 20px !important; + color: #3F51B5 !important; + margin-right: -4.5px; + margin-left: -5.5px; +} + +.flash-using-esphomeflasher { + vertical-align: middle; + color: #666 !important; +} diff --git a/esphomeyaml/dashboard/static/esphomeyaml.js b/esphomeyaml/dashboard/static/esphomeyaml.js new file mode 100644 index 0000000000..f3f9b9c1c1 --- /dev/null +++ b/esphomeyaml/dashboard/static/esphomeyaml.js @@ -0,0 +1,685 @@ +document.addEventListener('DOMContentLoaded', () => { + M.AutoInit(document.body); +}); + +const initializeColorState = () => { + return { + bold: false, + italic: false, + underline: false, + strikethrough: false, + foregroundColor: false, + backgroundColor: false, + carriageReturn: false, + }; +}; + +const colorReplace = (pre, state, text) => { + const re = /(?:\033|\\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g; + let i = 0; + + if (state.carriageReturn) { + if (text !== "\n") { + // don't remove if \r\n + pre.removeChild(pre.lastChild); + } + state.carriageReturn = false; + } + + if (text.includes("\r")) { + state.carriageReturn = true; + } + + const lineSpan = document.createElement("span"); + lineSpan.classList.add("line"); + pre.appendChild(lineSpan); + + const addSpan = (content) => { + if (content === "") + return; + + const span = document.createElement("span"); + if (state.bold) span.classList.add("log-bold"); + if (state.italic) span.classList.add("log-italic"); + if (state.underline) span.classList.add("log-underline"); + if (state.strikethrough) span.classList.add("log-strikethrough"); + if (state.foregroundColor !== null) span.classList.add(`log-fg-${state.foregroundColor}`); + if (state.backgroundColor !== null) span.classList.add(`log-bg-${state.backgroundColor}`); + span.appendChild(document.createTextNode(content)); + lineSpan.appendChild(span); + }; + + + while (true) { + const match = re.exec(text); + if (match === null) + break; + + const j = match.index; + addSpan(text.substring(i, j)); + i = j + match[0].length; + + if (match[1] === undefined) continue; + + for (const colorCode of match[1].split(";")) { + switch (parseInt(colorCode)) { + case 0: + // reset + state.bold = false; + state.italic = false; + state.underline = false; + state.strikethrough = false; + state.foregroundColor = null; + state.backgroundColor = null; + break; + case 1: + state.bold = true; + break; + case 3: + state.italic = true; + break; + case 4: + state.underline = true; + break; + case 9: + state.strikethrough = true; + break; + case 22: + state.bold = false; + break; + case 23: + state.italic = false; + break; + case 24: + state.underline = false; + break; + case 29: + state.strikethrough = false; + break; + case 30: + state.foregroundColor = "black"; + break; + case 31: + state.foregroundColor = "red"; + break; + case 32: + state.foregroundColor = "green"; + break; + case 33: + state.foregroundColor = "yellow"; + break; + case 34: + state.foregroundColor = "blue"; + break; + case 35: + state.foregroundColor = "magenta"; + break; + case 36: + state.foregroundColor = "cyan"; + break; + case 37: + state.foregroundColor = "white"; + break; + case 39: + state.foregroundColor = null; + break; + case 41: + state.backgroundColor = "red"; + break; + case 42: + state.backgroundColor = "green"; + break; + case 43: + state.backgroundColor = "yellow"; + break; + case 44: + state.backgroundColor = "blue"; + break; + case 45: + state.backgroundColor = "magenta"; + break; + case 46: + state.backgroundColor = "cyan"; + break; + case 47: + state.backgroundColor = "white"; + break; + case 40: + case 49: + state.backgroundColor = null; + break; + } + } + } + addSpan(text.substring(i)); +}; + +const removeUpdateAvailable = (filename) => { + const p = document.querySelector(`.update-available[data-node="${filename}"]`); + if (p === undefined) + return; + p.remove(); +}; + +let configuration = ""; +let wsProtocol = "ws:"; +if (window.location.protocol === "https:") { + wsProtocol = 'wss:'; +} +const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port; + +let isFetchingPing = false; +const fetchPing = () => { + if (isFetchingPing) + return; + isFetchingPing = true; + + fetch('/ping', {credentials: "same-origin"}).then(res => res.json()) + .then(response => { + for (let filename in response) { + let node = document.querySelector(`.status-indicator[data-node="${filename}"]`); + if (node === null) + continue; + + let status = response[filename]; + let klass; + if (status === null) { + klass = 'unknown'; + } else if (status === true) { + klass = 'online'; + node.setAttribute('data-last-connected', Date.now().toString()); + } else if (node.hasAttribute('data-last-connected')) { + const attr = parseInt(node.getAttribute('data-last-connected')); + if (Date.now() - attr <= 5000) { + klass = 'not-responding'; + } else { + klass = 'offline'; + } + } else { + klass = 'offline'; + } + + if (node.classList.contains(klass)) + continue; + + node.classList.remove('unknown', 'online', 'offline', 'not-responding'); + node.classList.add(klass); + } + + isFetchingPing = false; + }); +}; +setInterval(fetchPing, 2000); +fetchPing(); + +const portSelect = document.querySelector('.nav-wrapper select'); +let ports = []; + +const fetchSerialPorts = (begin=false) => { + fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json()) + .then(response => { + if (ports.length === response.length) { + let allEqual = true; + for (let i = 0; i < response.length; i++) { + if (ports[i].port !== response[i].port) { + allEqual = false; + break; + } + } + if (allEqual) + return; + } + const hasNewPort = response.length >= ports.length; + + ports = response; + + const inst = M.FormSelect.getInstance(portSelect); + if (inst !== undefined) { + inst.destroy(); + } + + portSelect.innerHTML = ""; + const prevSelected = getUploadPort(); + for (let i = 0; i < response.length; i++) { + const val = response[i]; + if (val.port === prevSelected) { + portSelect.innerHTML += ``; + } else { + portSelect.innerHTML += ``; + } + } + + M.FormSelect.init(portSelect, {}); + if (!begin && hasNewPort) + M.toast({html: "Discovered new serial port."}); + }); +}; + +const getUploadPort = () => { + const inst = M.FormSelect.getInstance(portSelect); + if (inst === undefined) { + return "OTA"; + } + + inst._setSelectedStates(); + return inst.getSelectedValues()[0]; +}; +setInterval(fetchSerialPorts, 5000); +fetchSerialPorts(true); + +const logsModalElem = document.getElementById("modal-logs"); + +document.querySelectorAll(".action-show-logs").forEach((showLogs) => { + showLogs.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(logsModalElem); + const log = logsModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = logsModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = logsModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/logs"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } else if (data.event === "exit") { + if (data.code === 0) { + M.toast({html: "Program exited successfully."}); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } + + stopLogsButton.innerHTML = "Close"; + stopped = true; + } + }); + logSocket.addEventListener('open', () => { + const msg = JSON.stringify({configuration: configuration, port: getUploadPort()}); + logSocket.send(msg); + }); + logSocket.addEventListener('close', () => { + if (!stopped) { + M.toast({html: 'Terminated process.'}); + } + }); + modalInstance.options.onCloseStart = () => { + logSocket.close(); + }; + }); +}); + +const uploadModalElem = document.getElementById("modal-upload"); + +document.querySelectorAll(".action-upload").forEach((upload) => { + upload.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(uploadModalElem); + const log = uploadModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = uploadModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = uploadModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/run"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } else if (data.event === "exit") { + if (data.code === 0) { + M.toast({html: "Program exited successfully."}); + removeUpdateAvailable(configuration); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } + + stopLogsButton.innerHTML = "Close"; + stopped = true; + } + }); + logSocket.addEventListener('open', () => { + const msg = JSON.stringify({configuration: configuration, port: getUploadPort()}); + logSocket.send(msg); + }); + logSocket.addEventListener('close', () => { + if (!stopped) { + M.toast({html: 'Terminated process.'}); + } + }); + modalInstance.options.onCloseStart = () => { + logSocket.close(); + }; + }); +}); + +const validateModalElem = document.getElementById("modal-validate"); + +document.querySelectorAll(".action-validate").forEach((upload) => { + upload.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(validateModalElem); + const log = validateModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = validateModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = validateModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/validate"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } else if (data.event === "exit") { + if (data.code === 0) { + M.toast({ + html: `${configuration} is valid 👍`, + displayLength: 5000, + }); + } else { + M.toast({ + html: `${configuration} is invalid 😕`, + displayLength: 5000, + }); + } + + 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 compileModalElem = document.getElementById("modal-compile"); +const downloadButton = compileModalElem.querySelector('.download-binary'); + +document.querySelectorAll(".action-compile").forEach((upload) => { + upload.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(compileModalElem); + const log = compileModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = compileModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + downloadButton.classList.add('disabled'); + + modalInstance.open(); + + const filenameField = compileModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/compile"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } 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(); + }; + }); +}); + +downloadButton.addEventListener('click', () => { + const link = document.createElement("a"); + link.download = name; + link.href = '/download.bin?configuration=' + encodeURIComponent(configuration); + document.body.appendChild(link); + link.click(); + link.remove(); +}); + +const cleanMqttModalElem = document.getElementById("modal-clean-mqtt"); + +document.querySelectorAll(".action-clean-mqtt").forEach((btn) => { + btn.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(cleanMqttModalElem); + const log = cleanMqttModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = cleanMqttModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = cleanMqttModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/clean-mqtt"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } else if (data.event === "exit") { + 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 cleanModalElem = document.getElementById("modal-clean"); + +document.querySelectorAll(".action-clean").forEach((btn) => { + btn.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(cleanModalElem); + const log = cleanModalElem.querySelector(".log"); + log.innerHTML = ""; + const colorState = initializeColorState(); + const stopLogsButton = cleanModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = cleanModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/clean"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + colorReplace(log, colorState, data.data); + } 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 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 colorState = initializeColorState(); + 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") { + colorReplace(log, colorState, data.data); + } 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 editModalElem = document.getElementById("modal-editor"); +const editorElem = editModalElem.querySelector("#editor"); +const editor = ace.edit(editorElem); +editor.setTheme("ace/theme/dreamweaver"); +editor.session.setMode("ace/mode/yaml"); +editor.session.setOption('useSoftTabs', true); +editor.session.setOption('tabSize', 2); + +const saveButton = editModalElem.querySelector(".save-button"); +const saveEditor = () => { + fetch(`/edit?configuration=${configuration}`, { + credentials: "same-origin", + method: "POST", + body: editor.getValue() + }).then(res => res.text()).then(() => { + M.toast({ + html: `Saved ${configuration}` + }); + }); +}; + +editor.commands.addCommand({ + name: 'saveCommand', + bindKey: {win: 'Ctrl-S', mac: 'Command-S'}, + exec: saveEditor, + readOnly: false +}); + +saveButton.addEventListener('click', saveEditor); + +document.querySelectorAll(".action-edit").forEach((btn) => { + btn.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(editModalElem); + const filenameField = editModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + fetch(`/edit?configuration=${configuration}`, {credentials: "same-origin"}) + .then(res => res.text()).then(response => { + editor.setValue(response, -1); + }); + + modalInstance.open(); + }); +}); + +const modalSetupElem = document.getElementById("modal-wizard"); +const setupWizardStart = document.getElementById('setup-wizard-start'); +const startWizard = () => { + const modalInstance = M.Modal.getInstance(modalSetupElem); + modalInstance.open(); + + modalInstance.options.onCloseStart = () => { + + }; + + $('.stepper').activateStepper({ + linearStepsNavigation: false, + autoFocusInput: true, + autoFormCreation: true, + showFeedbackLoader: true, + parallel: false + }); +}; + +setupWizardStart.addEventListener('click', startWizard); \ No newline at end of file diff --git a/esphomeyaml/dashboard/static/ext-searchbox.js b/esphomeyaml/dashboard/static/ext-searchbox.js new file mode 100644 index 0000000000..13241257d3 --- /dev/null +++ b/esphomeyaml/dashboard/static/ext-searchbox.js @@ -0,0 +1,8 @@ +ace.define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/lang"),s=e("../lib/event"),o='.ace_search {background-color: #ddd;color: #666;border: 1px solid #cbcbcb;border-top: 0 none;overflow: hidden;margin: 0;padding: 4px 6px 0 4px;position: absolute;top: 0;z-index: 99;white-space: normal;}.ace_search.left {border-left: 0 none;border-radius: 0px 0px 5px 0px;left: 0;}.ace_search.right {border-radius: 0px 0px 0px 5px;border-right: 0 none;right: 0;}.ace_search_form, .ace_replace_form {margin: 0 20px 4px 0;overflow: hidden;line-height: 1.9;}.ace_replace_form {margin-right: 0;}.ace_search_form.ace_nomatch {outline: 1px solid red;}.ace_search_field {border-radius: 3px 0 0 3px;background-color: white;color: black;border: 1px solid #cbcbcb;border-right: 0 none;outline: 0;padding: 0;font-size: inherit;margin: 0;line-height: inherit;padding: 0 6px;min-width: 17em;vertical-align: top;min-height: 1.8em;box-sizing: content-box;}.ace_searchbtn {border: 1px solid #cbcbcb;line-height: inherit;display: inline-block;padding: 0 6px;background: #fff;border-right: 0 none;border-left: 1px solid #dcdcdc;cursor: pointer;margin: 0;position: relative;color: #666;}.ace_searchbtn:last-child {border-radius: 0 3px 3px 0;border-right: 1px solid #cbcbcb;}.ace_searchbtn:disabled {background: none;cursor: default;}.ace_searchbtn:hover {background-color: #eef1f6;}.ace_searchbtn.prev, .ace_searchbtn.next {padding: 0px 0.7em}.ace_searchbtn.prev:after, .ace_searchbtn.next:after {content: "";border: solid 2px #888;width: 0.5em;height: 0.5em;border-width: 2px 0 0 2px;display:inline-block;transform: rotate(-45deg);}.ace_searchbtn.next:after {border-width: 0 2px 2px 0 ;}.ace_searchbtn_close {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;border-radius: 50%;border: 0 none;color: #656565;cursor: pointer;font: 16px/16px Arial;padding: 0;height: 14px;width: 14px;top: 9px;right: 7px;position: absolute;}.ace_searchbtn_close:hover {background-color: #656565;background-position: 50% 100%;color: white;}.ace_button {margin-left: 2px;cursor: pointer;-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;-ms-user-select: none;user-select: none;overflow: hidden;opacity: 0.7;border: 1px solid rgba(100,100,100,0.23);padding: 1px;box-sizing: border-box!important;color: black;}.ace_button:hover {background-color: #eee;opacity:1;}.ace_button:active {background-color: #ddd;}.ace_button.checked {border-color: #3399ff;opacity:1;}.ace_search_options{margin-bottom: 3px;text-align: right;-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;-ms-user-select: none;user-select: none;clear: both;}.ace_search_counter {float: left;font-family: arial;padding: 0 8px;}',u=e("../keyboard/hash_handler").HashHandler,a=e("../lib/keys"),f=999;r.importCssString(o,"ace_searchbox");var l=''.replace(/> +/g,">"),c=function(e,t,n){var i=r.createElement("div");i.innerHTML=l,this.element=i.firstChild,this.setSession=this.setSession.bind(this),this.$init(),this.setEditor(e),r.importCssString(o,"ace_searchbox",e.container)};(function(){this.setEditor=function(e){e.searchBox=this,e.renderer.scroller.appendChild(this.element),this.editor=e},this.setSession=function(e){this.searchRange=null,this.$syncOptions(!0)},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOption=e.querySelector("[action=searchInSelection]"),this.replaceOption=e.querySelector("[action=toggleReplace]"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field"),this.searchCounter=e.querySelector(".ace_search_counter")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.replaceOption.checked=!1,e.$syncOptions(),e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){if(e.editor.getReadOnly())return;e.replaceOption.checked=!0,e.$syncOptions(),e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}},{name:"toggleReplace",exec:function(e){e.replaceOption.checked=!e.replaceOption.checked,e.$syncOptions()}},{name:"searchInSelection",exec:function(e){e.searchOption.checked=!e.searchRange,e.setSearchRange(e.searchOption.checked&&e.editor.getSelectionRange()),e.$syncOptions()}}]),this.setSearchRange=function(e){this.searchRange=e,e?this.searchRangeMarker=this.editor.session.addMarker(e,"ace_active-line"):this.searchRangeMarker&&(this.editor.session.removeMarker(this.searchRangeMarker),this.searchRangeMarker=null)},this.$syncOptions=function(e){r.setCssClass(this.replaceOption,"checked",this.searchRange),r.setCssClass(this.searchOption,"checked",this.searchOption.checked),this.replaceOption.textContent=this.replaceOption.checked?"-":"+",r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked);var t=this.editor.getReadOnly();this.replaceOption.style.display=t?"none":"",this.replaceBox.style.display=this.replaceOption.checked&&!t?"":"none",this.find(!1,!1,e)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,n){var i=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n,range:this.searchRange}),s=!i&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",s),this.editor._emit("findSearchBox",{match:!s}),this.highlight(),this.updateCounter()},this.updateCounter=function(){var e=this.editor,t=e.$search.$options.re,n=0,r=0;if(t){var i=this.searchRange?e.session.getTextRange(this.searchRange):e.getValue(),s=e.session.doc.positionToIndex(e.selection.anchor);this.searchRange&&(s-=e.session.doc.positionToIndex(this.searchRange.start));var o=t.lastIndex=0,u;while(u=t.exec(i)){n++,o=u.index,o<=s&&r++;if(n>f)break;if(!u[0]){t.lastIndex=o+=1;if(o>=i.length)break}}}this.searchCounter.textContent=r+" of "+(n>f?f+"+":n)},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.active=!1,this.setSearchRange(null),this.editor.off("changeSession",this.setSession),this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.active=!0,this.editor.on("changeSession",this.setSession),this.element.style.display="",this.replaceOption.checked=t,e&&(this.searchInput.value=e),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb),this.$syncOptions(!0)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(c.prototype),t.SearchBox=c,t.Search=function(e,t){var n=e.searchBox||new c(e);n.show(e.session.getTextRange(),t)}}); (function() { + ace.require(["ace/ext/searchbox"], function(m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); + })(); + \ No newline at end of file diff --git a/esphomeyaml/dashboard/static/favicon.ico b/esphomeyaml/dashboard/static/favicon.ico new file mode 100644 index 0000000000..5aaaf3fb24 Binary files /dev/null and b/esphomeyaml/dashboard/static/favicon.ico differ diff --git a/esphomeyaml/dashboard/static/jquery-ui.min.js b/esphomeyaml/dashboard/static/jquery-ui.min.js new file mode 100644 index 0000000000..a4c69733c0 --- /dev/null +++ b/esphomeyaml/dashboard/static/jquery-ui.min.js @@ -0,0 +1,401 @@ +/*! + * jQuery UI 1.8.5 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.5",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, +"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); +if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"));if(!isNaN(b)&&b!=0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind("mousedown.ui-disableSelection selectstart.ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, +"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c.style(this,h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c.style(this, +h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); +c(function(){var a=document.createElement("div"),b=document.body;c.extend(a.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.appendChild(a).offsetHeight===100;b.removeChild(a).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top- +this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions(); +d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis|| +this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if(!this.element[0]||!this.element[0].parentNode)return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element, +b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== +a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]|| +0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], +this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top- +(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment== +"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&& +a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"), +10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], +this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft(): +f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.leftthis.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?e:!(e-this.offset.click.left').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options; +if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!= +"HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= +i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), +top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= +this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", +nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== +String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),k=0;k=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,k);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){e(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; +if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(), +d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset= +this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio: +this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize", +b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height; +f={width:c.size.width-(f?0:c.sizeDiff.width),height:c.size.height-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop", +b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(l(b.left))this.position.left=b.left;if(l(b.top))this.position.top=b.top;if(l(b.height))this.size.height=b.height;if(l(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top= +a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,d=l(b.width)&&a.maxWidth&&a.maxWidthb.width,h=l(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height, +k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minWidth;if(d&&k)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+ +a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this, +arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable, +{version:"1.8.5"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize, +function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n= +(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition= +false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left- +a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize", +b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top", +"Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset, +f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left= +a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+ +a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&& +e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative", +height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width= +d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery); +(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), +selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, +c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", +c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= +this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); +this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this, +arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= +c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, +{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); +if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", +a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); +if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, +c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== +document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate", +null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem): +d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute|| +"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")}, +_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!= +this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a= +this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable"); +if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h= +0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width= +this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f}, +update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b= +null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this)); +this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])? +g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive", +g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over= +0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"}); +c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c|| +typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c]||typeof c== +"boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c, +a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/= +e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+ +b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/ +2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100* +f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); +(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); +a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); +if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var f=d.closest(".ui-accordion-header");a.active=f.length?f:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",function(g){return a._keydown(g)}).next().attr("role", +"tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(g){a._clickHandler.call(a,g,this);g.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers); +this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex"); +this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); +b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,f=this.headers.index(a.target),g=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:g=this.headers[(f+1)%d];break;case b.LEFT:case b.UP:g=this.headers[(f-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); +a.preventDefault()}if(g){c(a.target).attr("tabIndex",-1);c(g).attr("tabIndex",0);g.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ +c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options; +if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected); +a.next().addClass("ui-accordion-content-active")}h=a.next();f=this.active.next();g={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):h,oldContent:f};d=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(h,f,g,b,d)}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); +this.active.next().addClass("ui-accordion-content-active");var f=this.active.next(),g={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:f},h=this.active=c([]);this._toggle(h,f,g)}},_toggle:function(a,b,d,f,g){var h=this,e=h.options;h.toShow=a;h.toHide=b;h.data=d;var j=function(){if(h)return h._completed.apply(h,arguments)};h._trigger("changestart",null,h.data);h.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&f?{toShow:c([]),toHide:b,complete:j, +down:g,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:g,autoHeight:e.autoHeight||e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;f=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!f[k]&&!c.easing[k])k="slide";f[k]||(f[k]=function(l){this.slide(l,{easing:k,duration:i||700})}); +f[k](d)}else{if(e.collapsible&&f)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.5",animations:{slide:function(a, +b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),f=0,g={},h={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){h[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);g[i]={value:j[1], +unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(h,{step:function(j,i){if(i.prop=="height")f=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=f*g[i.prop].value+g[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",paddingTop:"hide", +paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); +(function(e){e.widget("ui.autocomplete",{options:{appendTo:"body",delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},_create:function(){var a=this,b=this.element[0].ownerDocument;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!a.options.disabled){var d=e.ui.keyCode;switch(c.keyCode){case d.PAGE_UP:a._move("previousPage", +c);break;case d.PAGE_DOWN:a._move("nextPage",c);break;case d.UP:a._move("previous",c);c.preventDefault();break;case d.DOWN:a._move("next",c);c.preventDefault();break;case d.ENTER:case d.NUMPAD_ENTER:a.menu.element.is(":visible")&&c.preventDefault();case d.TAB:if(!a.menu.active)return;a.menu.select(c);break;case d.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay); +break}}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};this.menu=e("
    ").addClass("ui-autocomplete").appendTo(e(this.options.appendTo||"body",b)[0]).mousedown(function(c){var d=a.menu.element[0]; +c.target===d&&setTimeout(function(){e(document).one("mousedown",function(f){f.target!==a.element[0]&&f.target!==d&&!e.ui.contains(d,f.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,d){d=d.item.data("item.autocomplete");false!==a._trigger("focus",null,{item:d})&&/^key/.test(c.originalEvent.type)&&a.element.val(d.value)},selected:function(c,d){d=d.item.data("item.autocomplete");var f=a.previous;if(a.element[0]!==b.activeElement){a.element.focus(); +a.previous=f}if(false!==a._trigger("select",c,{item:d})){a.term=d.value;a.element.val(d.value)}a.close(c);a.selectedItem=d},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");e.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"); +this.menu.element.remove();e.Widget.prototype.destroy.call(this)},_setOption:function(a,b){e.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(e(b||"body",this.element[0].ownerDocument)[0])},_initSource:function(){var a=this,b,c;if(e.isArray(this.options.source)){b=this.options.source;this.source=function(d,f){f(e.ui.autocomplete.filter(b,d.term))}}else if(typeof this.options.source==="string"){c=this.options.source;this.source= +function(d,f){a.xhr&&a.xhr.abort();a.xhr=e.getJSON(c,d,function(g,i,h){h===a.xhr&&f(g);a.xhr=null})}}else this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(e("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});e.extend(e.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")}, +filter:function(a,b){var c=new RegExp(e.ui.autocomplete.escapeRegex(b),"i");return e.grep(a,function(d){return c.test(d.label||d.value||d)})}})})(jQuery); +(function(e){e.widget("ui.menu",{_create:function(){var a=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(b){if(e(b.target).closest(".ui-menu-item a").length){b.preventDefault();a.select(b)}});this.refresh()},refresh:function(){var a=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(b){a.activate(b,e(this).parent())}).mouseleave(function(){a.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.attr("scrollTop"),f=this.element.height();if(c<0)this.element.attr("scrollTop",d+c);else c>=f&&this.element.attr("scrollTop",d+c-f+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",a,{item:b})}, +deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(this.active){a=this.active[a+"All"](".ui-menu-item").eq(0); +a.length?this.activate(c,a):this.activate(c,this.element.children(b))}else this.activate(c,this.element.children(b))},nextPage:function(a){if(this.hasScroll())if(!this.active||this.last())this.activate(a,this.element.children(":first"));else{var b=this.active.offset().top,c=this.element.height(),d=this.element.children("li").filter(function(){var f=e(this).offset().top-b-c+e(this).height();return f<10&&f>-10});d.length||(d=this.element.children(":last"));this.activate(a,d)}else this.activate(a,this.element.children(!this.active|| +this.last()?":first":":last"))},previousPage:function(a){if(this.hasScroll())if(!this.active||this.first())this.activate(a,this.element.children(":last"));else{var b=this.active.offset().top,c=this.element.height();result=this.element.children("li").filter(function(){var d=e(this).offset().top-b+c-e(this).height();return d<10&&d>-10});result.length||(result=this.element.children(":first"));this.activate(a,result)}else this.activate(a,this.element.children(!this.active||this.first()?":last":":first"))}, +hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary;if(d.primary||d.secondary){b.addClass("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("");d.secondary&&b.append("");if(!this.options.text){b.addClass(e?"ui-button-icons-only":"ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary"); +this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}});a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":visible").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end().end()}, +destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); +(function(d,G){function L(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass= +"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su", +"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10", +minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=d('
    ')}function E(a,b){d.extend(a, +b);for(var c in b)if(b[c]==null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.5"}});var y=(new Date).getTime();d.extend(L.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]= +f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
    ')}}, +_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& +b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== +""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, +c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), +true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor== +Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]); +d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}}, +_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b= +d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; +for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target|| +a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a); +d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&& +d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f, +h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover"); +this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover"); +this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); +a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(), +k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"]; +a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val(): +"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&& +!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth; +b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b= +this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a= +d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a, +"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b== +"object"?b.toString():b+"";if(b=="")return null;for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1 +-1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24* +60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e? +"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k= +this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a, +"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+ +n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var M=this._getDefaultDate(a),I="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
    '+(/all|left/.test(t)&&C==0?c? +f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'
    ';var A=k?'":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, +A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O";var P=!k?"":'";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,K=B&&!H||!F[0]||j&&qo;P+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=P+""}g++;if(g>11){g=0;m++}x+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(q)+""+(B&&!w?" ":K?''+q.getDate()+ +"":''+q.getDate()+"")+"
    "+(l?""+(i[0]>0&&D==i[1]-1?'
    ':""):"");N+=x}I+=N}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': +"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='
    ',o="";if(h||!k)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(j+=o+(h||!(k&&l)?" ":""));if(h||!l)j+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b, +i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?" ":"")+o;j+="
    ";return j},_adjustInstDate:function(a,b,c){var e= +a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, +"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); +c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, +"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= +function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); +return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new L;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.5";window["DP_jQuery_"+y]=d})(jQuery); +(function(c,j){c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:{my:"center",at:"center",of:window,collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title"); +if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",f=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog", +"aria-labelledby":f}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var e=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i); +return false}).appendTo(e);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id",f).html(d).prependTo(e);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;e.find("*").add(e).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&& +g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog"); +b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==b.uiDialog[0])d=Math.max(d,c(this).css("z-index"))});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,f=d.options;if(f.modal&&!a||!f.stack&&!f.modal)return d._trigger("focus",b);if(f.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ= +f.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;d.next().length&&d.appendTo("body");a._size();a._position(b.position);d.show(b.show); +a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(f){if(f.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),e=g.filter(":first");g=g.filter(":last");if(f.target===g[0]&&!f.shiftKey){e.focus(1);return false}else if(f.target===e[0]&&f.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false, +f=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(f);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a,function(e,h){h=c.isFunction(h)?{click:h,text:e}:h;e=c("",h).unbind("click").click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.fn.button&&e.button()});f.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(e){return{position:e.position, +offset:e.offset}}var b=this,d=b.options,f=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(e,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",e,a(h))},drag:function(e,h){b._trigger("drag",e,a(h))},stop:function(e,h){d.position=[h.position.left-f.scrollLeft(),h.position.top-f.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g); +b._trigger("dragStop",e,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}a=a===j?this.options.resizable:a;var d=this,f=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:f.maxWidth,maxHeight:f.maxHeight,minWidth:f.minWidth,minHeight:d._minHeight(), +handles:a,start:function(e,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",e,b(h))},resize:function(e,h){d._trigger("resize",e,b(h))},stop:function(e,h){c(this).removeClass("ui-dialog-resizing");f.height=c(this).height();f.width=c(this).width();d._trigger("resizeStop",e,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight, +a.height)},_position:function(a){var b=[],d=[0,0],f;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,e){if(+b[g]===b[g]){d[g]=b[g];b[g]=e}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(f=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(a); +f||this.uiDialog.hide()},_setOption:function(a,b){var d=this,f=d.uiDialog,g=f.is(":data(resizable)"),e=false;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);e=true;break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":f.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case "draggable":b? +d._makeDraggable():f.draggable("destroy");break;case "height":e=true;break;case "maxHeight":g&&f.resizable("option","maxHeight",b);e=true;break;case "maxWidth":g&&f.resizable("option","maxWidth",b);e=true;break;case "minHeight":g&&f.resizable("option","minHeight",b);e=true;break;case "minWidth":g&&f.resizable("option","minWidth",b);e=true;break;case "position":d._position(b);break;case "resizable":g&&!b&&f.resizable("destroy");g&&typeof b==="string"&&f.resizable("option","handles",b);!g&&b!==false&& +d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break;case "width":e=true;break}c.Widget.prototype._setOption.apply(d,arguments);e&&d._size()},_size:function(){var a=this.options,b;this.element.css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();this.element.css(a.height==="auto"?{minHeight:Math.max(a.minHeight-b,0),height:c.support.minHeight?"auto":Math.max(a.minHeight- +b,0)}:{minHeight:0,height:Math.max(a.height-b,0)}).show();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.5",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","), +function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a, +b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0]; +b.left+=a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d= +c(b),g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); +(function(b,c){b.widget("ui.progressbar",{options:{value:0},min:0,max:100,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===c)return this._value();this._setOption("value",a);return this},_setOption:function(a,d){if(a==="value"){this.options.value=d;this._refreshValue();this._trigger("change")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.max,Math.max(this.min,a))},_refreshValue:function(){var a=this.value();this.valueDiv.toggleClass("ui-corner-right", +a===this.max).width(a+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.5"})})(jQuery); +(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options;this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");b.disabled&&this.element.addClass("ui-slider-disabled ui-disabled"); +this.range=d([]);if(b.range){if(b.range===true){this.range=d("
    ");if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}else this.range=d("
    ");this.range.appendTo(this.element).addClass("ui-slider-range");if(b.range==="min"||b.range==="max")this.range.addClass("ui-slider-range-"+b.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("").appendTo(this.element).addClass("ui-slider-handle"); +if(b.values&&b.values.length)for(;d(".ui-slider-handle",this.element).length").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur(); +else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),h,g,i;if(!a.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e= +false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");h=a._start(c,f);if(h===false)return}break}i=a.options.step;h=a.options.values&&a.options.values.length?(g=a.values(f)):(g=a.value());switch(c.keyCode){case d.ui.keyCode.HOME:g=a._valueMin();break;case d.ui.keyCode.END:g=a._valueMax();break;case d.ui.keyCode.PAGE_UP:g=a._trimAlignValue(h+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:g=a._trimAlignValue(h-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(h=== +a._valueMax())return;g=a._trimAlignValue(h+i);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(h===a._valueMin())return;g=a._trimAlignValue(h-i);break}a._slide(c,f,g);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(c,e);a._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); +this._mouseDestroy();return this},_mouseCapture:function(a){var b=this.options,c,e,f,h,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});e=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(i){var j=Math.abs(c-h.values(i));if(e>j){e=j;f=d(this);g=i}});if(b.range===true&&this.values(1)===b.min){g+=1;f=d(this.handles[g])}if(this._start(a, +g)===false)return false;this._mouseSliding=true;h._handleIndex=g;f.addClass("ui-state-active").focus();b=f.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-f.width()/2,top:a.pageY-b.top-f.height()/2-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b= +this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b= +this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b); +c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var e;if(this.options.values&&this.options.values.length){e=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>e||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;fthis._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=a%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= +this.options.range,b=this.options,c=this,e=!this._animateOff?b.animate:false,f,h={},g,i,j,l;if(this.options.values&&this.options.values.length)this.handles.each(function(k){f=(c.values(k)-c._valueMin())/(c._valueMax()-c._valueMin())*100;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](h,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(k===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({width:f- +g+"%"},{queue:false,duration:b.animate})}else{if(k===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({height:f-g+"%"},{queue:false,duration:b.animate})}g=f});else{i=this.value();j=this._valueMin();l=this._valueMax();f=l!==j?(i-j)/(l-j)*100:0;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](h,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"}, +b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.5"})})(jQuery); +(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(a,e){if(a=="selected")this.options.collapsible&& +e==this.options.selected||this.select(e);else{this.options[a]=e;this._tabify()}},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var a=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[a].concat(d.makeArray(arguments)))},_ui:function(a,e){return{tab:a,panel:e,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var a= +d(this);a.html(a.data("label.tabs")).removeData("label.tabs")})},_tabify:function(a){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var b=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| +(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))b.panels=b.panels.add(b._sanitizeSelector(i));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=b._tabId(f);f.href="#"+i;f=d("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(b.panels[g-1]||b.list);f.data("destroy.tabs",true)}b.panels=b.panels.add(f)}else c.disabled.push(g)});if(a){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(b._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return b.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(c.selected>=0&&this.anchors.length){this.panels.eq(c.selected).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");b.element.queue("tabs",function(){b._trigger("show",null,b._ui(b.anchors[c.selected],b.panels[c.selected]))});this.load(c.selected)}d(window).bind("unload",function(){b.lis.add(b.anchors).unbind(".tabs");b.lis=b.anchors=b.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));this.element[c.collapsible?"addClass": +"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);a=0;for(var j;j=this.lis[a];a++)d(j)[d.inArray(a,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+g)};this.lis.bind("mouseover.tabs", +function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",function(){e(f,o);b._trigger("show", +null,b._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");b._trigger("show",null,b._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){b.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);b.element.dequeue("tabs")})}:function(g,f){b.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");b.element.dequeue("tabs")};this.anchors.bind(c.event+".tabs", +function(){var g=this,f=d(g).closest("li"),i=b.panels.filter(":not(.ui-tabs-hide)"),l=d(b._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||b.panels.filter(":animated").length||b._trigger("select",null,b._ui(this,l[0]))===false){this.blur();return false}c.selected=b.anchors.index(this);b.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=-1;c.cookie&&b._cookie(c.selected,c.cookie);b.element.queue("tabs", +function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&b._cookie(c.selected,c.cookie);b.element.queue("tabs",function(){r(g,l)});b.load(b.anchors.index(this));this.blur();return false}c.cookie&&b._cookie(c.selected,c.cookie);if(l.length){i.length&&b.element.queue("tabs",function(){s(g,i)});b.element.queue("tabs",function(){r(g,l)});b.load(b.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";d.browser.msie&&this.blur()});this.anchors.bind("click.tabs", +function(){return false})},_getIndex:function(a){if(typeof a=="string")a=this.anchors.index(this.anchors.filter("[href$="+a+"]"));return a},destroy:function(){var a=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=d.data(this,"href.tabs");if(e)this.href= +e;var b=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){b.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});a.cookie&&this._cookie(null,a.cookie);return this},add:function(a,e,b){if(b===p)b=this.anchors.length; +var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,a).replace(/#\{label\}/g,e));a=!a.indexOf("#")?a.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=d("#"+a);j.length||(j=d(h.panelTemplate).attr("id",a).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(b>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[b]); +j.insertBefore(this.panels[b])}h.disabled=d.map(h.disabled,function(k){return k>=b?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[b],this.panels[b]));return this},remove:function(a){a=this._getIndex(a);var e=this.options,b=this.lis.eq(a).remove(),c=this.panels.eq(a).remove(); +if(b.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(a+(a+1=a?--h:h});this._tabify();this._trigger("remove",null,this._ui(b.find("a")[0],c[0]));return this},enable:function(a){a=this._getIndex(a);var e=this.options;if(d.inArray(a,e.disabled)!=-1){this.lis.eq(a).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(b){return b!=a});this._trigger("enable",null, +this._ui(this.anchors[a],this.panels[a]));return this}},disable:function(a){a=this._getIndex(a);var e=this.options;if(a!=e.selected){this.lis.eq(a).addClass("ui-state-disabled");e.disabled.push(a);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a]))}return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this}, +load:function(a){a=this._getIndex(a);var e=this,b=this.options,c=this.anchors.eq(a)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(a).addClass("ui-state-processing");if(b.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(b.spinner)}this.xhr=d.ajax(d.extend({},b.ajaxOptions,{url:h,success:function(k,n){d(e._sanitizeSelector(c.hash)).html(k);e._cleanup();b.cache&&d.data(c,"cache.tabs", +true);e._trigger("load",null,e._ui(e.anchors[a],e.panels[a]));try{b.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[a],e.panels[a]));try{b.ajaxOptions.error(k,n,a,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},url:function(a, +e){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.5"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(a,e){var b=this,c=this.options,h=b._rotate||(b._rotate=function(j){clearTimeout(b.rotation);b.rotation=setTimeout(function(){var k=c.selected;b.select(++ka?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; +}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("