diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0c0a46ee..8e50c277 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ FROM mcr.microsoft.com/devcontainers/python:0-3.11 # EXPOSE 5000:5000 -COPY .devcontainer/setup.sh requirements.txt requirements_webserver.txt ./ +COPY .devcontainer/setup.sh requirements.txt ./ RUN ./setup.sh diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index 220c197c..2939a230 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -18,7 +18,7 @@ main() { export HDF5_DIR=/usr/include/hdf5 pip install netCDF4 - pip install -r requirements_webserver.txt + pip install -r requirements.txt pip install requests-mock rm -rf "$0" diff --git a/.github/workflows/docker-build-test.yaml b/.github/workflows/docker-build-test.yaml index af5caf22..1317344a 100644 --- a/.github/workflows/docker-build-test.yaml +++ b/.github/workflows/docker-build-test.yaml @@ -1,5 +1,5 @@ #This action test builds EMHASS standalone Docker images, in each architecture. -name: "Test Docker Stanadlone" +name: "Test Docker Standalone" on: push: @@ -15,10 +15,10 @@ jobs: fail-fast: false matrix: platform: [ - {buildx: linux/amd64, target_arch: amd64}, - {buildx: linux/arm/v7, target_arch: armv7}, - {buildx: linux/arm/v7, target_arch: armhf}, - {buildx: linux/arm64, target_arch: aarch64} + {buildx: linux/amd64, target_arch: amd64, os_version: debian}, + {buildx: linux/arm/v7, target_arch: armv7, os_version: debian}, + {buildx: linux/arm/v7, target_arch: armhf, os_version: raspbian}, + {buildx: linux/arm64, target_arch: aarch64, os_version: debian} ] steps: - name: Checkout the repository @@ -36,6 +36,7 @@ jobs: build-args: | build_version=standalone TARGETARCH=${{ matrix.platform.target_arch }} + os_version=${{ matrix.platform.os_version }} tags: emhass/standalone-test load: true - name: Test #assume docker fail with FileNotFound secrets_emhass.yaml error diff --git a/.github/workflows/publish_docker.yaml b/.github/workflows/publish_docker.yaml index b87099c8..a64cbe3b 100644 --- a/.github/workflows/publish_docker.yaml +++ b/.github/workflows/publish_docker.yaml @@ -13,10 +13,10 @@ jobs: fail-fast: false matrix: platform: [ - {buildx: linux/amd64, target_arch: amd64}, - {buildx: linux/arm/v7, target_arch: armv7}, - {buildx: linux/arm/v7, target_arch: armhf}, - {buildx: linux/arm64, target_arch: aarch64} + {buildx: linux/amd64, target_arch: amd64, os_version: debian}, + {buildx: linux/arm/v7, target_arch: armv7, os_version: debian}, + {buildx: linux/arm/v7, target_arch: armhf, os_version: raspbian}, + {buildx: linux/arm64, target_arch: aarch64, os_version: debian} ] steps: - name: Checkout the repository @@ -45,6 +45,7 @@ jobs: build-args: | build_version=standalone TARGETARCH=${{ matrix.platform.target_arch }} + os_version=${{ matrix.platform.os_version }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/emhass-docker-standalone,push-by-digest=true,name-canonical=true,push=true - name: Export digest diff --git a/.vscode/launch.json b/.vscode/launch.json index e3c4716d..10313c97 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,9 +12,8 @@ "name": "EMHASS run", "type": "debugpy", "request": "launch", - "program": "web_server.py", + "module": "emhass.web_server", "console": "integratedTerminal", - "cwd": "${workspaceFolder}/src/emhass/", "purpose":["debug-in-terminal"], "justMyCode": true, "env": { @@ -29,9 +28,8 @@ "name": "EMHASS run ADDON", "type": "debugpy", "request": "launch", - "program": "web_server.py", + "module": "emhass.web_server", "console": "integratedTerminal", - "cwd": "${workspaceFolder}/src/emhass/", "args": ["--addon", "true", "--no_response", "true"], "purpose":["debug-in-terminal"], "justMyCode": true, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5be6806e..11a92388 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,13 +3,13 @@ "tasks": [ { "label": "EMHASS install", - "command": "sudo", + "command": "pip3", "group": { "kind": "build", "isDefault": true }, "args": [ - "python3", "-m", "pip", "install", "." + "install", "--no-deps", "--force-reinstall", "." ], "presentation": { "echo": true, diff --git a/Dockerfile b/Dockerfile index 00b90e4d..9eb1decf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,28 @@ -## EMHASS Docker -## Docker run ADD-ON testing example: -## docker build -t emhass/docker --build-arg build_version=addon-local . -## docker run -it -p 5000:5000 --name emhass-container -e LAT="45.83" -e LON="6.86" -e ALT="4807.8" -e TIME_ZONE="Europe/Paris" emhass/docker --url YOURHAURLHERE --key YOURHAKEYHERE -## -## Docker run Standalone example: -## docker build -t emhass/docker --build-arg build_version=standalone . -## docker run -it -p 5000:5000 --name emhass-container -v $(pwd)/config_emhass.yaml:/app/config_emhass.yaml -v $(pwd)/secrets_emhass.yaml:/app/secrets_emhass.yaml emhass/docker +## EMHASS Docker +## Docker run addon testing example: + ## docker build -t emhass/docker --build-arg build_version=addon-local . + ## docker run -it -p 5000:5000 --name emhass-container -e LAT="45.83" -e LON="6.86" -e ALT="4807.8" -e TIME_ZONE="Europe/Paris" emhass/docker --url YOURHAURLHERE --key YOURHAKEYHERE +## Docker run standalone example: + ## docker build -t emhass/docker --build-arg build_version=standalone . + ## docker run -it -p 5000:5000 --name emhass-container -v $(pwd)/config_emhass.yaml:/app/config_emhass.yaml -v $(pwd)/secrets_emhass.yaml:/app/secrets_emhass.yaml emhass/docker #build_version options are: addon, addon-pip, addon-git, addon-local, standalone (default) ARG build_version=standalone -FROM ghcr.io/home-assistant/$TARGETARCH-base-debian:bookworm AS base + +#armhf=raspbian, amd64,armv7,aarch64=debian +ARG os_version=debian + +FROM ghcr.io/home-assistant/$TARGETARCH-base-$os_version:bookworm AS base + +#check if TARGETARCH was passed by build-arg +ARG TARGETARCH +ENV TARGETARCH=${TARGETARCH:?} WORKDIR /app COPY requirements.txt /app/ -# Setup +#apt package install RUN apt-get update \ && apt-get install -y --no-install-recommends \ libffi-dev \ @@ -23,41 +30,60 @@ RUN apt-get update \ python3-pip \ python3-dev \ git \ - build-essential \ gcc \ - coinor-cbc \ - coinor-libcbc-dev \ - libglpk-dev \ - glpk-utils \ + patchelf \ + cmake \ + meson \ + ninja-build \ + build-essential \ libhdf5-dev \ libhdf5-serial-dev \ - netcdf-bin \ - libnetcdf-dev \ pkg-config \ gfortran \ + netcdf-bin \ + libnetcdf-dev \ + coinor-cbc \ + coinor-libcbc-dev \ + libglpk-dev \ + glpk-utils \ libatlas-base-dev \ - && ln -s /usr/include/hdf5/serial /usr/include/hdf5/include \ - && export HDF5_DIR=/usr/include/hdf5 \ - && pip3 install --extra-index-url=https://www.piwheels.org/simple --no-cache-dir --break-system-packages -U setuptools wheel \ - && pip3 install --extra-index-url=https://www.piwheels.org/simple --no-cache-dir --break-system-packages -r requirements.txt \ - && apt-get purge -y --auto-remove \ + libopenblas-dev +#specify hdf5 +RUN ln -s /usr/include/hdf5/serial /usr/include/hdf5/include && export HDF5_DIR=/usr/include/hdf5 + +#install packages from pip, use piwheels if arm 32bit +RUN [[ "${TARGETARCH}" == "armhf" || "${TARGETARCH}" == "armv7" ]] && pip3 install --index-url=https://www.piwheels.org/simple --no-cache-dir --break-system-packages -r requirements.txt || pip3 install --no-cache-dir --break-system-packages -r requirements.txt + +#try, symlink apt cbc, to pulp cbc, in python directory (for 32bit) +RUN [[ "${TARGETARCH}" == "armhf" || "${TARGETARCH}" == "armv7" ]] && ln -sf /usr/bin/cbc /usr/local/lib/python3.11/dist-packages/pulp/solverdir/cbc/linux/32/cbc || echo "cbc symlink didnt work/not required" + +#if armv7, try install libatomic1 to fix scipy issue +RUN [[ "${TARGETARCH}" == "armv7" ]] && apt-get update && apt-get install libatomic1 || echo "libatomic1 cant be installed" + + +#remove build only packages +RUN apt-get purge -y --auto-remove \ + git \ gcc \ + patchelf \ + cmake \ + meson \ + ninja-build \ build-essential \ - libhdf5-dev \ - libhdf5-serial-dev \ pkg-config \ gfortran \ + netcdf-bin \ + libnetcdf-dev \ && rm -rf /var/lib/apt/lists/* - -#copy config file (on all builds) +#copy config file COPY config_emhass.yaml /app/ -# Make sure data directory exists +#make sure data directory exists RUN mkdir -p /app/data/ #------------------------- -#EMHASS-ADDON Default (this has no emhass packadge) +##EMHASS-Add-on default (this has no emhass package) FROM base as addon LABEL \ @@ -67,23 +93,21 @@ LABEL \ io.hass.type="addon" \ io.hass.arch="aarch64|amd64|armhf|armv7" -ENTRYPOINT [ "python3", "-m", "emhass.web_server","--addon", "True", "--url", "http://supervisor/core/api"] - #----------- -#EMHASS-ADD-ON Testing with pip emhass (closest testing reference) +#EMHASS-ADD-ON testing with pip emhass (EMHASS-Add-on testing reference) FROM addon as addon-pip #set build arg for pip version ARG build_pip_version="" -RUN pip3 install --no-cache-dir --break-system-packages --upgrade --upgrade-strategy=only-if-needed -U emhass${build_pip_version} +RUN pip3 install --no-cache-dir --break-system-packages --upgrade --force-reinstall --no-deps --upgrade-strategy=only-if-needed -U emhass${build_pip_version} COPY options.json /app/ ENTRYPOINT [ "python3", "-m", "emhass.web_server","--addon", "True", "--no_response", "True"] #----------- -#EMHASS-ADD-ON Testing from local files +#EMHASS-Add-on testing from local files FROM addon as addon-local -COPY src/emhass/ /app/src/emhass/ +COPY src/emhass/ /app/src/emhass/ COPY src/emhass/templates/ /app/src/emhass/templates/ COPY src/emhass/static/ /app/src/emhass/static/ COPY src/emhass/static/img/ /app/src/emhass/static/img/ @@ -93,12 +117,12 @@ COPY options.json /app/ COPY README.md /app/ COPY setup.py /app/ #compile EMHASS locally -RUN python3 -m pip install --no-cache-dir --break-system-packages -U . +RUN pip3 install --no-cache-dir --break-system-packages --no-deps --force-reinstall . ENTRYPOINT [ "python3", "-m", "emhass.web_server","--addon", "True" , "--no_response", "True"] #----------- -#EMHASS-ADD-ON testing with git +#EMHASS-Add-on testing with git FROM addon as addon-git ARG build_repo=https://github.com/davidusb-geek/emhass.git ARG build_branch=master @@ -116,31 +140,28 @@ RUN cp /tmp/emhass/README.md /app/ #add options.json, this otherwise would be generated via HA RUN cp /tmp/emhass/options.json /app/ WORKDIR /app -RUN python3 -m pip install --no-cache-dir --break-system-packages -U . +RUN pip3 install --no-cache-dir --break-system-packages --no-deps --force-reinstall . ENTRYPOINT [ "python3", "-m", "emhass.web_server","--addon", "True" , "--no_response", "True"] #------------------------- -#EMHASS STANDALONE +#EMHASS stanalone FROM base as standalone -RUN pip3 install --extra-index-url=https://www.piwheels.org/simple --no-cache-dir --break-system-packages -U flask waitress plotly - -COPY src/emhass/ /app/src/emhass/ +COPY src/emhass/ /app/src/emhass/ COPY src/emhass/templates/ /app/src/emhass/templates/ COPY src/emhass/static/ /app/src/emhass/static/ COPY src/emhass/static/img/ /app/src/emhass/static/img/ COPY data/opt_res_latest.csv /app/data/ COPY README.md /app/ COPY setup.py /app/ -#secrets file will need to be copied manually at docker run +#secrets file can be copied manually at docker run -# # set env variables +#set python env variables ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 #build EMHASS -# RUN python3 setup.py install -RUN python3 -m pip install --no-cache-dir --break-system-packages -U . +RUN pip3 install --no-cache-dir --break-system-packages --no-deps --force-reinstall . ENTRYPOINT [ "python3", "-m", "emhass.web_server"] #------------------------- diff --git a/README.md b/README.md index 6cfa9f5a..79773243 100644 --- a/README.md +++ b/README.md @@ -97,16 +97,16 @@ These architectures are supported: `amd64`, `armv7`, `armhf` and `aarch64`. ### Method 2) Using Docker in standalone mode You can also install EMHASS using docker. This can be in the same machine as Home Assistant (if using the supervised install method) or in a different distant machine. To install first pull the latest image from docker hub: -``` +```bash docker pull davidusb/emhass-docker-standalone ``` You can also build your image locally. For this clone this repository, setup your `config_emhass.yaml` file and use the provided make file with this command: -``` +```bash make -f deploy_docker.mk clean_deploy ``` Then load the image in the .tar file: -``` +```bash docker load -i .tar ``` Finally check your image tag with `docker images` and launch the docker itself: @@ -124,17 +124,17 @@ docker run -it --restart always -p 5000:5000 -e "LOCAL_COSTFUN=profit" -v $(pwd) With this method it is recommended to install on a virtual environment. For this you will need `virtualenv`, install it using: -``` +```bash sudo apt install python3-virtualenv ``` Then create and activate the virtual environment: -``` +```bash virtualenv -p /usr/bin/python3 emhassenv cd emhassenv source bin/activate ``` Install using the distribution files: -``` +```bash python3 -m pip install emhass ``` Clone this repository to obtain the example configuration files. @@ -145,7 +145,7 @@ We will suppose that this repository is cloned to: This will be the root path containing the yaml configuration files (`config_emhass.yaml` and `secrets_emhass.yaml`) and the different needed folders (a `data` folder to store the optimizations results and a `scripts` folder containing the bash scripts described further below). To upgrade the installation in the future just use: -``` +```bash python3 -m pip install --upgrade emhass ``` @@ -181,7 +181,7 @@ The available arguments are: - `--version`: Show the current version of EMHASS. For example, the following line command can be used to perform a day-ahead optimization task: -``` +```bash emhass --action 'dayahead-optim' --config '/home/user/emhass/config_emhass.yaml' --costfun 'profit' ``` Before running any valuable command you need to modify the `config_emhass.yaml` and `secrets_emhass.yaml` files. These files should contain the information adapted to your own system. To do this take a look at the special section for this in the [documentation](https://emhass.readthedocs.io/en/latest/config.html). @@ -195,7 +195,7 @@ Then additional optimization strategies were developed, that can be used in comb ### Dayahead Optimization - Method 1) Add-on and docker standalone In `configuration.yaml`: -``` +```yaml shell_command: dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/dayahead-optim" publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/publish-data" @@ -203,25 +203,25 @@ shell_command: ### Dayahead Optimization - Method 2) Legacy method using a Python virtual environment In `configuration.yaml`: -``` +```yaml shell_command: dayahead_optim: /home/user/emhass/scripts/dayahead_optim.sh publish_data: /home/user/emhass/scripts/publish_data.sh ``` Create the file `dayahead_optim.sh` with the following content: -``` +```bash #!/bin/bash . /home/user/emhassenv/bin/activate emhass --action 'dayahead-optim' --config '/home/user/emhass/config_emhass.yaml' ``` And the file `publish_data.sh` with the following content: -``` +```bash #!/bin/bash . /home/user/emhassenv/bin/activate emhass --action 'publish-data' --config '/home/user/emhass/config_emhass.yaml' ``` Then specify user rights and make the files executables: -``` +```bash sudo chmod -R 755 /home/user/emhass/scripts/dayahead_optim.sh sudo chmod -R 755 /home/user/emhass/scripts/publish_data.sh sudo chmod +x /home/user/emhass/scripts/dayahead_optim.sh @@ -230,7 +230,7 @@ sudo chmod +x /home/user/emhass/scripts/publish_data.sh ### Common for any installation method In `automations.yaml`: -``` +```yaml - alias: EMHASS day-ahead optimization trigger: platform: time @@ -247,7 +247,7 @@ In `automations.yaml`: In these automations the day-ahead optimization is performed everyday at 5:30am and the data is published every 5 minutes. The final action will be to link a sensor value in Home Assistant to control the switch of a desired controllable load. For example imagine that I want to control my water heater and that the `publish-data` action is publishing the optimized value of a deferrable load that I want to be linked to my water heater desired behavior. In this case we could use an automation like this one below to control the desired real switch: -``` +```yaml automation: - alias: Water Heater Optimized ON trigger: @@ -262,7 +262,7 @@ automation: entity_id: switch.water_heater_switch ``` A second automation should be used to turn off the switch: -``` +```yaml automation: - alias: Water Heater Optimized OFF trigger: @@ -284,7 +284,7 @@ The `publish-data` command will push to Home Assistant the optimization results The `publish-data` command will also publish PV and load forecast data on sensors `p_pv_forecast` and `p_load_forecast`. If using a battery, then the battery optimized power and the SOC will be published on sensors `p_batt_forecast` and `soc_batt_forecast`. On these sensors the future values are passed as nested attributes. It is possible to provide custm sensor names for all the data exported by the `publish-data` command. For this, when using the `publish-data` endpoint just add some runtime parameters as dictionaries like this: -``` +```yaml shell_command: publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"custom_load_forecast_id\": {\"entity_id\": \"sensor.p_load_forecast\", \"unit_of_measurement\": \"W\", \"friendly_name\": \"Load Power Forecast\"}}' http://localhost:5000/action/publish-data" ``` @@ -292,7 +292,7 @@ shell_command: These keys are available to modify: `custom_pv_forecast_id`, `custom_load_forecast_id`, `custom_batt_forecast_id`, `custom_batt_soc_forecast_id`, `custom_grid_forecast_id`, `custom_cost_fun_id`, `custom_deferrable_forecast_id`, `custom_unit_load_cost_id` and `custom_unit_prod_price_id`. If you provide the `custom_deferrable_forecast_id` then the passed data should be a list of dictionaries, like this: -``` +```yaml shell_command: publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"custom_deferrable_forecast_id\": [{\"entity_id\": \"sensor.p_deferrable0\",\"unit_of_measurement\": \"W\", \"friendly_name\": \"Deferrable Load 0\"},{\"entity_id\": \"sensor.p_deferrable1\",\"unit_of_measurement\": \"W\", \"friendly_name\": \"Deferrable Load 1\"}]}' http://localhost:5000/action/publish-data" ``` @@ -342,11 +342,11 @@ The valid values to pass for both forecast data and MPC related data are explain It is possible to provide EMHASS with your own forecast data. For this just add the data as list of values to a data dictionary during the call to `emhass` using the `runtimeparams` option. For example if using the add-on or the standalone docker installation you can pass this data as list of values to the data dictionary during the `curl` POST: -``` +```bash curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93, 1164.33, 1046.68, 1559.1, 2091.26, 1556.76, 1166.73, 1516.63, 1391.13, 1720.13, 820.75, 804.41, 251.63, 79.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}' http://localhost:5000/action/dayahead-optim ``` Or if using the legacy method using a Python virtual environment: -``` +```bash emhass --action 'dayahead-optim' --config '/home/user/emhass/config_emhass.yaml' --runtimeparams '{"pv_power_forecast":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93, 1164.33, 1046.68, 1559.1, 2091.26, 1556.76, 1166.73, 1516.63, 1391.13, 1720.13, 820.75, 804.41, 251.63, 79.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}' ``` @@ -419,10 +419,16 @@ When applying this controller, the following `runtimeparams` should be defined: A correct call for a MPC optimization should look like: +```bash +curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6}' http://192.168.3.159:5000/action/naive-mpc-optim ``` -curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6,"def_total_hours":[1,3],"def_start_timestep":[0,3],"def_end_timestep":[0,6],}' http://localhost:5000/action/naive-mpc-optim +*Example with :`def_total_hours`, `def_start_timestep`, `def_end_timestep`.* +```bash +curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6,"def_total_hours":[1,3],"def_start_timestep":[0,3],"def_end_timestep":[0,6]}' http://localhost:5000/action/naive-mpc-optim ``` + + ## A machine learning forecaster Starting in v0.4.0 a new machine learning forecaster class was introduced. diff --git a/config_emhass.yaml b/config_emhass.yaml index 1b29cc19..07832780 100644 --- a/config_emhass.yaml +++ b/config_emhass.yaml @@ -51,8 +51,8 @@ optim_conf: prod_price_forecast_method: 'constant' # options are 'constant' for constant fixed value or 'csv' to load custom price forecast from a CSV file prod_sell_price: 0.065 # power production selling price in €/kWh (only needed if prod_price_forecast_method='constant') set_total_pv_sell: False # consider that all PV power is injected to the grid (self-consumption with total sell) - lp_solver: 'default' # set the name of the linear programming solver that will be used - lp_solver_path: 'empty' # set the path to the LP solver + lp_solver: 'default' # set the name of the linear programming solver that will be used. Options are 'PULP_CBC_CMD', 'GLPK_CMD' and 'COIN_CMD'. + lp_solver_path: 'empty' # set the path to the LP solver, COIN_CMD default is /usr/bin/cbc set_nocharge_from_grid: False # avoid battery charging from the grid set_nodischarge_to_grid: True # avoid battery discharging to the grid set_battery_dynamic: False # add a constraint to limit the dynamic of the battery power in power per time unit diff --git a/docs/develop.md b/docs/develop.md index 8aff294f..287a5dde 100644 --- a/docs/develop.md +++ b/docs/develop.md @@ -21,10 +21,15 @@ This method works well with standalone mode. _confirm terminal is in the root `emhass` directory before starting_ +**Install requirements** +```bash +python3 -m pip install -r requirements.txt #if arm try setting --extra-index-url=https://www.piwheels.org/simple +``` + **Create a developer environment:** ```bash -python -m venv .venv +python3 -m venv .venv ``` **Activate the environment:** @@ -46,7 +51,7 @@ An IDE like VSCode should automatically catch that a new virtual env was created **Install the _emhass_ package in editable mode:** ```bash -python -m pip install -e . +python3 -m pip install -e . ``` **Set paths with environment variables:** @@ -216,6 +221,16 @@ For those who wish to mount/sync the local `data` folder with the data folder fr docker run ... -v $(pwd)/data/:/app/data ... ``` +#### Issue with TARGETARCH +If your docker build fails with an error related to `TARGETARCH`. It may be best to add your devices artitecture this in manually: + +Example with armhf architecture +```bash +docker build ... --build-arg TARGETARCH=armhf --build-arg os_version=raspbian ... +``` +*For `armhf` only, create a build-arg for `os_version=raspbian`* + + #### Delete built Docker image We can delete the Docker image and container via: @@ -266,7 +281,70 @@ docker build -t emhass/docker --build-arg build_version=addon-local . docker run -it -p 5000:5000 --name emhass-container -e EMHASS_KEY -e EMHASS_URL -e TIME_ZONE -e LAT -e LON -e ALT emhass/docker ``` +### Example Docker testing pipeline +If you are wishing to test your changes compatibility, check out this example as a template: + +*Linux:* +*Assuming docker and git installed* +```bash +#setup environment variables for test +export repo=https://github.com/davidusb-geek/emhass.git +export branch=master +#Ex. HAURL=https://localhost:8123/ +export HAURL=HOMEASSISTANTURLHERE +export HAKEY=HOMEASSISTANTKEYHERE + +git clone $repo +cd emhass +git checkout $branch +``` +```bash +#testing addon (build and run) +docker build -t emhass/docker --build-arg build_version=addon-local . +docker run --rm -it -p 5000:5000 --name emhass-container -v $(pwd)/options.json:/app/options.json -e LAT="45.83" -e LON="6.86" -e ALT="4807.8" -e TIME_ZONE="Europe/Paris" emhass/docker --url $HAURL --key $HAKEY +``` +```bash +#run actions on a separate terminal +curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6}' http://localhost:5000/action/naive-mpc-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/perfect-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/dayahead-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-fit +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-predict +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-tune +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/publish-data +``` + +```bash +#testing standalone (build and run) +docker build -t emhass/docker --build-arg build_version=standalone . +#make secrets_emhass +cat <> secrets_emhass.yaml +hass_url: $HAURL +long_lived_token: $HAKEY +time_zone: Europe/Paris +lat: 45.83 +lon: 6.86 +alt: 4807.8 +EOT +docker run --rm -it -p 5000:5000 --name emhass-container -v $(pwd)/config_emhass.yaml:/app/config_emhass.yaml -v $(pwd)/secrets_emhass.yaml:/app/secrets_emhass.yaml emhass/docker +``` +```bash +#run actions on a separate terminal +curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6}' http://localhost:5000/action/naive-mpc-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/perfect-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/dayahead-optim +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-fit +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-predict +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/forecast-model-tune +curl -i -H 'Content-Type:application/json' -X POST http://localhost:5000/action/publish-data +``` + +User may wish to re-test with tweaked parameters such as `lp_solver` and `weather_forecast_method`, in `config_emhass.yaml` *(standalone)* or `options.json` *(addon)*, to broaden the testing scope. +*see [EMHASS & EMHASS-Add-on differences](https://emhass.readthedocs.io/en/latest/differences.html) for more information on how these config_emhass & options files differ* + +*Note: may need to set `--build-arg TARGETARCH=YOUR-ARCH` in docker build* + ## Step 3 - Pull request Once developed, commit your code, and push to your fork. -Then submit a pull request with your fork to the [davidusb-geek/emhass@master](https://github.com/davidusb-geek/emhass) repository. +Then submit a pull request with your fork to the [davidusb-geek/emhass@master](https://github.com/davidusb-geek/emhass) repository. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1a84a681..22b18dfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ -wheel -numpy<=1.26.0 -scipy<=1.11.3 +numpy==1.26.4 +scipy==1.12.0 pandas<=2.0.3 pvlib>=0.10.2 protobuf>=3.0.0 @@ -11,4 +10,8 @@ h5py==3.10.0 pulp>=2.4 pyyaml>=5.4.1 tables<=3.9.1 -skforecast==0.11.0 \ No newline at end of file +skforecast==0.11.0 +# web server packages +flask>=2.0.3 +waitress>=2.1.1 +plotly>=5.6.0 \ No newline at end of file diff --git a/requirements_addon.txt b/requirements_addon.txt deleted file mode 100644 index 7c690fe6..00000000 --- a/requirements_addon.txt +++ /dev/null @@ -1,18 +0,0 @@ -numpy<=1.26.0 -scipy<=1.11.3 -pandas<=2.0.3 -pvlib>=0.10.2 -protobuf>=3.0.0 -pytz>=2021.1 -requests>=2.25.1 -beautifulsoup4>=4.9.3 -h5py==3.10.0 -pulp>=2.4 -pyyaml>=5.4.1 -tables<=3.9.1 -skforecast==0.11.0 -flask>=2.0.3 -waitress>=2.1.1 -plotly>=5.6.0 -emhass==0.7.7 -#git+https://github.com/davidusb-geek/emhass \ No newline at end of file diff --git a/requirements_webserver.txt b/requirements_webserver.txt deleted file mode 100644 index d5dfd368..00000000 --- a/requirements_webserver.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements.txt -flask>=2.0.3 -waitress>=2.1.1 -plotly>=5.6.0 diff --git a/setup.py b/setup.py index 31b944a8..21ed7a1e 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,8 @@ python_requires='>=3.9, <3.12', install_requires=[ 'wheel', - 'numpy<=1.26', - 'scipy<=1.11.3', + 'numpy==1.26.4', + 'scipy==1.12.0', 'pandas<=2.0.3', 'pvlib>=0.10.2', 'protobuf>=3.0.0', diff --git a/src/emhass/optimization.py b/src/emhass/optimization.py index 0172463d..83306adf 100644 --- a/src/emhass/optimization.py +++ b/src/emhass/optimization.py @@ -85,6 +85,9 @@ def __init__(self, retrieve_hass_conf: dict, optim_conf: dict, plant_conf: dict, self.lp_solver_path = 'empty' if self.lp_solver != 'COIN_CMD' and self.lp_solver_path != 'empty': self.logger.error("Use COIN_CMD solver name if you want to set a path for the LP solver") + if self.lp_solver == 'COIN_CMD' and self.lp_solver_path == 'empty': #if COIN_CMD but lp_solver_path is empty + self.logger.warning("lp_solver=COIN_CMD but lp_solver_path=empty, attempting to use lp_solver_path=/usr/bin/cbc") + self.lp_solver_path = '/usr/bin/cbc' def perform_optimization(self, data_opt: pd.DataFrame, P_PV: np.array, P_load: np.array, unit_load_cost: np.array, unit_prod_price: np.array, diff --git a/src/emhass/web_server.py b/src/emhass/web_server.py index 425d9995..e78413c9 100644 --- a/src/emhass/web_server.py +++ b/src/emhass/web_server.py @@ -9,7 +9,6 @@ from pathlib import Path import os, json, argparse, pickle, yaml, logging, re from distutils.util import strtobool -import pandas as pd from emhass.command_line import set_input_data_dict from emhass.command_line import perfect_forecast_optim, dayahead_forecast_optim, naive_mpc_optim