diff --git a/CHANGELOG.md b/CHANGELOG.md index 663ee8723..f18026b8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,64 @@ Changelog ========= -2.8.2 (2017-10-21) +(unreleased) ------------ +**New** +- Add sqlite3 dependency for manual database manupilation. [TheYOSH] +- Added webcam update timeout. Webcams are now only updated once a + minute. Should reduce the load and makes the enginge a bit faster. + [TheYOSH] +- Add extra dimming timing for small changes. [TheYOSH] +- Add PI power user to total power usage. [TheYOSH] +- Added missing translation fields. [TheYOSH] +- Add dimmer settings processing and saving to config file. [TheYOSH] +- Add dimmer support part 1. [TheYOSH] + +**Fixes** +- Fix empty graphs. [TheYOSH] +- Fixed missing translation in home dashboard. [#33](https://github.com/theyosh/TerrariumPI/issues/33). [TheYOSH] +- Fix timer issues [#34](https://github.com/theyosh/TerrariumPI/issues/34). [TheYOSH] +- Fix graphing empty graphs and smaller dimmer knob. [TheYOSH] +- Fixed total duration calculation in total power usage. [TheYOSH] +- Fix switch loading without dimmer settings. [TheYOSH] +- Fix switch toggle to support dimmers. [TheYOSH] +- Fix switch toggle to support dimmers. [TheYOSH] +- Fix devision by zero. [TheYOSH] +- Fix SQL duplicate key error. [TheYOSH] + +**Updates** +- Update version number. [TheYOSH] +- Update engine loop to make sure at least one run per minute is + possible. Should prevent spicky graphs. [TheYOSH] +- Update environment dashboard modus names [#34](https://github.com/theyosh/TerrariumPI/issues/34). [TheYOSH] +- Update translations. [TheYOSH] +- Update readme. [TheYOSH] +- Update readme. [TheYOSH] +- Update power duration calculation. [TheYOSH] +- Updated installation so it works faster and handles upgrades better. + [TheYOSH] +- Updated data logging and graphing. Reduced load on the client side. + [TheYOSH] +- Update gentelella. [TheYOSH] +- Updated dimmer settings in switch settings page. [TheYOSH] +- Updated supported hardware page. [TheYOSH] + +**Other** +- Collector code clean up and better data storage and retrieval. + [TheYOSH] +- Merge branch 'master' into feature/power_dimmer. [TheYOSH] +- Trying to add more stability for dimming hardware. [TheYOSH] +- Remove unused settings for non dimmer switches. [TheYOSH] +- Make PI user restart PiGPIOd process. [TheYOSH] +- Enable pigpiod service at reboot. [TheYOSH] +- Better on and off detection for dimmers. [TheYOSH] +- Merge branch 'master' into feature/power_dimmer. [TheYOSH] + + +2.8.2 (2017-10-21) +------------------ + **New** - Add sync command. [TheYOSH] @@ -15,6 +70,7 @@ Changelog - Fix HTML code in Dutch translation. Was broke in usage page. [TheYOSH] **Updates** +- Update changelog. [TheYOSH] - Update version number. [TheYOSH] - Update README.md. [TheYOSH] - Update README.md. [TheYOSH] @@ -453,3 +509,5 @@ Changelog - Typo. [TheYOSH] - Moved door status to own page. [TheYOSH] - Use system default pip. [TheYOSH] + + diff --git a/README.md b/README.md index 0ef430117..e9e4a2495 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,33 @@ -# TerrariumPI 2.8.2 -Software for cheap home automation of your reptile terrarium or any other enclosed environment. With this software you are able to control an enclosed environment so that the temperature and humidity is of a constant value. This is done by using temperature and humidity sensors and realy switches to activate external devices. +# TerrariumPI 3.0.0 +Software for cheap home automation of your reptile terrarium or any other enclosed environment. With this software you are able to control for example a terrarium so that the temperature and humidity is of a constant value. Controlling the temperature can be done with heat lights, external heating or cooling system. As long as there is one temperature sensor available the software is able to keep a constant temperature. -It has support for lights, sprayer, heater and cooler equipment. The amount of devices that can be controlled depends on the used relay boards. +For humidity control there is support for a spraying system. The sprayer can be configured to spray for an X amount of seconds and there is a minumal period between two spray actions. Use at least one humitidy sensors to get a constant humidity value. + +The software is that flexible that there is no limit in amount of sensors, relay boards or door sensors. The usage can be endless. Think off: -- Terrarium +- Terrarium (wet of dry) - Aquarium +- Tanks with animals or plants - Growhouse And all this is controlled with a nice webinterface based on [Gentelella a Bootstrap 3 template](https://github.com/puikinsh/gentelella/). ## Features - Controlling electronic devices like lights, sprayers, heating and cooling equipment -- Reading out temperature and humidity sensors -- Open door detection (sprayer will not spray when a door is open) +- Support for dimming electronic devices + - Manual dimming through web interface + - Predefined on and off durations + - Predefined on and off dimming percentages +- Reading out multiple temperature and humidity sensors - Support for native Raspberry Pi cam out of the box - Support for USB and remote webcams +- Open door detection (sprayer will not spray when a door is open) - Total power and water usage for costs calculation - Lights control based on sun rise and sun set or timers -- Rain control based on measured humidity +- Rain control based on humidity sensors and timers - Heater control based on temperature sensors or timers - Cooling control based on temperature sensors or timers -- Weather forecast from external source +- Weather forecast from external source for lighting schema - Temperatures in Celsius or Fahrenheit - Alarm detections @@ -61,18 +68,21 @@ This updating is based on that the software is installed with the steps in the I `cd TerrariumPI` 3. Update the new code with git `git pull` +4. Re-run the installation script in order to update software dependencies + `sudo ./install.sh` 4. Restart TerrariumPI according to: https://github.com/theyosh/TerrariumPI/wiki/FAQ#how-to-restart-terrariumpi Now clear your browser cache and reload the webinterface. A brand new version should be running. ## Hardware -This software requires a Raspberry Pi and some extra hardware in order to run and work. The bare minimun is +This software requires a Raspberry Pi and some extra hardware in order to run and work. The bare minimun and tested hardware is - Raspberry PI - Pi 2 - Pi 3 - Power relay board - USB versions (Serial and Bitbang) - GPIO versions + - PWM Dimmer versions - Temperature/humdity sensors DHT11, DHT22, AM2303, DS1820, HIH4000, etc through - OWFS - GPIO diff --git a/defaults.cfg b/defaults.cfg index 96c9b27ec..d5031bd90 100644 --- a/defaults.cfg +++ b/defaults.cfg @@ -1,7 +1,7 @@ [terrariumpi] host = :: port = 8090 -version = 2.8.2 +version = 3.0.0 title = TerrariumPI %(version)s power_usage = 5 owfs_port = 4304 diff --git a/install.sh b/install.sh index 2bfbe5c5c..7dcc088b3 100755 --- a/install.sh +++ b/install.sh @@ -15,14 +15,7 @@ apt-get -y autoremove # Install required packages to get the terrarium software running aptitude -y update aptitude -y safe-upgrade -aptitude -y install libftdi1 screen python-imaging python-dateutil python-ow python-rpi.gpio python-psutil git subversion watchdog build-essential python-dev python-picamera python-opencv python-pip - -if [ `ls -l gentelella | grep -v ^t | wc -l` -eq 0 ]; then - # Manual get Gentelella bootstrap 3 template - git clone https://github.com/puikinsh/gentelella.git gentelella -fi - -cd .. +aptitude -y install libftdi1 screen python-imaging python-dateutil python-ow python-rpi.gpio python-psutil git subversion watchdog build-essential python-dev python-picamera python-opencv python-pip python-pigpio i2c-tools owfs ow-shell sqlite3 # Basic config: raspi-config @@ -30,23 +23,36 @@ raspi-config # Set the timezone dpkg-reconfigure tzdata +if [ `ls -l gentelella | grep -v ^t | wc -l` -eq 0 ]; then + # Manual get Gentelella bootstrap 3 template + git clone https://github.com/puikinsh/gentelella.git gentelella +fi + +cd gentelella +git pull +cd "${BASEDIR}/.." + # Install multiple python modules -pip install --upgrade gevent untangle uptime bottle bottle_websocket +pip install --upgrade gevent untangle uptime bottle bottle_websocket pylibftdi # Install https://pypi.python.org/pypi/pylibftdi # Docu https://pylibftdi.readthedocs.io/ -pip install --upgrade pylibftdi # Make sure that the normal Pi user can read and write to the usb driver echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="dialout", MODE="0660"' > /etc/udev/rules.d/99-libftdi.rules echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", GROUP="dialout", MODE="0660"' >> /etc/udev/rules.d/99-libftdi.rules # Install 1 Wire stuff -aptitude -y install i2c-tools owfs ow-shell sed -i.bak 's/^server: FAKE = DS18S20,DS2405/#server: FAKE = DS18S20,DS2405/' /etc/owfs.conf -echo "server: device=/dev/i2c-1" >> /etc/owfs.conf + +if [ `grep -ic "server: device=/dev/i2c-1" /etc/owfs.conf` -eq 0 ]; then + echo "server: device=/dev/i2c-1" >> /etc/owfs.conf +fi sed -i.bak 's/^blacklist i2c-bcm2708/#blacklist i2c-bcm2708/' /etc/modprobe.d/raspi-blacklist.conf -echo "i2c-dev" >> /etc/modules +if [ `grep -ic "i2c-dev" /etc/modules` -eq 0 ]; then + echo "i2c-dev" >> /etc/modules +fi + modprobe i2c-bcm2708 modprobe i2c-dev @@ -58,6 +64,7 @@ fi cd Adafruit_Python_DHT git pull sudo python setup.py install +cd "${BASEDIR}/.." # Remove unneeded OWS services update-rc.d -f owftpd remove @@ -71,7 +78,10 @@ fi groupadd gpio 2> /dev/null usermod -a -G gpio pi 2> /dev/null -sync +# Make sure pigpiod is started at boot, and that user PI can restart it with sudo command +echo "pi ALL=(ALL) NOPASSWD: /usr/sbin/service pigpiod restart" > /etc/sudoers.d/terrariumpi +systemctl enable pigpiod # We are done! +sync echo "Instaltion is done. Please reboot once to get the I2C and Adafruit DHT libary working correctly" diff --git a/locales/en_US/LC_MESSAGES/terrariumpi.mo b/locales/en_US/LC_MESSAGES/terrariumpi.mo index c1ec932d5..914b9c839 100644 Binary files a/locales/en_US/LC_MESSAGES/terrariumpi.mo and b/locales/en_US/LC_MESSAGES/terrariumpi.mo differ diff --git a/locales/en_US/LC_MESSAGES/terrariumpi.po b/locales/en_US/LC_MESSAGES/terrariumpi.po index 879275814..a47b7e986 100644 --- a/locales/en_US/LC_MESSAGES/terrariumpi.po +++ b/locales/en_US/LC_MESSAGES/terrariumpi.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: TerrariumPI 0.1\n" -"POT-Creation-Date: 2017-09-27 20:19+CEST\n" -"PO-Revision-Date: 2017-09-27 20:19+0200\n" +"POT-Creation-Date: 2017-10-24 23:08+CEST\n" +"PO-Revision-Date: 2017-10-24 23:08+0200\n" "Last-Translator: Joshua (TheYOSH) Rubingh \n" "Language-Team: \n" "Language: en_US\n" @@ -80,55 +80,75 @@ msgstr "Holds the switch power usage in Watt when switched on." msgid "Holds the switch water flow in liters per minute when switched on" msgstr "Holds the switch water flow in liters per minute when switched on" +#: terrariumTranslations.py:43 +msgid "Holds the amount of seconds for the duration in which the dimmer changes to the new value." +msgstr "Holds the amount of seconds for the duration in which the dimmer changes to the new value." + +#: terrariumTranslations.py:44 +msgid "Holds the amount of seconds for the duration in which it increases the power." +msgstr "Holds the amount of seconds for the duration in which it increases the power." + +#: terrariumTranslations.py:45 +msgid "Holds the amount in percentage to go to when switched on." +msgstr "Holds the amount in percentage to go to when switched on." + #: terrariumTranslations.py:46 +msgid "Holds the amount of seconds for the duration in which it decresses the power." +msgstr "Holds the amount of seconds for the duration in which it decresses the power." + +#: terrariumTranslations.py:47 +msgid "Holds the amount in percentage to go to when switched off." +msgstr "Holds the amount in percentage to go to when switched off." + +#: terrariumTranslations.py:51 msgid "Holds the door hardware type. Supported hardware types are: %s." msgstr "Holds the door hardware type. Supported hardware types are: %s." -#: terrariumTranslations.py:47 +#: terrariumTranslations.py:52 msgid "Holds the door address. When using GPIO use Physical GPIO pin numbering as address." msgstr "Holds the door address. When using GPIO use Physical GPIO pin numbering as address." -#: terrariumTranslations.py:48 +#: terrariumTranslations.py:53 msgid "Holds the door name." msgstr "Holds the door name." -#: terrariumTranslations.py:52 +#: terrariumTranslations.py:57 msgid "Holds the webcam location source. Supported sources are: %s" msgstr "Holds the webcam location source. Supported sources are: %s" -#: terrariumTranslations.py:53 +#: terrariumTranslations.py:58 msgid "Holds the webcam name." msgstr "Holds the webcam name." -#: terrariumTranslations.py:54 +#: terrariumTranslations.py:59 msgid "Holds the webcam rotation of the image." msgstr "Holds the webcam rotation of the image." -#: terrariumTranslations.py:55 +#: terrariumTranslations.py:60 msgid "Shows the webcam preview image." msgstr "Shows the webcam preview image." -#: terrariumTranslations.py:59 +#: terrariumTranslations.py:64 msgid "Enable or disable the light system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the lighting system." msgstr "Enable or disable the light system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the lighting system." -#: terrariumTranslations.py:60 +#: terrariumTranslations.py:65 msgid "Select the mode on which the lights will be put on and off. Select '%s' to use the sun rise and sun set at your location. This will make the amount of lighting variable to the actual amount of daylight. When selecting '%s', the light will put on and off at selected times." msgstr "Select the mode on which the lights will be put on and off. Select '%s' to use the sun rise and sun set at your location. This will make the amount of lighting variable to the actual amount of daylight. When selecting '%s', the light will put on and off at selected times." -#: terrariumTranslations.py:60 terrariumTranslations.py:61 -#: terrariumTranslations.py:62 terrariumTranslations.py:80 -#: terrariumTranslations.py:81 terrariumTranslations.py:82 -#: terrariumTranslations.py:89 terrariumTranslations.py:90 -#: terrariumTranslations.py:91 views/inc/usage_environment.tpl:33 +#: terrariumTranslations.py:65 terrariumTranslations.py:66 +#: terrariumTranslations.py:67 terrariumTranslations.py:85 +#: terrariumTranslations.py:86 terrariumTranslations.py:87 +#: terrariumTranslations.py:94 terrariumTranslations.py:95 +#: terrariumTranslations.py:96 views/inc/usage_environment.tpl:33 #: views/inc/usage_environment.tpl:229 views/inc/usage_environment.tpl:323 #: views/system_environment.tpl:46 views/system_environment.tpl:202 #: views/system_environment.tpl:277 msgid "Timer" msgstr "Timer" -#: terrariumTranslations.py:60 terrariumTranslations.py:65 -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:65 terrariumTranslations.py:70 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/menu.tpl:32 views/inc/usage_environment.tpl:34 #: views/inc/usage_environment.tpl:230 views/inc/usage_environment.tpl:324 #: views/inc/usage_weather.tpl:13 views/inc/usage_weather.tpl:82 @@ -139,71 +159,71 @@ msgstr "Timer" msgid "Weather" msgstr "Weather" -#: terrariumTranslations.py:61 +#: terrariumTranslations.py:66 msgid "Enter the time when the light should be put on. Only available when running in '%s' mode." msgstr "Enter the time when the light should be put on. Only available when running in '%s' mode." -#: terrariumTranslations.py:62 +#: terrariumTranslations.py:67 msgid "Enter the time when the lights should be put off. Only available when running in '%s' mode." msgstr "Enter the time when the lights should be put off. Only available when running in '%s' mode." -#: terrariumTranslations.py:63 +#: terrariumTranslations.py:68 msgid "Enter the maximum amount of lights time in hours. When the time between on and off is more then this maximum, the on and off time will be shifted more to each other." msgstr "Enter the maximum amount of lights time in hours. When the time between on and off is more then this maximum, the on and off time will be shifted more to each other." -#: terrariumTranslations.py:64 +#: terrariumTranslations.py:69 msgid "Enter the minimum amount of lights time in hours. When the time between on and off is less then this amount of hours, the on and off time will be widened until the minimum amount of hours entered here." msgstr "Enter the minimum amount of lights time in hours. When the time between on and off is less then this amount of hours, the on and off time will be widened until the minimum amount of hours entered here." -#: terrariumTranslations.py:65 +#: terrariumTranslations.py:70 msgid "Enter the amount of hours that the lights should shift. Is only needed when running in the '%s' mode. Enter a positive number for adding hours to the start time. Use negative numbers for subtracting from the start time." msgstr "Enter the amount of hours that the lights should shift. Is only needed when running in the '%s' mode. Enter a positive number for adding hours to the start time. Use negative numbers for subtracting from the start time." -#: terrariumTranslations.py:66 +#: terrariumTranslations.py:71 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the lights. Select all needed switches below." msgstr "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the lights. Select all needed switches below." -#: terrariumTranslations.py:69 +#: terrariumTranslations.py:74 msgid "Enable or disable the sprayer system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the sprayer system." msgstr "Enable or disable the sprayer system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the sprayer system." -#: terrariumTranslations.py:70 +#: terrariumTranslations.py:75 msgid "Enable spraying when the lights are off. This can cause water flow when there is not enough heat to vaporize the water." msgstr "Enable spraying when the lights are off. This can cause water flow when there is not enough heat to vaporize the water." -#: terrariumTranslations.py:71 +#: terrariumTranslations.py:76 msgid "Select the operating mode. For now only sensor mode is available." msgstr "Select the operating mode. For now only sensor mode is available." -#: terrariumTranslations.py:72 +#: terrariumTranslations.py:77 msgid "How much time must there be between two spray actions and after start up. Enter the amount of seconds in which the humidity can settle." msgstr "How much time must there be between two spray actions and after start up. Enter the amount of seconds in which the humidity can settle." -#: terrariumTranslations.py:73 +#: terrariumTranslations.py:78 msgid "How long is the system spraying. Enter the amount of seconds that the system is on when the humidity is too low." msgstr "How long is the system spraying. Enter the amount of seconds that the system is on when the humidity is too low." -#: terrariumTranslations.py:74 +#: terrariumTranslations.py:79 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the sprayer. Select all needed switches below." msgstr "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the sprayer. Select all needed switches below." -#: terrariumTranslations.py:75 +#: terrariumTranslations.py:80 msgid "Select the humidity sensors that are used to control the humidity. When selecting multiple sensors, the average is calculated to determine the final humidity." msgstr "Select the humidity sensors that are used to control the humidity. When selecting multiple sensors, the average is calculated to determine the final humidity." -#: terrariumTranslations.py:78 +#: terrariumTranslations.py:83 msgid "Enable or disable the heater system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the heater system." msgstr "Enable or disable the heater system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the heater system." -#: terrariumTranslations.py:79 +#: terrariumTranslations.py:84 msgid "Enable heating when the lights are on. This can cause overheating when the lights are on." msgstr "Enable heating when the lights are on. This can cause overheating when the lights are on." -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 msgid "Select the operating mode. Use '%s' mode to select the time period in which the heating is running. Select '%s' mode to use the sun rise and sun set as on and off times. When the sun rises the heating system will stop. Use '%s' mode to have the heating running when the lights are off." msgstr "Select the operating mode. Use '%s' mode to select the time period in which the heating is running. Select '%s' mode to use the sun rise and sun set as on and off times. When the sun rises the heating system will stop. Use '%s' mode to have the heating running when the lights are off." -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/usage_environment.tpl:137 views/inc/usage_environment.tpl:231 #: views/inc/usage_environment.tpl:325 views/inc/usage_sensors.tpl:23 #: views/sensor_settings.tpl:74 views/system_environment.tpl:129 @@ -211,152 +231,154 @@ msgstr "Select the operating mode. Use '%s' mode to select the time period in wh msgid "Sensor" msgstr "Sensor" -#: terrariumTranslations.py:81 +#: terrariumTranslations.py:86 msgid "Enter the time when the heater should be put on. Only available when running in '%s' mode." msgstr "Enter the time when the heater should be put on. Only available when running in '%s' mode." -#: terrariumTranslations.py:82 +#: terrariumTranslations.py:87 msgid "Enter the time when the heater should be put off. Only available when running in '%s' mode." msgstr "Enter the time when the heater should be put off. Only available when running in '%s' mode." -#: terrariumTranslations.py:83 +#: terrariumTranslations.py:88 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the heater. Select all needed switches below." msgstr "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the heater. Select all needed switches below." -#: terrariumTranslations.py:84 terrariumTranslations.py:93 +#: terrariumTranslations.py:89 terrariumTranslations.py:98 msgid "Select the temperature sensors that are used to control the temperature. When selecting multiple sensors, the average is calculated to determine the final temperature." msgstr "Select the temperature sensors that are used to control the temperature. When selecting multiple sensors, the average is calculated to determine the final temperature." -#: terrariumTranslations.py:87 +#: terrariumTranslations.py:92 msgid "Enable or disable the cooler system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the cooler system." msgstr "Enable or disable the cooler system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the cooler system." -#: terrariumTranslations.py:88 +#: terrariumTranslations.py:93 msgid "Enable cooling when the lights are off. This can cause a very low temperature when the lights are off." msgstr "Enable cooling when the lights are off. This can cause a very low temperature when the lights are off." -#: terrariumTranslations.py:90 +#: terrariumTranslations.py:95 msgid "Enter the time when the cooler should be put on. Only available when running in '%s' mode." msgstr "Enter the time when the cooler should be put on. Only available when running in '%s' mode." -#: terrariumTranslations.py:91 +#: terrariumTranslations.py:96 msgid "Enter the time when the cooler should be put off. Only available when running in '%s' mode." msgstr "Enter the time when the cooler should be put off. Only available when running in '%s' mode." -#: terrariumTranslations.py:92 +#: terrariumTranslations.py:97 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the cooler. Select all needed switches below." msgstr "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the cooler. Select all needed switches below." -#: terrariumTranslations.py:96 +#: terrariumTranslations.py:101 msgid "Choose your interface language." msgstr "Choose your interface language." -#: terrariumTranslations.py:97 +#: terrariumTranslations.py:102 msgid "Holds the username which can make changes (Administrator)." msgstr "Holds the username which can make changes (Administrator)." -#: terrariumTranslations.py:98 +#: terrariumTranslations.py:103 msgid "Enter the new password for the administration user. Leaving empty will not change the password!" msgstr "Enter the new password for the administration user. Leaving empty will not change the password!" -#: terrariumTranslations.py:99 +#: terrariumTranslations.py:104 msgid "Enter the current password in order to change the password." msgstr "Enter the current password in order to change the password." -#: terrariumTranslations.py:100 +#: terrariumTranslations.py:105 msgid "Holds the amount of power in Wattage that the Raspberry PI uses including all USB equipment." msgstr "Holds the amount of power in Wattage that the Raspberry PI uses including all USB equipment." -#: terrariumTranslations.py:101 +#: terrariumTranslations.py:106 msgid "Holds the amount of euro/dollar per 1 kW/h (1 Kilowatt per hour)." msgstr "Holds the amount of euro/dollar per 1 kW/h (1 Kilowatt per hour)." -#: terrariumTranslations.py:102 +#: terrariumTranslations.py:107 msgid "Holds the amount of euro/dollar per 1000 liters water." msgstr "Holds the amount of euro/dollar per 1000 liters water." -#: terrariumTranslations.py:103 +#: terrariumTranslations.py:108 msgid "Choose the temperature indicator. The software will recalculate to the chosen indicator." msgstr "Choose the temperature indicator. The software will recalculate to the chosen indicator." -#: terrariumTranslations.py:104 +#: terrariumTranslations.py:109 msgid "Holds the host name or IP address on which the software will listen for connections. Enter :: for all addresses to bind." msgstr "Holds the host name or IP address on which the software will listen for connections. Enter :: for all addresses to bind." -#: terrariumTranslations.py:105 +#: terrariumTranslations.py:110 msgid "Holds the port number on which the software is listening for connections." msgstr "Holds the port number on which the software is listening for connections." -#: terrariumTranslations.py:106 +#: terrariumTranslations.py:111 msgid "Holds the port number on which the OWFS software is running. Leave empty to disable OWFS support." msgstr "Holds the port number on which the OWFS software is running. Leave empty to disable OWFS support." -#: terrariumTranslations.py:110 +#: terrariumTranslations.py:115 msgid "Holds the name of the animal." msgstr "Holds the name of the animal." -#: terrariumTranslations.py:111 +#: terrariumTranslations.py:116 msgid "Holds the type of the animal" msgstr "Holds the type of the animal" -#: terrariumTranslations.py:112 +#: terrariumTranslations.py:117 msgid "Holds the gender of the animal." msgstr "Holds the gender of the animal." -#: terrariumTranslations.py:113 +#: terrariumTranslations.py:118 msgid "Holds the day of birth of the animal." msgstr "Holds the day of birth of the animal." -#: terrariumTranslations.py:114 +#: terrariumTranslations.py:119 msgid "Holds the species name of the animal." msgstr "Holds the species name of the animal." -#: terrariumTranslations.py:115 +#: terrariumTranslations.py:120 msgid "Holds the latin name of the animal." msgstr "Holds the latin name of the animal." -#: terrariumTranslations.py:116 +#: terrariumTranslations.py:121 msgid "Holds a small description about the animal." msgstr "Holds a small description about the animal." -#: terrariumTranslations.py:117 +#: terrariumTranslations.py:122 msgid "Holds a link to more information." msgstr "Holds a link to more information." -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authenticate to make any changes" msgstr "Authenticate to make any changes" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authentication" msgstr "Authentication" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 -#: views/system_settings.tpl:23 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 views/system_settings.tpl:23 msgid "TerrariumPI" msgstr "TerrariumPI" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Data could not be saved" msgstr "Data could not be saved" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Error!" msgstr "Error!" -#: terrariumWebserver.py:161 +#: terrariumWebserver.py:167 msgid "Data saved" msgstr "Data saved" -#: terrariumWebserver.py:162 +#: terrariumWebserver.py:168 msgid "Your changes are saved" msgstr "Your changes are saved" -#: terrariumWebserver.py:234 views/inc/menu.tpl:130 +#: terrariumWebserver.py:247 views/inc/menu.tpl:130 msgid "Log out" msgstr "Log out" -#: terrariumWebserver.py:235 +#: terrariumWebserver.py:248 msgid "You are now logged out" msgstr "You are now logged out" @@ -367,13 +389,13 @@ msgstr "Uptime" #: views/dashboard.tpl:24 views/inc/usage_dashboard.tpl:93 #: views/inc/usage_switches.tpl:121 views/inc/usage_switches.tpl:143 -#: views/switch_settings.tpl:28 views/switch_settings.tpl:97 +#: views/switch_settings.tpl:28 views/switch_settings.tpl:99 msgid "Power usage in Watt" msgstr "Power usage in Watt" #: views/dashboard.tpl:35 views/inc/usage_dashboard.tpl:99 #: views/inc/usage_switches.tpl:125 views/inc/usage_switches.tpl:146 -#: views/switch_settings.tpl:31 views/switch_settings.tpl:101 +#: views/switch_settings.tpl:31 views/switch_settings.tpl:103 msgid "Water flow in L/m" msgstr "Water flow in L/m" @@ -517,7 +539,7 @@ msgstr "warning" #: views/dashboard.tpl:207 views/dashboard.tpl:259 views/door_status.tpl:16 #: views/inc/sensor_list.tpl:22 views/inc/usage_dashboard.tpl:67 -#: views/inc/usage_dashboard.tpl:180 views/inc/usage_doors.tpl:31 +#: views/inc/usage_dashboard.tpl:181 views/inc/usage_doors.tpl:31 #: views/inc/usage_sensors.tpl:35 views/inc/usage_switches.tpl:31 #: views/inc/usage_webcams.tpl:29 views/switch_status.tpl:17 #: views/system_status.tpl:15 views/system_status.tpl:71 @@ -526,7 +548,7 @@ msgid "Day" msgstr "Day" #: views/dashboard.tpl:210 views/dashboard.tpl:262 views/door_status.tpl:19 -#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:181 +#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:182 #: views/inc/usage_doors.tpl:34 views/inc/usage_sensors.tpl:38 #: views/inc/usage_switches.tpl:34 views/inc/usage_webcams.tpl:32 #: views/switch_status.tpl:20 views/system_status.tpl:18 @@ -536,7 +558,7 @@ msgid "Week" msgstr "Week" #: views/dashboard.tpl:213 views/dashboard.tpl:265 views/door_status.tpl:22 -#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:182 +#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:183 #: views/inc/usage_doors.tpl:37 views/inc/usage_sensors.tpl:41 #: views/inc/usage_switches.tpl:37 views/inc/usage_webcams.tpl:35 #: views/switch_status.tpl:23 views/system_status.tpl:21 @@ -546,7 +568,7 @@ msgid "Month" msgstr "Month" #: views/dashboard.tpl:216 views/dashboard.tpl:268 views/door_status.tpl:25 -#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:183 +#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:184 #: views/inc/usage_doors.tpl:40 views/inc/usage_sensors.tpl:44 #: views/inc/usage_switches.tpl:40 views/inc/usage_webcams.tpl:38 #: views/switch_status.tpl:26 views/system_status.tpl:24 @@ -583,7 +605,7 @@ msgstr "Required fields are marked with '%s'." #: views/inc/usage_sensors.tpl:162 views/inc/usage_switches.tpl:103 #: views/inc/usage_switches.tpl:134 views/sensor_settings.tpl:19 #: views/sensor_settings.tpl:87 views/switch_settings.tpl:19 -#: views/switch_settings.tpl:78 +#: views/switch_settings.tpl:79 msgid "Hardware" msgstr "Hardware" @@ -592,7 +614,7 @@ msgstr "Hardware" #: views/inc/usage_sensors.tpl:120 views/inc/usage_sensors.tpl:165 #: views/inc/usage_switches.tpl:112 views/inc/usage_switches.tpl:137 #: views/sensor_settings.tpl:22 views/sensor_settings.tpl:99 -#: views/switch_settings.tpl:22 views/switch_settings.tpl:88 +#: views/switch_settings.tpl:22 views/switch_settings.tpl:90 msgid "Address" msgstr "Address" @@ -603,7 +625,7 @@ msgstr "Address" #: views/inc/usage_webcams.tpl:104 views/inc/usage_webcams.tpl:134 #: views/profile.tpl:56 views/sensor_settings.tpl:28 #: views/sensor_settings.tpl:113 views/switch_settings.tpl:25 -#: views/switch_settings.tpl:93 views/webcam_settings.tpl:27 +#: views/switch_settings.tpl:95 views/webcam_settings.tpl:27 #: views/webcam_settings.tpl:86 msgid "Name" msgstr "Name" @@ -630,19 +652,19 @@ msgstr "new" #: views/door_settings.tpl:73 views/hardware.tpl:63 views/hardware.tpl:88 #: views/inc/usage_doors.tpl:101 views/inc/usage_switches.tpl:107 -#: views/switch_settings.tpl:82 +#: views/switch_settings.tpl:83 msgid "GPIO" msgstr "GPIO" #: views/door_settings.tpl:93 views/inc/usage_doors.tpl:70 #: views/inc/usage_sensors.tpl:80 views/inc/usage_switches.tpl:70 #: views/inc/usage_webcams.tpl:66 views/sensor_settings.tpl:142 -#: views/switch_settings.tpl:110 views/webcam_settings.tpl:108 +#: views/switch_settings.tpl:135 views/webcam_settings.tpl:108 msgid "Close" msgstr "Close" #: views/door_settings.tpl:94 views/sensor_settings.tpl:143 -#: views/switch_settings.tpl:111 views/webcam_settings.tpl:109 +#: views/switch_settings.tpl:136 views/webcam_settings.tpl:109 msgid "Add" msgstr "Add" @@ -664,7 +686,7 @@ msgstr "Sensors" msgid "Switches" msgstr "Switches" -#: views/hardware.tpl:19 views/hardware.tpl:107 views/inc/menu.tpl:68 +#: views/hardware.tpl:19 views/hardware.tpl:116 views/inc/menu.tpl:68 #: views/usage.tpl:19 msgid "Doors" msgstr "Doors" @@ -758,7 +780,7 @@ msgstr "TerrariumPI software has support for different kind of relay boards. The msgid "For every relay on the board there is need for a dedicated GPIO pin. For a 4 ports relay board there are 4 control GPIO pins and 2 pins for power and ground needed. This makes that it needs 6 GPIO pins in total." msgstr "For every relay on the board there is need for a dedicated GPIO pin. For a 4 ports relay board there are 4 control GPIO pins and 2 pins for power and ground needed. This makes that it needs 6 GPIO pins in total." -#: views/hardware.tpl:90 views/hardware.tpl:99 +#: views/hardware.tpl:90 views/hardware.tpl:99 views/hardware.tpl:108 msgid "The following boards are tested" msgstr "The following boards are tested" @@ -766,7 +788,7 @@ msgstr "The following boards are tested" msgid "VMA400" msgstr "VMA400" -#: views/hardware.tpl:97 views/hardware.tpl:120 +#: views/hardware.tpl:97 views/hardware.tpl:129 msgid "USB" msgstr "USB" @@ -778,29 +800,41 @@ msgstr "The USB relay board does not need GPIO pins. It only needs one USB conne msgid "Denkovi" msgstr "Denkovi" -#: views/hardware.tpl:108 +#: views/hardware.tpl:106 +msgid "PWM" +msgstr "PWM" + +#: views/hardware.tpl:107 +msgid "With PWM controlled boards it is possible to support dimming devices." +msgstr "With PWM controlled boards it is possible to support dimming devices." + +#: views/hardware.tpl:110 +msgid "Nextevo" +msgstr "Nextevo" + +#: views/hardware.tpl:117 msgid "TerrariumPI software has support for magnetic door sensors. Only versions with two wires are supported. Connect one wire to the ground and the other wire to any GPIO pin that is free (except power and ground pins)." msgstr "TerrariumPI software has support for magnetic door sensors. Only versions with two wires are supported. Connect one wire to the ground and the other wire to any GPIO pin that is free (except power and ground pins)." -#: views/hardware.tpl:111 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 +#: views/hardware.tpl:120 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 #: views/usage.tpl:22 views/webcam.tpl:7 views/webcam_settings.tpl:64 msgid "Webcam" msgstr "Webcam" -#: views/hardware.tpl:112 +#: views/hardware.tpl:121 msgid "TerrariumPI software has support for different kind of cameras. The following cameras below are tested with TerrariumPI software." msgstr "TerrariumPI software has support for different kind of cameras. The following cameras below are tested with TerrariumPI software." -#: views/hardware.tpl:115 views/inc/usage_webcams.tpl:128 +#: views/hardware.tpl:124 views/inc/usage_webcams.tpl:128 #: views/webcam_settings.tpl:21 msgid "RPICam" msgstr "RPICam" -#: views/hardware.tpl:116 +#: views/hardware.tpl:125 msgid "There are different Raspberry Pi cameras available. If the camera is Raspberry Pi compatible, it can be used with TerrariumPI software." msgstr "There are different Raspberry Pi cameras available. If the camera is Raspberry Pi compatible, it can be used with TerrariumPI software." -#: views/hardware.tpl:121 +#: views/hardware.tpl:130 msgid "All kind of USB cameras can be used. Enter physical path of the device like /dev/videoX." msgstr "All kind of USB cameras can be used. Enter physical path of the device like /dev/videoX." @@ -828,12 +862,12 @@ msgstr "Profile" msgid "Forecast" msgstr "Forecast" -#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:158 +#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:159 #: views/inc/usage_sensors.tpl:128 views/sensor_settings.tpl:107 msgid "Temperature" msgstr "Temperature" -#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:157 +#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:158 #: views/inc/usage_sensors.tpl:129 views/sensor_settings.tpl:108 msgid "Humidity" msgstr "Humidity" @@ -1418,7 +1452,7 @@ msgstr "The history graph will shows when a switch was powered on. It will show msgid "On the switch settings page you can configure all needed switches. Click on %s button to add a new switch. And empty form like below is shown and has to be filled in. Make sure the right values are filled in. All fields with a %s are required." msgstr "On the switch settings page you can configure all needed switches. Click on %s button to add a new switch. And empty form like below is shown and has to be filled in. Make sure the right values are filled in. All fields with a %s are required." -#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:81 +#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:82 msgid "FTDI" msgstr "FTDI" @@ -1689,10 +1723,34 @@ msgstr "Here you can configure your power switches." msgid "Add new switch" msgstr "Add new switch" -#: views/switch_settings.tpl:83 +#: views/switch_settings.tpl:84 msgid "GPIO Inverse" msgstr "GPIO Inverse" +#: views/switch_settings.tpl:85 +msgid "PWM Dimmer" +msgstr "PWM Dimmer" + +#: views/switch_settings.tpl:109 +msgid "Dimmer action duration" +msgstr "Dimmer action duration" + +#: views/switch_settings.tpl:113 +msgid "Dimmer on duration" +msgstr "Dimmer on duration" + +#: views/switch_settings.tpl:117 +msgid "Dimmer on percentage" +msgstr "Dimmer on percentage" + +#: views/switch_settings.tpl:121 +msgid "Dimmer off duration" +msgstr "Dimmer off duration" + +#: views/switch_settings.tpl:125 +msgid "Dimmer off percentage" +msgstr "Dimmer off percentage" + #: views/system_environment.tpl:16 msgid "Here you can configure your environment." msgstr "Here you can configure your environment." @@ -1981,6 +2039,10 @@ msgstr "Total memory" msgid "Total open for" msgstr "Total open for" +#: Missing text string +msgid "Universal AC MAINS Dimmer - MPDMv4.1" +msgstr "Universal AC MAINS Dimmer - MPDMv4.1" + #: Missing text string msgid "USB Relay board" msgstr "USB Relay board" diff --git a/locales/nl_NL/LC_MESSAGES/terrariumpi.mo b/locales/nl_NL/LC_MESSAGES/terrariumpi.mo index efdd85d8c..3091b8958 100644 Binary files a/locales/nl_NL/LC_MESSAGES/terrariumpi.mo and b/locales/nl_NL/LC_MESSAGES/terrariumpi.mo differ diff --git a/locales/nl_NL/LC_MESSAGES/terrariumpi.po b/locales/nl_NL/LC_MESSAGES/terrariumpi.po index daa26a691..2d6fe0e09 100644 --- a/locales/nl_NL/LC_MESSAGES/terrariumpi.po +++ b/locales/nl_NL/LC_MESSAGES/terrariumpi.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: TerrariumPI 0.1\n" -"POT-Creation-Date: 2017-09-30 14:37+0200\n" -"PO-Revision-Date: 2017-10-06 23:46+0200\n" +"POT-Creation-Date: 2017-10-24 23:09+0200\n" +"PO-Revision-Date: 2017-10-24 23:11+0200\n" "Last-Translator: Joshua (TheYOSH) Rubingh \n" "Language-Team: \n" "Language: nl_NL\n" @@ -114,11 +114,35 @@ msgid "Holds the switch water flow in liters per minute when switched on" msgstr "" "Houd de schakelaar waterstroom in liter per minuut wanneer ingeschakeld." +#: terrariumTranslations.py:43 +msgid "" +"Holds the amount of seconds for the duration in which the dimmer changes to " +"the new value." +msgstr "" + +#: terrariumTranslations.py:44 +msgid "" +"Holds the amount of seconds for the duration in which it increases the power." +msgstr "" + +#: terrariumTranslations.py:45 +msgid "Holds the amount in percentage to go to when switched on." +msgstr "" + #: terrariumTranslations.py:46 +msgid "" +"Holds the amount of seconds for the duration in which it decresses the power." +msgstr "" + +#: terrariumTranslations.py:47 +msgid "Holds the amount in percentage to go to when switched off." +msgstr "" + +#: terrariumTranslations.py:51 msgid "Holds the door hardware type. Supported hardware types are: %s." msgstr "Bevat de deurhardware type. Ondersteunde hardware types zijn: %s." -#: terrariumTranslations.py:47 +#: terrariumTranslations.py:52 msgid "" "Holds the door address. When using GPIO use Physical GPIO " "pin numbering as address." @@ -126,27 +150,27 @@ msgstr "" "Bevat het deuradres. Wanneer u GPIO gebruikt, gebruikt u Physical GPIO pin nummering als adres" -#: terrariumTranslations.py:48 +#: terrariumTranslations.py:53 msgid "Holds the door name." msgstr "Bevat de deur naam." -#: terrariumTranslations.py:52 +#: terrariumTranslations.py:57 msgid "Holds the webcam location source. Supported sources are: %s" msgstr "Bevat de webcam locatie bron. Ondersteunde bronnen zijn: %s" -#: terrariumTranslations.py:53 +#: terrariumTranslations.py:58 msgid "Holds the webcam name." msgstr "Bevat de webcam naam." -#: terrariumTranslations.py:54 +#: terrariumTranslations.py:59 msgid "Holds the webcam rotation of the image." msgstr "Bevat de webcam rotatie van de afbeelding." -#: terrariumTranslations.py:55 +#: terrariumTranslations.py:60 msgid "Shows the webcam preview image." msgstr "Toont de webcam voorbeeld afbeelding." -#: terrariumTranslations.py:59 +#: terrariumTranslations.py:64 msgid "" "Enable or disable the light system. When enabled, you can make changes " "below. By disabling it will not loose the current settings. It will " @@ -157,7 +181,7 @@ msgstr "" "instellingen niet verloren gaan. Het zal het verlichtingssysteem tijdelijk " "stoppen." -#: terrariumTranslations.py:60 +#: terrariumTranslations.py:65 msgid "" "Select the mode on which the lights will be put on and off. Select '%s' to " "use the sun rise and sun set at your location. This will make the amount of " @@ -170,19 +194,19 @@ msgstr "" "Bij het selecteren van '%s', zal het licht aan en uit zetten op " "geselecteerde tijden." -#: terrariumTranslations.py:60 terrariumTranslations.py:61 -#: terrariumTranslations.py:62 terrariumTranslations.py:80 -#: terrariumTranslations.py:81 terrariumTranslations.py:82 -#: terrariumTranslations.py:89 terrariumTranslations.py:90 -#: terrariumTranslations.py:91 views/inc/usage_environment.tpl:33 +#: terrariumTranslations.py:65 terrariumTranslations.py:66 +#: terrariumTranslations.py:67 terrariumTranslations.py:85 +#: terrariumTranslations.py:86 terrariumTranslations.py:87 +#: terrariumTranslations.py:94 terrariumTranslations.py:95 +#: terrariumTranslations.py:96 views/inc/usage_environment.tpl:33 #: views/inc/usage_environment.tpl:229 views/inc/usage_environment.tpl:323 #: views/system_environment.tpl:46 views/system_environment.tpl:202 #: views/system_environment.tpl:277 msgid "Timer" msgstr "Klok" -#: terrariumTranslations.py:60 terrariumTranslations.py:65 -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:65 terrariumTranslations.py:70 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/menu.tpl:32 views/inc/usage_environment.tpl:34 #: views/inc/usage_environment.tpl:230 views/inc/usage_environment.tpl:324 #: views/inc/usage_weather.tpl:13 views/inc/usage_weather.tpl:82 @@ -193,7 +217,7 @@ msgstr "Klok" msgid "Weather" msgstr "Weer" -#: terrariumTranslations.py:61 +#: terrariumTranslations.py:66 msgid "" "Enter the time when the light should be put on. Only available when running " "in '%s' mode." @@ -201,7 +225,7 @@ msgstr "" "Voer de tijd in waarop de lampen moet worden ingeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:62 +#: terrariumTranslations.py:67 msgid "" "Enter the time when the lights should be put off. Only available when " "running in '%s' mode." @@ -209,7 +233,7 @@ msgstr "" "Voer de tijd in waarop de lampen moet worden uitgeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:63 +#: terrariumTranslations.py:68 msgid "" "Enter the maximum amount of lights time in hours. When the time between on " "and off is more then this maximum, the on and off time will be shifted more " @@ -219,7 +243,7 @@ msgstr "" "en uit meer is dan dit maximum, wordt de aan- en uitschakel tijd meer naar " "elkaar verschoven." -#: terrariumTranslations.py:64 +#: terrariumTranslations.py:69 msgid "" "Enter the minimum amount of lights time in hours. When the time between on " "and off is less then this amount of hours, the on and off time will be " @@ -229,7 +253,7 @@ msgstr "" "minder is dan deze hoeveelheid uren, wordt de aan- en uittijd vergroot tot " "het minimum aantal uren dat hier is ingevoerd." -#: terrariumTranslations.py:65 +#: terrariumTranslations.py:70 msgid "" "Enter the amount of hours that the lights should shift. Is only needed when " "running in the '%s' mode. Enter a positive number for adding hours to the " @@ -240,7 +264,7 @@ msgstr "" "aan de begintijd. Gebruik negatieve getallen om af te trekken vanaf de " "starttijd." -#: terrariumTranslations.py:66 +#: terrariumTranslations.py:71 msgid "" "Select the power switches that should be toggled on the selected times " "above. Normally these are the switches connected to the lights. Select all " @@ -250,7 +274,7 @@ msgstr "" "moeten worden ingeschakeld. Normaal gesproken zijn dit de schakelaars die op " "de lichten zijn aangesloten. Selecteer alle benodigde schakelaars hieronder." -#: terrariumTranslations.py:69 +#: terrariumTranslations.py:74 msgid "" "Enable or disable the sprayer system. When enabled, you can make changes " "below. By disabling it will not loose the current settings. It will " @@ -260,7 +284,7 @@ msgstr "" "wijzigingen aanbrengen. Door het uitschakelen zal het de huidige " "instellingen niet verloren gaan. Het zal het spuitsysteem tijdelijk stoppen." -#: terrariumTranslations.py:70 +#: terrariumTranslations.py:75 msgid "" "Enable spraying when the lights are off. This can cause water flow when " "there is not enough heat to vaporize the water." @@ -268,12 +292,12 @@ msgstr "" "Spuiten inschakelen als de lampen uit zijn. Dit kan ervoor zorgen dat water " "stroomt als er niet voldoende warmte is om het water te verdampen." -#: terrariumTranslations.py:71 +#: terrariumTranslations.py:76 msgid "Select the operating mode. For now only sensor mode is available." msgstr "" "Selecteer de bedrijfsmodus. Vooralsnog is alleen sensor modus beschikbaar." -#: terrariumTranslations.py:72 +#: terrariumTranslations.py:77 msgid "" "How much time must there be between two spray actions and after start up. " "Enter the amount of seconds in which the humidity can settle." @@ -281,7 +305,7 @@ msgstr "" "Hoeveel tijd moet er bestaan tussen twee spuitacties en na het opstarten. " "Voer het aantal seconden in waarin de luchtvochtigheid kan bezinken." -#: terrariumTranslations.py:73 +#: terrariumTranslations.py:78 msgid "" "How long is the system spraying. Enter the amount of seconds that the system " "is on when the humidity is too low." @@ -289,7 +313,7 @@ msgstr "" "Hoe lang is het systeem aan het spuiten. Voer het aantal seconden in dat het " "systeem aanstaat als de luchtvochtigheid te laag is." -#: terrariumTranslations.py:74 +#: terrariumTranslations.py:79 msgid "" "Select the power switches that should be toggled on the selected times " "above. Normally these are the switches connected to the sprayer. Select all " @@ -299,7 +323,7 @@ msgstr "" "moeten worden ingeschakeld. Normaal gesproken zijn deze de schakelaars " "aangesloten op de sproeier. Selecteer alle benodigde schakelaars hieronder." -#: terrariumTranslations.py:75 +#: terrariumTranslations.py:80 msgid "" "Select the humidity sensors that are used to control the humidity. When " "selecting multiple sensors, the average is calculated to determine the final " @@ -309,7 +333,7 @@ msgstr "" "luchtvochtigheid te regelen. Bij het selecteren van meerdere sensoren wordt " "het gemiddelde berekend om de uiteindelijke luchtvochtigheid te bepalen." -#: terrariumTranslations.py:78 +#: terrariumTranslations.py:83 msgid "" "Enable or disable the heater system. When enabled, you can make changes " "below. By disabling it will not loose the current settings. It will " @@ -320,7 +344,7 @@ msgstr "" "instellingen niet verloren gaan. Het zal de verwarmingsinstallatie tijdelijk " "stoppen." -#: terrariumTranslations.py:79 +#: terrariumTranslations.py:84 msgid "" "Enable heating when the lights are on. This can cause overheating when the " "lights are on." @@ -328,7 +352,7 @@ msgstr "" "Schakel de verwarming in als de lampen aan zijn. Dit kan oververhitting " "veroorzaken wanneer de lichten aan zijn." -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 msgid "" "Select the operating mode. Use '%s' mode to select the time period in which " "the heating is running. Select '%s' mode to use the sun rise and sun set as " @@ -341,7 +365,7 @@ msgstr "" "opkomt, stopt het verwarmingssysteem. Gebruik de '%s' modus om de verwarming " "te laten draaien als de verlichting uit is." -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/usage_environment.tpl:137 views/inc/usage_environment.tpl:231 #: views/inc/usage_environment.tpl:325 views/inc/usage_sensors.tpl:23 #: views/sensor_settings.tpl:74 views/system_environment.tpl:129 @@ -349,7 +373,7 @@ msgstr "" msgid "Sensor" msgstr "Sensor" -#: terrariumTranslations.py:81 +#: terrariumTranslations.py:86 msgid "" "Enter the time when the heater should be put on. Only available when running " "in '%s' mode." @@ -357,7 +381,7 @@ msgstr "" "Voer de tijd in waarop de verwarming moet worden ingeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:82 +#: terrariumTranslations.py:87 msgid "" "Enter the time when the heater should be put off. Only available when " "running in '%s' mode." @@ -365,7 +389,7 @@ msgstr "" "Voer de tijd in waarop de verwarming moet worden uitgeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:83 +#: terrariumTranslations.py:88 msgid "" "Select the power switches that should be toggled on the selected times " "above. Normally these are the switches connected to the heater. Select all " @@ -375,7 +399,7 @@ msgstr "" "moeten worden ingeschakeld. Normaal gesproken zijn deze de schakelaars " "aangesloten op de verwarming. Selecteer alle benodigde schakelaars hieronder." -#: terrariumTranslations.py:84 terrariumTranslations.py:93 +#: terrariumTranslations.py:89 terrariumTranslations.py:98 msgid "" "Select the temperature sensors that are used to control the temperature. " "When selecting multiple sensors, the average is calculated to determine the " @@ -385,7 +409,7 @@ msgstr "" "regelen. Bij het selecteren van meerdere sensoren wordt het gemiddelde " "berekend om de eindtemperatuur te bepalen." -#: terrariumTranslations.py:87 +#: terrariumTranslations.py:92 msgid "" "Enable or disable the cooler system. When enabled, you can make changes " "below. By disabling it will not loose the current settings. It will " @@ -395,7 +419,7 @@ msgstr "" "wijzigingen aanbrengen. Door het uitschakelen zal het de huidige " "instellingen niet verloren gaan. Het zal het koelsysteem tijdelijk stoppen." -#: terrariumTranslations.py:88 +#: terrariumTranslations.py:93 msgid "" "Enable cooling when the lights are off. This can cause a very low " "temperature when the lights are off." @@ -403,7 +427,7 @@ msgstr "" "Zet koeler aan als de lampen uit zijn. Dit kan leiden tot een zeer lage " "temperatuur wanneer de lichten uit zijn." -#: terrariumTranslations.py:90 +#: terrariumTranslations.py:95 msgid "" "Enter the time when the cooler should be put on. Only available when running " "in '%s' mode." @@ -411,7 +435,7 @@ msgstr "" "Voer de tijd in waarop de koeler moet worden ingeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:91 +#: terrariumTranslations.py:96 msgid "" "Enter the time when the cooler should be put off. Only available when " "running in '%s' mode." @@ -419,7 +443,7 @@ msgstr "" "Voer de tijd in waarop de koeler moet worden uitgeschakeld. Alleen " "beschikbaar in modus '%s'." -#: terrariumTranslations.py:92 +#: terrariumTranslations.py:97 msgid "" "Select the power switches that should be toggled on the selected times " "above. Normally these are the switches connected to the cooler. Select all " @@ -429,15 +453,15 @@ msgstr "" "moeten worden ingeschakeld. Normaal gesproken zijn deze de schakelaars " "aangesloten op de koeler. Selecteer alle benodigde schakelaars hieronder." -#: terrariumTranslations.py:96 +#: terrariumTranslations.py:101 msgid "Choose your interface language." msgstr "Kies uw interface taal." -#: terrariumTranslations.py:97 +#: terrariumTranslations.py:102 msgid "Holds the username which can make changes (Administrator)." msgstr "Bevat de gebruikersnaam die wijzigingen kan doen (Administrator)." -#: terrariumTranslations.py:98 +#: terrariumTranslations.py:103 msgid "" "Enter the new password for the administration user. Leaving empty will not " "change the password!" @@ -445,11 +469,11 @@ msgstr "" "Voer het nieuwe wachtwoord in voor de beheerder. Als u het leeg gaat, " "verandert het wachtwoord niet!" -#: terrariumTranslations.py:99 +#: terrariumTranslations.py:104 msgid "Enter the current password in order to change the password." msgstr "Voer het huidige wachtwoord in om het wachtwoord te wijzigen." -#: terrariumTranslations.py:100 +#: terrariumTranslations.py:105 msgid "" "Holds the amount of power in Wattage that the Raspberry PI uses including " "all USB equipment." @@ -457,15 +481,15 @@ msgstr "" "Bevat de hoeveelheid energie in watt die de Raspberry PI gebruikt, inclusief " "alle USB-apparatuur." -#: terrariumTranslations.py:101 +#: terrariumTranslations.py:106 msgid "Holds the amount of euro/dollar per 1 kW/h (1 Kilowatt per hour)." msgstr "Bevat de hoeveelheid euro/dollar per 1 kW/h (1 Kilowatt per uur)." -#: terrariumTranslations.py:102 +#: terrariumTranslations.py:107 msgid "Holds the amount of euro/dollar per 1000 liters water." msgstr "Bevat de hoeveelheid euro/dollar per 1000 liter water." -#: terrariumTranslations.py:103 +#: terrariumTranslations.py:108 msgid "" "Choose the temperature indicator. The software will recalculate to the " "chosen indicator." @@ -473,7 +497,7 @@ msgstr "" "Kies de temperatuurindicator. De software zal opnieuw worden berekend op de " "gekozen indicator." -#: terrariumTranslations.py:104 +#: terrariumTranslations.py:109 msgid "" "Holds the host name or IP address on which the software will listen for " "connections. Enter :: for all addresses to bind." @@ -481,12 +505,12 @@ msgstr "" "Bevat de hostnaam of het IP-adres waarop de software naar verbindingen " "luistert. Vul in :: voor alle adressen om te luisteren." -#: terrariumTranslations.py:105 +#: terrariumTranslations.py:110 msgid "" "Holds the port number on which the software is listening for connections." msgstr "Bevat het poortnummer waarop de software luistert naar verbindingen." -#: terrariumTranslations.py:106 +#: terrariumTranslations.py:111 msgid "" "Holds the port number on which the OWFS software is running. Leave empty to " "disable OWFS support." @@ -494,72 +518,74 @@ msgstr "" "Bevat het poortnummer waarop de OWFS-software draait. Laat leeg om OWFS-" "ondersteuning uit te schakelen." -#: terrariumTranslations.py:110 +#: terrariumTranslations.py:115 msgid "Holds the name of the animal." msgstr "Bevat de dier naam." -#: terrariumTranslations.py:111 +#: terrariumTranslations.py:116 msgid "Holds the type of the animal" msgstr "Bevat de type van het dier." -#: terrariumTranslations.py:112 +#: terrariumTranslations.py:117 msgid "Holds the gender of the animal." msgstr "Bevat het geslacht van het dier." -#: terrariumTranslations.py:113 +#: terrariumTranslations.py:118 msgid "Holds the day of birth of the animal." msgstr "Bevat de geboortedatum van het dier." -#: terrariumTranslations.py:114 +#: terrariumTranslations.py:119 msgid "Holds the species name of the animal." msgstr "Bevat de soortnaam van het dier." -#: terrariumTranslations.py:115 +#: terrariumTranslations.py:120 msgid "Holds the latin name of the animal." msgstr "Bevat de Latijnse naam van het dier." -#: terrariumTranslations.py:116 +#: terrariumTranslations.py:121 msgid "Holds a small description about the animal." msgstr "Bevat een kleine beschrijving over het dier." -#: terrariumTranslations.py:117 +#: terrariumTranslations.py:122 msgid "Holds a link to more information." msgstr "Bevat een link naar meer informatie." -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authenticate to make any changes" msgstr "Login om wijzigingen te kunnen maken" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authentication" msgstr "Authenticatie" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 -#: views/system_settings.tpl:23 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 views/system_settings.tpl:23 msgid "TerrariumPI" msgstr "TerrariumPI" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Data could not be saved" msgstr "Data kon niet worden opgeslagen" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Error!" msgstr "Fout!" -#: terrariumWebserver.py:161 +#: terrariumWebserver.py:167 msgid "Data saved" msgstr "Data is opgeslagen" -#: terrariumWebserver.py:162 +#: terrariumWebserver.py:168 msgid "Your changes are saved" msgstr "Je wijzigingen zijn opgeslagen" -#: terrariumWebserver.py:234 views/inc/menu.tpl:130 +#: terrariumWebserver.py:247 views/inc/menu.tpl:130 msgid "Log out" msgstr "Log uit" -#: terrariumWebserver.py:235 +#: terrariumWebserver.py:248 msgid "You are now logged out" msgstr "Je bent nu uitgelogd" @@ -570,13 +596,13 @@ msgstr "Actief" #: views/dashboard.tpl:24 views/inc/usage_dashboard.tpl:93 #: views/inc/usage_switches.tpl:121 views/inc/usage_switches.tpl:143 -#: views/switch_settings.tpl:28 views/switch_settings.tpl:97 +#: views/switch_settings.tpl:28 views/switch_settings.tpl:99 msgid "Power usage in Watt" msgstr "Stroomverbruik in Watt" #: views/dashboard.tpl:35 views/inc/usage_dashboard.tpl:99 #: views/inc/usage_switches.tpl:125 views/inc/usage_switches.tpl:146 -#: views/switch_settings.tpl:31 views/switch_settings.tpl:101 +#: views/switch_settings.tpl:31 views/switch_settings.tpl:103 msgid "Water flow in L/m" msgstr "Waterverbruik in L/m" @@ -720,7 +746,7 @@ msgstr "waarschuwing" #: views/dashboard.tpl:207 views/dashboard.tpl:259 views/door_status.tpl:16 #: views/inc/sensor_list.tpl:22 views/inc/usage_dashboard.tpl:67 -#: views/inc/usage_dashboard.tpl:180 views/inc/usage_doors.tpl:31 +#: views/inc/usage_dashboard.tpl:181 views/inc/usage_doors.tpl:31 #: views/inc/usage_sensors.tpl:35 views/inc/usage_switches.tpl:31 #: views/inc/usage_webcams.tpl:29 views/switch_status.tpl:17 #: views/system_status.tpl:15 views/system_status.tpl:71 @@ -729,7 +755,7 @@ msgid "Day" msgstr "Dag" #: views/dashboard.tpl:210 views/dashboard.tpl:262 views/door_status.tpl:19 -#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:181 +#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:182 #: views/inc/usage_doors.tpl:34 views/inc/usage_sensors.tpl:38 #: views/inc/usage_switches.tpl:34 views/inc/usage_webcams.tpl:32 #: views/switch_status.tpl:20 views/system_status.tpl:18 @@ -739,7 +765,7 @@ msgid "Week" msgstr "Week" #: views/dashboard.tpl:213 views/dashboard.tpl:265 views/door_status.tpl:22 -#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:182 +#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:183 #: views/inc/usage_doors.tpl:37 views/inc/usage_sensors.tpl:41 #: views/inc/usage_switches.tpl:37 views/inc/usage_webcams.tpl:35 #: views/switch_status.tpl:23 views/system_status.tpl:21 @@ -749,7 +775,7 @@ msgid "Month" msgstr "Maand" #: views/dashboard.tpl:216 views/dashboard.tpl:268 views/door_status.tpl:25 -#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:183 +#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:184 #: views/inc/usage_doors.tpl:40 views/inc/usage_sensors.tpl:44 #: views/inc/usage_switches.tpl:40 views/inc/usage_webcams.tpl:38 #: views/switch_status.tpl:26 views/system_status.tpl:24 @@ -786,7 +812,7 @@ msgstr "Verplichte velden zijn gemarkeerd met '% s'." #: views/inc/usage_sensors.tpl:162 views/inc/usage_switches.tpl:103 #: views/inc/usage_switches.tpl:134 views/sensor_settings.tpl:19 #: views/sensor_settings.tpl:87 views/switch_settings.tpl:19 -#: views/switch_settings.tpl:78 +#: views/switch_settings.tpl:79 msgid "Hardware" msgstr "Hardware" @@ -795,7 +821,7 @@ msgstr "Hardware" #: views/inc/usage_sensors.tpl:120 views/inc/usage_sensors.tpl:165 #: views/inc/usage_switches.tpl:112 views/inc/usage_switches.tpl:137 #: views/sensor_settings.tpl:22 views/sensor_settings.tpl:99 -#: views/switch_settings.tpl:22 views/switch_settings.tpl:88 +#: views/switch_settings.tpl:22 views/switch_settings.tpl:90 msgid "Address" msgstr "Adres" @@ -806,7 +832,7 @@ msgstr "Adres" #: views/inc/usage_webcams.tpl:104 views/inc/usage_webcams.tpl:134 #: views/profile.tpl:56 views/sensor_settings.tpl:28 #: views/sensor_settings.tpl:113 views/switch_settings.tpl:25 -#: views/switch_settings.tpl:93 views/webcam_settings.tpl:27 +#: views/switch_settings.tpl:95 views/webcam_settings.tpl:27 #: views/webcam_settings.tpl:86 msgid "Name" msgstr "Naam" @@ -833,19 +859,19 @@ msgstr "nieuw" #: views/door_settings.tpl:73 views/hardware.tpl:63 views/hardware.tpl:88 #: views/inc/usage_doors.tpl:101 views/inc/usage_switches.tpl:107 -#: views/switch_settings.tpl:82 +#: views/switch_settings.tpl:83 msgid "GPIO" msgstr "GPIO" #: views/door_settings.tpl:93 views/inc/usage_doors.tpl:70 #: views/inc/usage_sensors.tpl:80 views/inc/usage_switches.tpl:70 #: views/inc/usage_webcams.tpl:66 views/sensor_settings.tpl:142 -#: views/switch_settings.tpl:110 views/webcam_settings.tpl:108 +#: views/switch_settings.tpl:135 views/webcam_settings.tpl:108 msgid "Close" msgstr "Sluiten" #: views/door_settings.tpl:94 views/sensor_settings.tpl:143 -#: views/switch_settings.tpl:111 views/webcam_settings.tpl:109 +#: views/switch_settings.tpl:136 views/webcam_settings.tpl:109 msgid "Add" msgstr "Voeg toe" @@ -867,7 +893,7 @@ msgstr "Sensoren" msgid "Switches" msgstr "Schakelaars" -#: views/hardware.tpl:19 views/hardware.tpl:107 views/inc/menu.tpl:68 +#: views/hardware.tpl:19 views/hardware.tpl:116 views/inc/menu.tpl:68 #: views/usage.tpl:19 msgid "Doors" msgstr "Deuren" @@ -1017,7 +1043,7 @@ msgstr "" "poorts relaisbord zijn er 4 controle GPIO-pennen en 2 pennen voor stroom en " "aarde nodig. Dit houdt in dat het in totaal 6 GPIO pins nodig heeft." -#: views/hardware.tpl:90 views/hardware.tpl:99 +#: views/hardware.tpl:90 views/hardware.tpl:99 views/hardware.tpl:108 msgid "The following boards are tested" msgstr "De volgende borden zijn getest" @@ -1025,7 +1051,7 @@ msgstr "De volgende borden zijn getest" msgid "VMA400" msgstr "VMA400" -#: views/hardware.tpl:97 views/hardware.tpl:120 +#: views/hardware.tpl:97 views/hardware.tpl:129 msgid "USB" msgstr "USB" @@ -1045,7 +1071,19 @@ msgstr "" msgid "Denkovi" msgstr "Denkovi" -#: views/hardware.tpl:108 +#: views/hardware.tpl:106 +msgid "PWM" +msgstr "PWM" + +#: views/hardware.tpl:107 +msgid "With PWM controlled boards it is possible to support dimming devices." +msgstr "Met PWM relais borden is het mogelijk om apparaten te dimmen" + +#: views/hardware.tpl:110 +msgid "Nextevo" +msgstr "" + +#: views/hardware.tpl:117 msgid "" "TerrariumPI software has support for magnetic door sensors. Only versions " "with two wires are supported. Connect one wire to the ground and the other " @@ -1056,12 +1094,12 @@ msgstr "" "grond en de andere draad aan op een GPIO-pin die vrij is (behalve power en " "grond pinnen)." -#: views/hardware.tpl:111 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 +#: views/hardware.tpl:120 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 #: views/usage.tpl:22 views/webcam.tpl:7 views/webcam_settings.tpl:64 msgid "Webcam" msgstr "Webcam" -#: views/hardware.tpl:112 +#: views/hardware.tpl:121 msgid "" "TerrariumPI software has support for different kind of cameras. The " "following cameras below are tested with TerrariumPI software." @@ -1069,12 +1107,12 @@ msgstr "" "TerrariumPI software heeft ondersteuning voor verschillende soorten " "camera's. De onderstaande camera's zijn getest met TerrariumPI software." -#: views/hardware.tpl:115 views/inc/usage_webcams.tpl:128 +#: views/hardware.tpl:124 views/inc/usage_webcams.tpl:128 #: views/webcam_settings.tpl:21 msgid "RPICam" msgstr "RPICam" -#: views/hardware.tpl:116 +#: views/hardware.tpl:125 msgid "" "There are different Raspberry Pi cameras available. If the camera is " "Raspberry Pi compatible, it can be used with TerrariumPI software." @@ -1083,7 +1121,7 @@ msgstr "" "Raspberry Pi compatibel is, kan deze worden gebruikt met TerrariumPI " "software." -#: views/hardware.tpl:121 +#: views/hardware.tpl:130 msgid "" "All kind of USB cameras can be used. Enter physical path of the device like /" "dev/videoX." @@ -1115,12 +1153,12 @@ msgstr "Profiel" msgid "Forecast" msgstr "Voorspelling" -#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:158 +#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:159 #: views/inc/usage_sensors.tpl:128 views/sensor_settings.tpl:107 msgid "Temperature" msgstr "Temperatuur" -#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:157 +#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:158 #: views/inc/usage_sensors.tpl:129 views/sensor_settings.tpl:108 msgid "Humidity" msgstr "Luchtvochtigheid" @@ -1981,7 +2019,7 @@ msgstr "" "ervoor dat de juiste waarden zijn ingevuld. Alle velden met een %s zijn " "verplicht." -#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:81 +#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:82 msgid "FTDI" msgstr "FTDI" @@ -2336,10 +2374,34 @@ msgstr "Hier kunt u uw stroomschakelaars configureren." msgid "Add new switch" msgstr "Voeg nieuwe schakelaar toe" -#: views/switch_settings.tpl:83 +#: views/switch_settings.tpl:84 msgid "GPIO Inverse" msgstr "GPIO omgekeerd" +#: views/switch_settings.tpl:85 +msgid "PWM Dimmer" +msgstr "PWM Dimmer" + +#: views/switch_settings.tpl:109 +msgid "Dimmer action duration" +msgstr "Dimmer tijd" + +#: views/switch_settings.tpl:113 +msgid "Dimmer on duration" +msgstr "Dimmer aan tijd in seconden" + +#: views/switch_settings.tpl:117 +msgid "Dimmer on percentage" +msgstr "Dimmer aan percentage" + +#: views/switch_settings.tpl:121 +msgid "Dimmer off duration" +msgstr "Dimmer uit in seconden" + +#: views/switch_settings.tpl:125 +msgid "Dimmer off percentage" +msgstr "Dimmer uit percentage" + #: views/system_environment.tpl:16 msgid "Here you can configure your environment." msgstr "Hier kunt u uw omgeving configureren." @@ -2628,6 +2690,10 @@ msgstr "Alle geheugen" msgid "Total open for" msgstr "In totaal open" +#: Missing text string +msgid "Universal AC MAINS Dimmer - MPDMv4.1" +msgstr "Universal AC MAINS Dimmer - MPDMv4.1" + #: Missing text string msgid "USB Relay board" msgstr "USB Relais board" diff --git a/locales/terrariumpi.pot b/locales/terrariumpi.pot index 18ec969b0..a91dc8b55 100644 --- a/locales/terrariumpi.pot +++ b/locales/terrariumpi.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2017-09-27 20:19+CEST\n" +"POT-Creation-Date: 2017-10-24 23:08+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -79,55 +79,75 @@ msgstr "" msgid "Holds the switch water flow in liters per minute when switched on" msgstr "" +#: terrariumTranslations.py:43 +msgid "Holds the amount of seconds for the duration in which the dimmer changes to the new value." +msgstr "" + +#: terrariumTranslations.py:44 +msgid "Holds the amount of seconds for the duration in which it increases the power." +msgstr "" + +#: terrariumTranslations.py:45 +msgid "Holds the amount in percentage to go to when switched on." +msgstr "" + #: terrariumTranslations.py:46 -msgid "Holds the door hardware type. Supported hardware types are: %s." +msgid "Holds the amount of seconds for the duration in which it decresses the power." msgstr "" #: terrariumTranslations.py:47 +msgid "Holds the amount in percentage to go to when switched off." +msgstr "" + +#: terrariumTranslations.py:51 +msgid "Holds the door hardware type. Supported hardware types are: %s." +msgstr "" + +#: terrariumTranslations.py:52 msgid "Holds the door address. When using GPIO use Physical GPIO pin numbering as address." msgstr "" -#: terrariumTranslations.py:48 +#: terrariumTranslations.py:53 msgid "Holds the door name." msgstr "" -#: terrariumTranslations.py:52 +#: terrariumTranslations.py:57 msgid "Holds the webcam location source. Supported sources are: %s" msgstr "" -#: terrariumTranslations.py:53 +#: terrariumTranslations.py:58 msgid "Holds the webcam name." msgstr "" -#: terrariumTranslations.py:54 +#: terrariumTranslations.py:59 msgid "Holds the webcam rotation of the image." msgstr "" -#: terrariumTranslations.py:55 +#: terrariumTranslations.py:60 msgid "Shows the webcam preview image." msgstr "" -#: terrariumTranslations.py:59 +#: terrariumTranslations.py:64 msgid "Enable or disable the light system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the lighting system." msgstr "" -#: terrariumTranslations.py:60 +#: terrariumTranslations.py:65 msgid "Select the mode on which the lights will be put on and off. Select '%s' to use the sun rise and sun set at your location. This will make the amount of lighting variable to the actual amount of daylight. When selecting '%s', the light will put on and off at selected times." msgstr "" -#: terrariumTranslations.py:60 terrariumTranslations.py:61 -#: terrariumTranslations.py:62 terrariumTranslations.py:80 -#: terrariumTranslations.py:81 terrariumTranslations.py:82 -#: terrariumTranslations.py:89 terrariumTranslations.py:90 -#: terrariumTranslations.py:91 views/inc/usage_environment.tpl:33 +#: terrariumTranslations.py:65 terrariumTranslations.py:66 +#: terrariumTranslations.py:67 terrariumTranslations.py:85 +#: terrariumTranslations.py:86 terrariumTranslations.py:87 +#: terrariumTranslations.py:94 terrariumTranslations.py:95 +#: terrariumTranslations.py:96 views/inc/usage_environment.tpl:33 #: views/inc/usage_environment.tpl:229 views/inc/usage_environment.tpl:323 #: views/system_environment.tpl:46 views/system_environment.tpl:202 #: views/system_environment.tpl:277 msgid "Timer" msgstr "" -#: terrariumTranslations.py:60 terrariumTranslations.py:65 -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:65 terrariumTranslations.py:70 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/menu.tpl:32 views/inc/usage_environment.tpl:34 #: views/inc/usage_environment.tpl:230 views/inc/usage_environment.tpl:324 #: views/inc/usage_weather.tpl:13 views/inc/usage_weather.tpl:82 @@ -138,71 +158,71 @@ msgstr "" msgid "Weather" msgstr "" -#: terrariumTranslations.py:61 +#: terrariumTranslations.py:66 msgid "Enter the time when the light should be put on. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:62 +#: terrariumTranslations.py:67 msgid "Enter the time when the lights should be put off. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:63 +#: terrariumTranslations.py:68 msgid "Enter the maximum amount of lights time in hours. When the time between on and off is more then this maximum, the on and off time will be shifted more to each other." msgstr "" -#: terrariumTranslations.py:64 +#: terrariumTranslations.py:69 msgid "Enter the minimum amount of lights time in hours. When the time between on and off is less then this amount of hours, the on and off time will be widened until the minimum amount of hours entered here." msgstr "" -#: terrariumTranslations.py:65 +#: terrariumTranslations.py:70 msgid "Enter the amount of hours that the lights should shift. Is only needed when running in the '%s' mode. Enter a positive number for adding hours to the start time. Use negative numbers for subtracting from the start time." msgstr "" -#: terrariumTranslations.py:66 +#: terrariumTranslations.py:71 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the lights. Select all needed switches below." msgstr "" -#: terrariumTranslations.py:69 +#: terrariumTranslations.py:74 msgid "Enable or disable the sprayer system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the sprayer system." msgstr "" -#: terrariumTranslations.py:70 +#: terrariumTranslations.py:75 msgid "Enable spraying when the lights are off. This can cause water flow when there is not enough heat to vaporize the water." msgstr "" -#: terrariumTranslations.py:71 +#: terrariumTranslations.py:76 msgid "Select the operating mode. For now only sensor mode is available." msgstr "" -#: terrariumTranslations.py:72 +#: terrariumTranslations.py:77 msgid "How much time must there be between two spray actions and after start up. Enter the amount of seconds in which the humidity can settle." msgstr "" -#: terrariumTranslations.py:73 +#: terrariumTranslations.py:78 msgid "How long is the system spraying. Enter the amount of seconds that the system is on when the humidity is too low." msgstr "" -#: terrariumTranslations.py:74 +#: terrariumTranslations.py:79 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the sprayer. Select all needed switches below." msgstr "" -#: terrariumTranslations.py:75 +#: terrariumTranslations.py:80 msgid "Select the humidity sensors that are used to control the humidity. When selecting multiple sensors, the average is calculated to determine the final humidity." msgstr "" -#: terrariumTranslations.py:78 +#: terrariumTranslations.py:83 msgid "Enable or disable the heater system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the heater system." msgstr "" -#: terrariumTranslations.py:79 +#: terrariumTranslations.py:84 msgid "Enable heating when the lights are on. This can cause overheating when the lights are on." msgstr "" -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 msgid "Select the operating mode. Use '%s' mode to select the time period in which the heating is running. Select '%s' mode to use the sun rise and sun set as on and off times. When the sun rises the heating system will stop. Use '%s' mode to have the heating running when the lights are off." msgstr "" -#: terrariumTranslations.py:80 terrariumTranslations.py:89 +#: terrariumTranslations.py:85 terrariumTranslations.py:94 #: views/inc/usage_environment.tpl:137 views/inc/usage_environment.tpl:231 #: views/inc/usage_environment.tpl:325 views/inc/usage_sensors.tpl:23 #: views/sensor_settings.tpl:74 views/system_environment.tpl:129 @@ -210,152 +230,154 @@ msgstr "" msgid "Sensor" msgstr "" -#: terrariumTranslations.py:81 +#: terrariumTranslations.py:86 msgid "Enter the time when the heater should be put on. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:82 +#: terrariumTranslations.py:87 msgid "Enter the time when the heater should be put off. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:83 +#: terrariumTranslations.py:88 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the heater. Select all needed switches below." msgstr "" -#: terrariumTranslations.py:84 terrariumTranslations.py:93 +#: terrariumTranslations.py:89 terrariumTranslations.py:98 msgid "Select the temperature sensors that are used to control the temperature. When selecting multiple sensors, the average is calculated to determine the final temperature." msgstr "" -#: terrariumTranslations.py:87 +#: terrariumTranslations.py:92 msgid "Enable or disable the cooler system. When enabled, you can make changes below. By disabling it will not loose the current settings. It will temporary stop the cooler system." msgstr "" -#: terrariumTranslations.py:88 +#: terrariumTranslations.py:93 msgid "Enable cooling when the lights are off. This can cause a very low temperature when the lights are off." msgstr "" -#: terrariumTranslations.py:90 +#: terrariumTranslations.py:95 msgid "Enter the time when the cooler should be put on. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:91 +#: terrariumTranslations.py:96 msgid "Enter the time when the cooler should be put off. Only available when running in '%s' mode." msgstr "" -#: terrariumTranslations.py:92 +#: terrariumTranslations.py:97 msgid "Select the power switches that should be toggled on the selected times above. Normally these are the switches connected to the cooler. Select all needed switches below." msgstr "" -#: terrariumTranslations.py:96 +#: terrariumTranslations.py:101 msgid "Choose your interface language." msgstr "" -#: terrariumTranslations.py:97 +#: terrariumTranslations.py:102 msgid "Holds the username which can make changes (Administrator)." msgstr "" -#: terrariumTranslations.py:98 +#: terrariumTranslations.py:103 msgid "Enter the new password for the administration user. Leaving empty will not change the password!" msgstr "" -#: terrariumTranslations.py:99 +#: terrariumTranslations.py:104 msgid "Enter the current password in order to change the password." msgstr "" -#: terrariumTranslations.py:100 +#: terrariumTranslations.py:105 msgid "Holds the amount of power in Wattage that the Raspberry PI uses including all USB equipment." msgstr "" -#: terrariumTranslations.py:101 +#: terrariumTranslations.py:106 msgid "Holds the amount of euro/dollar per 1 kW/h (1 Kilowatt per hour)." msgstr "" -#: terrariumTranslations.py:102 +#: terrariumTranslations.py:107 msgid "Holds the amount of euro/dollar per 1000 liters water." msgstr "" -#: terrariumTranslations.py:103 +#: terrariumTranslations.py:108 msgid "Choose the temperature indicator. The software will recalculate to the chosen indicator." msgstr "" -#: terrariumTranslations.py:104 +#: terrariumTranslations.py:109 msgid "Holds the host name or IP address on which the software will listen for connections. Enter :: for all addresses to bind." msgstr "" -#: terrariumTranslations.py:105 +#: terrariumTranslations.py:110 msgid "Holds the port number on which the software is listening for connections." msgstr "" -#: terrariumTranslations.py:106 +#: terrariumTranslations.py:111 msgid "Holds the port number on which the OWFS software is running. Leave empty to disable OWFS support." msgstr "" -#: terrariumTranslations.py:110 +#: terrariumTranslations.py:115 msgid "Holds the name of the animal." msgstr "" -#: terrariumTranslations.py:111 +#: terrariumTranslations.py:116 msgid "Holds the type of the animal" msgstr "" -#: terrariumTranslations.py:112 +#: terrariumTranslations.py:117 msgid "Holds the gender of the animal." msgstr "" -#: terrariumTranslations.py:113 +#: terrariumTranslations.py:118 msgid "Holds the day of birth of the animal." msgstr "" -#: terrariumTranslations.py:114 +#: terrariumTranslations.py:119 msgid "Holds the species name of the animal." msgstr "" -#: terrariumTranslations.py:115 +#: terrariumTranslations.py:120 msgid "Holds the latin name of the animal." msgstr "" -#: terrariumTranslations.py:116 +#: terrariumTranslations.py:121 msgid "Holds a small description about the animal." msgstr "" -#: terrariumTranslations.py:117 +#: terrariumTranslations.py:122 msgid "Holds a link to more information." msgstr "" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authenticate to make any changes" msgstr "" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 msgid "Authentication" msgstr "" -#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:92 -#: views/system_settings.tpl:23 +#: terrariumWebserver.py:78 terrariumWebserver.py:84 terrariumWebserver.py:90 +#: terrariumWebserver.py:98 views/system_settings.tpl:23 msgid "TerrariumPI" msgstr "" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Data could not be saved" msgstr "" -#: terrariumWebserver.py:153 +#: terrariumWebserver.py:159 msgid "Error!" msgstr "" -#: terrariumWebserver.py:161 +#: terrariumWebserver.py:167 msgid "Data saved" msgstr "" -#: terrariumWebserver.py:162 +#: terrariumWebserver.py:168 msgid "Your changes are saved" msgstr "" -#: terrariumWebserver.py:234 views/inc/menu.tpl:130 +#: terrariumWebserver.py:247 views/inc/menu.tpl:130 msgid "Log out" msgstr "" -#: terrariumWebserver.py:235 +#: terrariumWebserver.py:248 msgid "You are now logged out" msgstr "" @@ -366,13 +388,13 @@ msgstr "" #: views/dashboard.tpl:24 views/inc/usage_dashboard.tpl:93 #: views/inc/usage_switches.tpl:121 views/inc/usage_switches.tpl:143 -#: views/switch_settings.tpl:28 views/switch_settings.tpl:97 +#: views/switch_settings.tpl:28 views/switch_settings.tpl:99 msgid "Power usage in Watt" msgstr "" #: views/dashboard.tpl:35 views/inc/usage_dashboard.tpl:99 #: views/inc/usage_switches.tpl:125 views/inc/usage_switches.tpl:146 -#: views/switch_settings.tpl:31 views/switch_settings.tpl:101 +#: views/switch_settings.tpl:31 views/switch_settings.tpl:103 msgid "Water flow in L/m" msgstr "" @@ -516,7 +538,7 @@ msgstr "" #: views/dashboard.tpl:207 views/dashboard.tpl:259 views/door_status.tpl:16 #: views/inc/sensor_list.tpl:22 views/inc/usage_dashboard.tpl:67 -#: views/inc/usage_dashboard.tpl:180 views/inc/usage_doors.tpl:31 +#: views/inc/usage_dashboard.tpl:181 views/inc/usage_doors.tpl:31 #: views/inc/usage_sensors.tpl:35 views/inc/usage_switches.tpl:31 #: views/inc/usage_webcams.tpl:29 views/switch_status.tpl:17 #: views/system_status.tpl:15 views/system_status.tpl:71 @@ -525,7 +547,7 @@ msgid "Day" msgstr "" #: views/dashboard.tpl:210 views/dashboard.tpl:262 views/door_status.tpl:19 -#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:181 +#: views/inc/sensor_list.tpl:25 views/inc/usage_dashboard.tpl:182 #: views/inc/usage_doors.tpl:34 views/inc/usage_sensors.tpl:38 #: views/inc/usage_switches.tpl:34 views/inc/usage_webcams.tpl:32 #: views/switch_status.tpl:20 views/system_status.tpl:18 @@ -535,7 +557,7 @@ msgid "Week" msgstr "" #: views/dashboard.tpl:213 views/dashboard.tpl:265 views/door_status.tpl:22 -#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:182 +#: views/inc/sensor_list.tpl:28 views/inc/usage_dashboard.tpl:183 #: views/inc/usage_doors.tpl:37 views/inc/usage_sensors.tpl:41 #: views/inc/usage_switches.tpl:37 views/inc/usage_webcams.tpl:35 #: views/switch_status.tpl:23 views/system_status.tpl:21 @@ -545,7 +567,7 @@ msgid "Month" msgstr "" #: views/dashboard.tpl:216 views/dashboard.tpl:268 views/door_status.tpl:25 -#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:183 +#: views/inc/sensor_list.tpl:31 views/inc/usage_dashboard.tpl:184 #: views/inc/usage_doors.tpl:40 views/inc/usage_sensors.tpl:44 #: views/inc/usage_switches.tpl:40 views/inc/usage_webcams.tpl:38 #: views/switch_status.tpl:26 views/system_status.tpl:24 @@ -582,7 +604,7 @@ msgstr "" #: views/inc/usage_sensors.tpl:162 views/inc/usage_switches.tpl:103 #: views/inc/usage_switches.tpl:134 views/sensor_settings.tpl:19 #: views/sensor_settings.tpl:87 views/switch_settings.tpl:19 -#: views/switch_settings.tpl:78 +#: views/switch_settings.tpl:79 msgid "Hardware" msgstr "" @@ -591,7 +613,7 @@ msgstr "" #: views/inc/usage_sensors.tpl:120 views/inc/usage_sensors.tpl:165 #: views/inc/usage_switches.tpl:112 views/inc/usage_switches.tpl:137 #: views/sensor_settings.tpl:22 views/sensor_settings.tpl:99 -#: views/switch_settings.tpl:22 views/switch_settings.tpl:88 +#: views/switch_settings.tpl:22 views/switch_settings.tpl:90 msgid "Address" msgstr "" @@ -602,7 +624,7 @@ msgstr "" #: views/inc/usage_webcams.tpl:104 views/inc/usage_webcams.tpl:134 #: views/profile.tpl:56 views/sensor_settings.tpl:28 #: views/sensor_settings.tpl:113 views/switch_settings.tpl:25 -#: views/switch_settings.tpl:93 views/webcam_settings.tpl:27 +#: views/switch_settings.tpl:95 views/webcam_settings.tpl:27 #: views/webcam_settings.tpl:86 msgid "Name" msgstr "" @@ -629,19 +651,19 @@ msgstr "" #: views/door_settings.tpl:73 views/hardware.tpl:63 views/hardware.tpl:88 #: views/inc/usage_doors.tpl:101 views/inc/usage_switches.tpl:107 -#: views/switch_settings.tpl:82 +#: views/switch_settings.tpl:83 msgid "GPIO" msgstr "" #: views/door_settings.tpl:93 views/inc/usage_doors.tpl:70 #: views/inc/usage_sensors.tpl:80 views/inc/usage_switches.tpl:70 #: views/inc/usage_webcams.tpl:66 views/sensor_settings.tpl:142 -#: views/switch_settings.tpl:110 views/webcam_settings.tpl:108 +#: views/switch_settings.tpl:135 views/webcam_settings.tpl:108 msgid "Close" msgstr "" #: views/door_settings.tpl:94 views/sensor_settings.tpl:143 -#: views/switch_settings.tpl:111 views/webcam_settings.tpl:109 +#: views/switch_settings.tpl:136 views/webcam_settings.tpl:109 msgid "Add" msgstr "" @@ -663,7 +685,7 @@ msgstr "" msgid "Switches" msgstr "" -#: views/hardware.tpl:19 views/hardware.tpl:107 views/inc/menu.tpl:68 +#: views/hardware.tpl:19 views/hardware.tpl:116 views/inc/menu.tpl:68 #: views/usage.tpl:19 msgid "Doors" msgstr "" @@ -757,7 +779,7 @@ msgstr "" msgid "For every relay on the board there is need for a dedicated GPIO pin. For a 4 ports relay board there are 4 control GPIO pins and 2 pins for power and ground needed. This makes that it needs 6 GPIO pins in total." msgstr "" -#: views/hardware.tpl:90 views/hardware.tpl:99 +#: views/hardware.tpl:90 views/hardware.tpl:99 views/hardware.tpl:108 msgid "The following boards are tested" msgstr "" @@ -765,7 +787,7 @@ msgstr "" msgid "VMA400" msgstr "" -#: views/hardware.tpl:97 views/hardware.tpl:120 +#: views/hardware.tpl:97 views/hardware.tpl:129 msgid "USB" msgstr "" @@ -777,29 +799,41 @@ msgstr "" msgid "Denkovi" msgstr "" -#: views/hardware.tpl:108 +#: views/hardware.tpl:106 +msgid "PWM" +msgstr "" + +#: views/hardware.tpl:107 +msgid "With PWM controlled boards it is possible to support dimming devices." +msgstr "" + +#: views/hardware.tpl:110 +msgid "Nextevo" +msgstr "" + +#: views/hardware.tpl:117 msgid "TerrariumPI software has support for magnetic door sensors. Only versions with two wires are supported. Connect one wire to the ground and the other wire to any GPIO pin that is free (except power and ground pins)." msgstr "" -#: views/hardware.tpl:111 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 +#: views/hardware.tpl:120 views/inc/menu.tpl:79 views/inc/usage_webcams.tpl:19 #: views/usage.tpl:22 views/webcam.tpl:7 views/webcam_settings.tpl:64 msgid "Webcam" msgstr "" -#: views/hardware.tpl:112 +#: views/hardware.tpl:121 msgid "TerrariumPI software has support for different kind of cameras. The following cameras below are tested with TerrariumPI software." msgstr "" -#: views/hardware.tpl:115 views/inc/usage_webcams.tpl:128 +#: views/hardware.tpl:124 views/inc/usage_webcams.tpl:128 #: views/webcam_settings.tpl:21 msgid "RPICam" msgstr "" -#: views/hardware.tpl:116 +#: views/hardware.tpl:125 msgid "There are different Raspberry Pi cameras available. If the camera is Raspberry Pi compatible, it can be used with TerrariumPI software." msgstr "" -#: views/hardware.tpl:121 +#: views/hardware.tpl:130 msgid "All kind of USB cameras can be used. Enter physical path of the device like /dev/videoX." msgstr "" @@ -827,12 +861,12 @@ msgstr "" msgid "Forecast" msgstr "" -#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:158 +#: views/inc/menu.tpl:46 views/inc/usage_dashboard.tpl:159 #: views/inc/usage_sensors.tpl:128 views/sensor_settings.tpl:107 msgid "Temperature" msgstr "" -#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:157 +#: views/inc/menu.tpl:49 views/inc/usage_dashboard.tpl:158 #: views/inc/usage_sensors.tpl:129 views/sensor_settings.tpl:108 msgid "Humidity" msgstr "" @@ -1417,7 +1451,7 @@ msgstr "" msgid "On the switch settings page you can configure all needed switches. Click on %s button to add a new switch. And empty form like below is shown and has to be filled in. Make sure the right values are filled in. All fields with a %s are required." msgstr "" -#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:81 +#: views/inc/usage_switches.tpl:106 views/switch_settings.tpl:82 msgid "FTDI" msgstr "" @@ -1688,10 +1722,34 @@ msgstr "" msgid "Add new switch" msgstr "" -#: views/switch_settings.tpl:83 +#: views/switch_settings.tpl:84 msgid "GPIO Inverse" msgstr "" +#: views/switch_settings.tpl:85 +msgid "PWM Dimmer" +msgstr "" + +#: views/switch_settings.tpl:109 +msgid "Dimmer action duration" +msgstr "" + +#: views/switch_settings.tpl:113 +msgid "Dimmer on duration" +msgstr "" + +#: views/switch_settings.tpl:117 +msgid "Dimmer on percentage" +msgstr "" + +#: views/switch_settings.tpl:121 +msgid "Dimmer off duration" +msgstr "" + +#: views/switch_settings.tpl:125 +msgid "Dimmer off percentage" +msgstr "" + #: views/system_environment.tpl:16 msgid "Here you can configure your environment." msgstr "" @@ -1980,6 +2038,10 @@ msgstr "" msgid "Total open for" msgstr "" +#: Missing text string +msgid "Universal AC MAINS Dimmer - MPDMv4.1" +msgstr "" + #: Missing text string msgid "USB Relay board" msgstr "" diff --git a/start.sh b/start.sh index 7891977f9..63a889784 100755 --- a/start.sh +++ b/start.sh @@ -53,6 +53,9 @@ then sleep 1 done echo " restart!" + + # Restart PiGPIOd process.... + sudo service pigpiod restart done else WHOAMI=`whoami` diff --git a/static/css/terrariumpi.css b/static/css/terrariumpi.css index f43fbeda4..f78965f99 100644 --- a/static/css/terrariumpi.css +++ b/static/css/terrariumpi.css @@ -162,16 +162,23 @@ div.modal div.row.webcam{ text-align: center; font-size: 1.5em; display: inline; - margin: 0.15em 25%; } .power_switch span { cursor: pointer; } +.power_switch.dimmer { + display: block; + position: relative; +} + .power_switch.big, .fa.fa-lock.big { - font-size: 11em; + font-size: 12em; + display: block; + padding-top: 0.1em; + line-height: 1em; } .x_title .power_switch { diff --git a/static/images/documentation/AC_Dimmer_MPDMv4_1_3_Kit_top_1.jpg.2560x2560_q85.jpg b/static/images/documentation/AC_Dimmer_MPDMv4_1_3_Kit_top_1.jpg.2560x2560_q85.jpg new file mode 100644 index 000000000..51a7b8192 Binary files /dev/null and b/static/images/documentation/AC_Dimmer_MPDMv4_1_3_Kit_top_1.jpg.2560x2560_q85.jpg differ diff --git a/static/js/terrariumpi.js b/static/js/terrariumpi.js index 94ddc4150..afb70da64 100644 --- a/static/js/terrariumpi.js +++ b/static/js/terrariumpi.js @@ -351,18 +351,16 @@ function update_dashboard_power_usage(data) { update_dashboard_tile('power_wattage', formatNumber(data.current) + '/' + formatNumber(data.max)); var percentage = (data.max > 0 ? (data.current / data.max) * 100 : 0); $("#power_wattage .progress-bar-success").css('height', percentage + '%'); - data.total /= 1000; - $("#total_power .count_bottom .costs span").text(formatCurrency(data.price * data.total)); + $("#total_power .count_bottom .costs span").text(formatCurrency(data.price)); $("#total_power .count_bottom span.duration").text(moment.duration(data.duration * 1000).humanize()); - update_dashboard_tile('total_power',formatNumber(data.total)); + update_dashboard_tile('total_power',formatNumber(data.total / (3600 * 1000))); } function update_dashboard_water_flow(data) { update_dashboard_tile('water_flow', formatNumber(data.current) + '/' + formatNumber(data.max)); var percentage = (data.max > 0 ? (data.current / data.max) * 100 : 0); $("#water_flow .progress-bar-info").css('height', percentage + '%'); - data.total /= 1000; - $("#total_water .count_bottom .costs span").text(formatCurrency(data.price * data.total)); + $("#total_water .count_bottom .costs span").text(formatCurrency(data.price)); $("#total_water .count_bottom span.duration").text(moment.duration(data.duration * 1000).humanize()); update_dashboard_tile('total_water', formatNumber(data.total)); } @@ -827,13 +825,7 @@ function load_history_graph(id,type,data_url,nocache) { $.each(online_data, function(dummy, value) { $.each(value, function(dummy, data_array) { globals.graphs[id].timestamp = now; - if (type == 'switch') { - globals.graphs[id].data = process_switch_data(data_array); - } else if (type == 'door') { - globals.graphs[id].data = process_door_data(data_array); - } else { - globals.graphs[id].data = data_array; - } + globals.graphs[id].data = data_array; }); }); @@ -928,8 +920,8 @@ function history_graph(name, data, type) { }; switch (type) { - case 'temperature': case 'humidity': + case 'temperature': graph_data = [{ label: '{{_('Current')}}', data: data.current @@ -941,6 +933,7 @@ function history_graph(name, data, type) { data: data.alarm_max }]; break; + case 'weather': case 'system_temperature': graph_data = [{ @@ -948,6 +941,7 @@ function history_graph(name, data, type) { data: data }]; break; + case 'system_uptime': delete(graph_options.series.curvedLines); graph_options.series.lines = { @@ -963,6 +957,7 @@ function history_graph(name, data, type) { $('div.row.uptime .x_title small').text(moment.duration(data[data.length-1][1] * 1000).humanize()); break; + case 'system_load': graph_data = [{ label: '{{_('Load')}}', @@ -975,6 +970,7 @@ function history_graph(name, data, type) { data: data.load15 }]; break; + case 'system_memory': graph_data = [{ label: '{{_('Used memory')}}', @@ -987,6 +983,7 @@ function history_graph(name, data, type) { data: data.total }]; break; + case 'switch': delete(graph_options.series.curvedLines); graph_options.series.lines = { @@ -994,16 +991,16 @@ function history_graph(name, data, type) { lineWidth: 2, fill: true }; - - graph_data = [data.power_wattage, data.water_flow]; + graph_options.yaxis.min = 0; graph_data = [{ label: '{{_('Power usage in Watt')}}', data: data.power_wattage }, { label: '{{_('Water flow in L/m')}}', - data: data.water_flow + data: data.water_flow, }]; break; + case 'door': delete(graph_options.series.curvedLines); graph_options.series.lines = { @@ -1011,15 +1008,15 @@ function history_graph(name, data, type) { lineWidth: 2, fill: true }; - - graph_data = [data.state]; + graph_options.yaxis.min = 0; + graph_options.yaxis.max = 1; graph_data = [{ label: '{{_('Door status')}}', data: data.state }]; break; - } + if (graph_data[0].data != undefined && graph_data[0].data.length > 0) { var total_data_duration = (graph_data[0].data[graph_data[0].data.length - 1][0] - graph_data[0].data[0][0]) / 3600000; graph_options.xaxis.tickSize[0] = Math.round(total_data_duration * 2.5); @@ -1031,17 +1028,22 @@ function history_graph(name, data, type) { if (type == 'switch') { var usage = ''; - if (data.total_power_usage > 0) { - usage = '{{_('Total power in kWh')}}: ' + formatNumber(data.total_power_usage); - } - if (data.total_water_usage > 0) { - usage += (usage != '' ? ' - ' : '') + '{{_('Total water in L')}}: ' + formatNumber(data.total_water_usage); + if (data.totals !== undefined) { + if (data.totals.power_wattage.duration > 0) { + usage = '{{_('Duration')}}: ' + moment.duration(data.totals.power_wattage.duration * 1000).humanize() + } + if (data.totals.power_wattage.wattage > 0) { + usage += (usage != '' ? ' - ' : '') + '{{_('Total power in kWh')}}: ' + formatNumber(data.totals.power_wattage.wattage / (3600 * 1000)); + } + if (data.totals.water_flow.water > 0) { + usage += (usage != '' ? ' - ' : '') + '{{_('Total water in L')}}: ' + formatNumber(data.totals.water_flow.water); + } } $('#' + name + ' .total_usage').text(usage); } else if (type == 'door') { var usage = ''; - if (data.open > 0) { - usage = '{{_('Total open for')}}: ' + moment.duration(data.open).humanize(); + if (data.totals !== undefined) { + usage = '{{_('Total open for')}}: ' + moment.duration(data.totals.duration).humanize(); } $('#' + name + ' .total_usage').text(usage); } @@ -1119,12 +1121,18 @@ function add_switch() { form.find('input[name="switch_[nr]_address"]').val(), form.find('input[name="switch_[nr]_name"]').val(), form.find('input[name="switch_[nr]_power_wattage"]').val(), - form.find('input[name="switch_[nr]_water_flow"]').val()); + form.find('input[name="switch_[nr]_water_flow"]').val(), + form.find('input[name="switch_[nr]_dimmer_duration"]').val(), + form.find('input[name="switch_[nr]_dimmer_on_duration"]').val(), + form.find('input[name="switch_[nr]_dimmer_on_percentage"]').val(), + form.find('input[name="switch_[nr]_dimmer_off_duration"]').val(), + form.find('input[name="switch_[nr]_dimmer_off_percentage"]').val(), + ); $('.new-switch-form').modal('hide'); } -function add_switch_row(id,hardwaretype,address,name,power_wattage,water_flow) { +function add_switch_row(id,hardwaretype,address,name,power_wattage,water_flow, dimmer_duration,dimmer_on_duration,dimmer_on_percentage,dimmer_off_duration,dimmer_off_percentage) { var switch_row = $($('.modal-body div.row.switch').parent().clone().html().replace(/\[nr\]/g, $('form div.row.switch').length)); switch_row.find('div.power_switch.small').attr('id','switch_' + id); @@ -1132,7 +1140,7 @@ function add_switch_row(id,hardwaretype,address,name,power_wattage,water_flow) { switch_row.find('.x_title').show().find('h2 small').text(name); switch_row.find('span.select2.select2-container').remove(); - switch_row.find('input, select').each(function(counter,item){ + switch_row.find('input, select').each(function(counter,item) { $(item).val(eval($(item).attr('name').replace(/switch_[0-9]+_/g,''))); }); @@ -1143,7 +1151,10 @@ function add_switch_row(id,hardwaretype,address,name,power_wattage,water_flow) { placeholder: '{{_('Select an option')}}', allowClear: false, minimumResultsForSearch: Infinity + }).on('change',function() { + switch_row.find('.row.dimmer').toggle(this.value === 'pwm-dimmer'); }); + switch_row.find('.row.dimmer').toggle(hardwaretype === 'pwm-dimmer'); } function add_door() { @@ -1216,128 +1227,39 @@ function add_webcam_row(id,location,name,rotation,preview) { function update_power_switch(id, data) { var power_switch = $('#switch_' + id); - power_switch.find('h2 span.title').text('{{_('Switch')}} ' + data.name); - power_switch.find('h2 small.data_update').text(formatNumber(data.power_wattage) + 'W' + (data.water_flow > 0 ? ' - ' + formatNumber(data.water_flow) + 'L/m' : '')); - power_switch.find('span.glyphicon').removeClass('blue green').addClass((data.state ? 'green' : 'blue')).attr('title','{{_('Toggle power switch')}}'); -} - -function toggleSwitch(id) { - id = id.split('_')[1]; - $.getJSON('/api/switch/toggle/' + id,function(data){ - }); -} - -function process_graph_data(type, raw_data) { - var graphdata = {} - switch (type) { - case 'door': - graphdata.state = []; - graphdata.open = 0; - break; - case 'switch': - graphdata.power_wattage = []; - graphdata.water_flow = []; - graphdata.total_power_usage = 0; - graphdata.total_water_usage = 0; - break; + var update_data = ''; + if (data.hardwaretype === 'pwm-dimmer') { + update_data = formatNumber(data.current_power_wattage) + 'W / ' + } + update_data += formatNumber(data.power_wattage) + 'W'; + if (data.water_flow > 0) { + update_data += ' - ' + formatNumber(data.water_flow) + 'L/m'; } - var state_change = -1; - $.each(raw_data.state, function(counter, status) { - // Sanitize input - switch (type) { - case 'door': - status[1] = (status[1] === 'closed' ? 0 : 1) - break; - case 'switch': - if (!status[1]) { - raw_data.power_wattage[counter][1] = 0; - raw_data.water_flow[counter][1] = 0; - } - break; - } + power_switch.find('h2 span.title').text('{{_('Switch')}} ' + data.name); + power_switch.find('h2 small.data_update').text(update_data); - if (state_change != status[1]) { - // Copy previous object to get the right status with current timestamp - var copy = []; - if (counter > 0) { - $.each(graphdata, function(name,data){ - if (typeof graphdata[name] == 'object' ) { - copy = $.extend(true, [], raw_data[name][counter-1]); - // If turned down/off/closed, calculate usage, else it is zero! - var usage = (copy[1] != 0 ? (status[0] - copy[0]) / 1000 * copy[1] : 0); - switch (name) { - case 'state': - graphdata.open += usage; - break; - case 'power_wattage': - graphdata.total_power_usage += usage; - break; - case 'water_flow': - graphdata.total_water_usage += usage; - break; - } - copy[0] = status[0]; - graphdata[name].push(copy); - } - }); - } else if (counter == 0 && status[1] == 1 && type == 'door') { - // If starting with status up/on add a status down/off first for nice graphing - $.each(graphdata, function(name,data){ - if (typeof graphdata[name] == 'object' ) { - copy = $.extend(true, [], raw_data[name][counter]); - // If turned down/off/closed, calculate usage, else it is zero! - var usage = (copy[1] != 0 ? (status[0] - copy[0]) / 1000 * copy[1] : 0); - switch (name) { - case 'state': - graphdata.open += usage; - break; - case 'power_wattage': - graphdata.total_power_usage += usage; - break; - case 'water_flow': - graphdata.total_water_usage += usage; - break; - } - copy[1] = 0; - graphdata[name].push(copy); - } + if (data.hardwaretype === 'pwm-dimmer') { + power_switch.find('div.power_switch').removeClass('big').addClass('dimmer').html(''); + + power_switch.find('.knob').knob({ + release: function(value) { + $.getJSON('/api/switch/state/' + id + '/' + value,function(dummy){ }); - } - state_change = status[1]; - } - $.each(graphdata, function(name,data){ - if (typeof graphdata[name] == 'object' ) { - graphdata[name].push(raw_data[name][counter]); + }, + format: function(value) { + return value + '%'; } }); - }); - // Add end data to now... and a startdate of 24 hours ago if needed - var now = new Date().getTime(); - var start = now - (24 * 60 * 60 * 1000); - $.each(graphdata, function(name,data){ - if (typeof graphdata[name] == 'object' ) { - graphdata[name].push([now,graphdata[name][graphdata[name].length-1][1]]); - // Add begin timestamp 24 hours back if needed - if (graphdata[name][0][0] > start) { - graphdata[name].unshift([start,graphdata[name][0][1]]); - } - } - }); - if (type === 'switch') { - graphdata.total_power_usage /= 3600; // To kWh - graphdata.total_water_usage /= 60; // To liters + data.state = data.state > data.dimmer_off_percentage } - // Return data - return graphdata; -} - -function process_switch_data(raw_data) { - return process_graph_data('switch', raw_data); + power_switch.find('span.glyphicon').removeClass('blue green').addClass((data.state ? 'green' : 'blue')); } -function process_door_data(raw_data) { - return process_graph_data('door', raw_data); +function toggleSwitch(id) { + id = id.split('_')[1]; + $.getJSON('/api/switch/toggle/' + id,function(data){ + }); } function update_webcam_preview(name, url) { @@ -1388,7 +1310,7 @@ function createWebcamLayer(webcamid, maxzoom) { function load_door_history() { $.getJSON('/api/history/doors', function(door_data) { var door_status = {}; - $.each(door_data.door, function(counter, statedata) { + $.each(door_data.doors, function(counter, statedata) { for (var i = 0; i < statedata.state.length; i++) { if (i == 0 || statedata.state[i][1] != statedata.state[i-1][1]) { door_status[statedata.state[i][0]] = statedata.state[i][1]; @@ -1397,7 +1319,7 @@ function load_door_history() { }); // Sort door data events on time. Needed if you have more than one door $.each(Object.keys(door_status).sort(), function(counter,change_time) { - update_door_messages((door_status[change_time] == 'open'), change_time); + update_door_messages((door_status[change_time] === 1), change_time); }) }); } @@ -1434,9 +1356,6 @@ function version_check() { } function init_wysiwyg() { - - if (typeof ($.fn.wysiwyg) === 'undefined') { return; } - function init_ToolbarBootstrapBindings() { var fonts = [ 'Serif', 'Sans', 'Arial', 'Arial Black', 'Courier', diff --git a/terrariumCollector.py b/terrariumCollector.py index b52a01c52..b5d8bf004 100644 --- a/terrariumCollector.py +++ b/terrariumCollector.py @@ -9,21 +9,21 @@ logger = logging.getLogger(__name__) class terrariumCollector(): - database = 'history.db' + DATABASE = 'history.db' + # Store data every Xth minute. Except switches and doors + STORE_MODULO = 1 * 60 def __init__(self): - logger.info('Setting up collector database %s' % (terrariumCollector.database,)) + logger.info('Setting up collector database %s' % (terrariumCollector.DATABASE,)) self.__recovery = False self.__connect() - # Store data every Xth minute. Except switches. - self.modulo = 5 * 60 self.__create_database_structure() logger.info('TerrariumPI Collecter is ready') def __connect(self): - self.db = sqlite3.connect(terrariumCollector.database) + self.db = sqlite3.connect(terrariumCollector.DATABASE) self.db.row_factory = sqlite3.Row - logger.info('Database connection created to database %s' % (terrariumCollector.database,)) + logger.info('Database connection created to database %s' % (terrariumCollector.DATABASE,)) def __create_database_structure(self): with self.db: @@ -96,53 +96,12 @@ def __create_database_structure(self): self.db.commit() - def __log_data(self,type,id,newdata): - if self.__recovery: - logger.warn('TerrariumPI Collecter is in recovery mode. Cannot store new logging data!') - return - - now = int(time.time()) - rows = [] - if 'switches' != type and 'door' != type: - now -= (now % 60) - - try: - with self.db: - cur = self.db.cursor() - - if type in ['humidity','temperature']: - cur.execute('REPLACE INTO sensor_data (id, type, timestamp, current, limit_min, limit_max, alarm_min, alarm_max, alarm) VALUES (?,?,?,?,?,?,?,?,?)', - (id, type, now, newdata['current'], newdata['limit_min'], newdata['limit_max'], newdata['alarm_min'], newdata['alarm_max'], newdata['alarm'])) - - if type in ['switches']: - if 'time' in newdata: - now = newdata['time'] - cur.execute('REPLACE INTO switch_data (id, timestamp, state, power_wattage, water_flow) VALUES (?,?,?,?,?)', - (id, now, newdata['state'], newdata['power_wattage'], newdata['water_flow'])) - - if type in ['door']: - cur.execute('REPLACE INTO door_data (id, timestamp, state) VALUES (?,?,?)', - (id, now, newdata)) - - if type in ['weather']: - cur.execute('REPLACE INTO weather_data (timestamp, wind_speed, temperature, pressure, wind_direction, weather, icon) VALUES (?,?,?,?,?,?,?)', - (now, newdata['wind_speed'], newdata['temperature'], newdata['pressure'], newdata['wind_direction'], newdata['weather'], newdata['icon'])) - - if type in ['system']: - cur.execute('REPLACE INTO system_data (timestamp, load_load1, load_load5, load_load15, uptime, temperature, cores, memory_total, memory_used, memory_free) VALUES (?,?,?,?,?,?,?,?,?,?)', - (now, newdata['load']['load1'], newdata['load']['load5'], newdata['load']['load15'], newdata['uptime'], newdata['temperature'], newdata['cores'], newdata['memory']['total'], newdata['memory']['used'], newdata['memory']['free'])) - - self.db.commit() - except sqlite3.DatabaseError as ex: - logger.error('TerrariumPI Collecter exception! %s', (ex,)) - if 'database disk image is malformed' == str(ex): - self.__recover() - def __recover(self): + starttime = time.time() # Based on: http://www.dosomethinghere.com/2013/02/20/fixing-the-sqlite-error-the-database-disk-image-is-malformed/ # Enable recovery status self.__recovery = True - logger.warn('TTerrariumPI Collecter recovery mode is starting! %s', (self.__recovery,)) + logger.warn('TerrariumPI Collecter recovery mode is starting! %s', (self.__recovery,)) # Create empty sql dump variable sqldump = '' @@ -157,145 +116,177 @@ def __recover(self): logger.warn('TerrariumPI Collecter recovery mode created SQL dump of %s lines and %s bytes!', (lines,strlen(sqldump),)) # Delete broken db - os.remove(terrariumCollector.database) - logger.warn('TerrariumPI Collecter recovery mode deleted faulty database from disk %s', (terrariumCollector.database,)) + os.remove(terrariumCollector.DATABASE) + logger.warn('TerrariumPI Collecter recovery mode deleted faulty database from disk %s', (terrariumCollector.DATABASE,)) # Reconnect will recreate the db - logger.warn('TerrariumPI Collecter recovery mode starts reconnecting database to create a new clean database at %s', (terrariumCollector.database,)) + logger.warn('TerrariumPI Collecter recovery mode starts reconnecting database to create a new clean database at %s', (terrariumCollector.DATABASE,)) self.__connect() cur = self.db.cursor() # Load the SQL data back to db cur.executescript(sqldump) - logger.warn('TerrariumPI Collecter recovery mode restored the old data in a new database. %s', (terrariumCollector.database,)) + logger.warn('TerrariumPI Collecter recovery mode restored the old data in a new database. %s', (terrariumCollector.DATABASE,)) # Return to normal mode self.__recovery = False - logger.warn('TerrariumPI Collecter recovery mode is finished! %s', (self.__recovery,)) - - def log_switch_data(self,switch): - switch_id = switch['id'] - del(switch['id']) - del(switch['hardwaretype']) - del(switch['address']) - del(switch['name']) - - # Create new object for the previous state. Copy data and invert boolean value - if 'init' not in switch: - old_swich = switch.copy() - old_swich['state'] = not switch['state'] - old_swich['time'] = int(time.time()) - 1 - self.__log_data('switches',switch_id,old_swich) - else: - del(switch['init']) - - self.__log_data('switches',switch_id,switch) - - def log_door_data(self,door): - self.__log_data('door',door['id'], door['state']) - - def log_weather_data(self,weather): - del(weather['from']) - del(weather['to']) - self.__log_data('weather',None,weather) - - def log_sensor_data(self,sensor): - sensor_data = sensor.get_data() - del(sensor_data['id']) - del(sensor_data['hardwaretype']) - del(sensor_data['address']) - del(sensor_data['type']) - del(sensor_data['name']) - self.__log_data(sensor.get_type(),sensor.get_id(),sensor_data) - - def log_system_data(self, data): - self.__log_data('system',None,data) + logger.warn('TerrariumPI Collecter recovery mode is finished in %s seconds!', (time.time()-starttime,)) - def log_total_power_and_water_usage(self,pi_wattage): + def __log_data(self,type,id,newdata): if self.__recovery: - logger.warn('TerrariumPI Collecter is in recovery mode. Cannot store new power and water total usage!') + logger.warn('TerrariumPI Collecter is in recovery mode. Cannot store new logging data!') return - today = int(time.time()) - time_past = today % 86400 - today -= time_past - data = {'day' : today, 'on': 0, 'power' : time_past * pi_wattage, 'water' : 0} - - sql = '''SELECT - switch_data_duration.id, - switch_data_duration.timestamp AS aan, - switch_data.timestamp AS uit, - switch_data.power_wattage, - switch_data.water_flow , - switch_data.timestamp - max(switch_data_duration.timestamp) AS duration - FROM switch_data left JOIN switch_data as switch_data_duration - ON switch_data.id = switch_data_duration.id - AND switch_data.timestamp > switch_data_duration.timestamp - AND switch_data_duration.id <> 'total' - WHERE switch_data_duration.id NOT null - AND switch_data.id <> 'total' - AND switch_data.timestamp >= ? - AND switch_data_duration.timestamp < ? - GROUP BY switch_data.id, switch_data.timestamp - HAVING switch_data_duration.state = 1 - AND switch_data.timestamp >= ? - AND switch_data_duration.timestamp < ? - ORDER BY aan ASC''' - - filters = (today,today+86400,today,today+86400,) + now = int(time.time()) rows = [] + if type not in ['switches','door']: + now -= (now % terrariumCollector.STORE_MODULO) + try: with self.db: cur = self.db.cursor() - cur.execute(sql, filters) - rows = cur.fetchall() - except sqlite3.DatabaseError as ex: - logger.error('TerrariumPI Collecter exception! %s', (ex,)) - if 'database disk image is malformed' == str(ex): - self.__recover() - for row_tmp in rows: - row = {'aan': row_tmp['aan'], - 'uit':row_tmp['uit'], - 'duration' : row_tmp['duration'], - 'power_wattage' : row_tmp['power_wattage'], - 'water_flow' : row_tmp['water_flow']} + if type in ['humidity','temperature']: + cur.execute('REPLACE INTO sensor_data (id, type, timestamp, current, limit_min, limit_max, alarm_min, alarm_max, alarm) VALUES (?,?,?,?,?,?,?,?,?)', + (id, type, now, newdata['current'], newdata['limit_min'], newdata['limit_max'], newdata['alarm_min'], newdata['alarm_max'], newdata['alarm'])) + + if type in ['weather']: + cur.execute('REPLACE INTO weather_data (timestamp, wind_speed, temperature, pressure, wind_direction, weather, icon) VALUES (?,?,?,?,?,?,?)', + (now, newdata['wind_speed'], newdata['temperature'], newdata['pressure'], newdata['wind_direction'], newdata['weather'], newdata['icon'])) + + if type in ['system']: + cur.execute('REPLACE INTO system_data (timestamp, load_load1, load_load5, load_load15, uptime, temperature, cores, memory_total, memory_used, memory_free) VALUES (?,?,?,?,?,?,?,?,?,?)', + (now, newdata['load']['load1'], newdata['load']['load5'], newdata['load']['load15'], newdata['uptime'], newdata['temperature'], newdata['cores'], newdata['memory']['total'], newdata['memory']['used'], newdata['memory']['free'])) - if row['aan'] < today: - row['duration'] -= today - row['aan'] - row['aan'] = today + if type in ['switches']: + if 'time' in newdata: + now = newdata['time'] - if row['uit'] > today+86400: - row['duration'] -= row['uit'] - (today+86400) - row['uit'] = today+86400 + # Make a duplicate of last state and save it with 1 sec back in time to smooth the graphs + cur.execute('''REPLACE INTO switch_data (id,timestamp,state,power_wattage,water_flow) + SELECT id, ? as curtimestamp,state,power_wattage,water_flow + FROM switch_data + WHERE id = ? ORDER BY timestamp DESC LIMIT 1''', (now-1, id)) - data['on'] += row['duration'] - data['power'] += row['duration'] * row['power_wattage'] - data['water'] += row['duration'] * row['water_flow'] + cur.execute('REPLACE INTO switch_data (id, timestamp, state, power_wattage, water_flow) VALUES (?,?,?,?,?)', + (id, now, newdata['state'], newdata['power_wattage'], newdata['water_flow'])) - # Power is in Wh (Watt/hour) so devide by 3600 seconds - data['power'] /= 3600 - # Water is in Liters - data['water'] /= 60 + if type in ['door']: + # Make a duplicate of last state and save it with 1 sec back in time to smooth the graphs + cur.execute('''REPLACE INTO door_data (id,timestamp,state) + SELECT id, ? as curtimestamp,state + FROM door_data + WHERE id = ? ORDER BY timestamp DESC LIMIT 1''', (now-1, id)) + + cur.execute('REPLACE INTO door_data (id, timestamp, state) VALUES (?,?,?)', + (id, now, newdata)) - try: - with self.db: - cur = self.db.cursor() - cur.execute('REPLACE INTO switch_data (id, timestamp, state, power_wattage, water_flow) VALUES (?,?,?,?,?)', - ('total', today, data['on'], data['power'], data['water'])) self.db.commit() except sqlite3.DatabaseError as ex: logger.error('TerrariumPI Collecter exception! %s', (ex,)) if 'database disk image is malformed' == str(ex): self.__recover() + def __calculate_power_and_water_usage(self,history): + if 'switches' not in history: + return + + now = int(time.time()) * 1000 + for switchid in history['switches']: + # First add a new element to all the data arrays with the current timestamp. This is needed for: + # - Better power usage calculation + # - Better graphs in the interface + history['switches'][switchid]['power_wattage'].append([now,history['switches'][switchid]['power_wattage'][-1][1]]) + history['switches'][switchid]['water_flow'].append([now,history['switches'][switchid]['water_flow'][-1][1]]) + history['switches'][switchid]['state'].append([now,history['switches'][switchid]['state'][-1][1]]) + + totals = {'power_wattage' : {'duration' : 0.0 , 'wattage' : 0.0, 'price' : 0.0}, + 'water_flow' : {'duration' : 0.0 , 'water' : 0.0, 'price' : 0.0}} + power_on_time = None + for counter,state in enumerate(history['switches'][switchid]['state']): + if state[1] > 0 and power_on_time is None: # Power went on! The value could be variable from zero to 100. Above zero is 'on' + power_on_time = counter + elif power_on_time is not None: # Now check if the power went off, or put on a second time... + power_wattage_start = history['switches'][switchid]['power_wattage'][power_on_time][1] * (history['switches'][switchid]['state'][power_on_time][1] / 100.0) + power_wattage_end = history['switches'][switchid]['power_wattage'][counter][1] * (state[1] / 100.0) + power_wattage = (power_wattage_start + power_wattage_end) / 2.0 + + water_flow_start = history['switches'][switchid]['water_flow'][power_on_time][1] * (history['switches'][switchid]['state'][power_on_time][1] / 100.0) + water_flow_end = history['switches'][switchid]['water_flow'][counter][1] * (state[1] / 100.0) + water_flow = (water_flow_start + water_flow_end) / 2.0 + + duration = (state[0] - history['switches'][switchid]['state'][power_on_time][0]) / 1000.0 # Devide by 1000 because history is using Javascript timestamps + + totals['power_wattage']['duration'] += duration + totals['power_wattage']['wattage'] += (duration * power_wattage) + + totals['water_flow']['duration'] += duration + totals['water_flow']['water'] += (duration * (water_flow / 60)) # Water flow is in Liter per minute. So devide by 60 to get per seconds + + if state[1] == 0: + power_on_time = None # Power went down. Reset so we can measure new period + else: + power_on_time = counter # Change in power useage (dimmer) + + # Here we change the wattage and water flow to zero if the switch was off. This is needed for drawing the right graphs + if state[1] == 0: + history['switches'][switchid]['power_wattage'][counter][1] = 0 + history['switches'][switchid]['water_flow'][counter][1] = 0 + else: + history['switches'][switchid]['power_wattage'][counter][1] *= (state[1] / 100.0) + history['switches'][switchid]['water_flow'][counter][1] *= (state[1] / 100.0) + + history['switches'][switchid]['totals'] = totals + + def __calculate_door_usage(self,history): + if 'doors' not in history: + return + + now = int(time.time()) * 1000 + for doorid in history['doors']: + history['doors'][doorid]['state'].append([now,history['doors'][doorid]['state'][-1][1]]) + + totals = {'duration': 0} + door_open_on_time = None + for counter,state in enumerate(history['doors'][doorid]['state']): + if state[1] != 'closed' and door_open_on_time is None: # Door went open! + door_open_on_time = counter + elif state[1] == 'closed' and door_open_on_time is not None: # Door is closed. Calc period and data + totals['duration'] += (state[0] - history['doors'][doorid]['state'][door_open_on_time][0]) / 1000.0 # Devide by 1000 because history is using Javascript timestamps + door_open_on_time = None # Reset so we can measure new period + + # Here we translate closed to zero and open to one. Else the graphs will not work + history['doors'][doorid]['state'][counter][1] = (0 if state[1] == 'closed' else 1) + + history['doors'][doorid]['totals'] = totals + + def log_switch_data(self,data): + if data['hardwaretype'] != 'pwm-dimmer': + # Store normal switches with value 100 indicating full power (aka no dimming) + data['state'] = (100 if data['state'] == 1 else 0) + + self.__log_data('switches',data['id'],data) + + def log_door_data(self,data): + self.__log_data('door',data['id'], data['state']) + + def log_weather_data(self,data): + self.__log_data('weather',None,data) + + def log_sensor_data(self,data): + self.__log_data(data['type'],data['id'],data) + + def log_system_data(self, data): + self.__log_data('system',None,data) + def get_history(self, parameters = [], starttime = None, stoptime = None): # Default return object + timer = time.time() history = {} periods = {'day' : 1 * 24, 'week' : 7 * 24, 'month' : 30 * 24, 'year' : 365 * 24} - modulo = self.modulo + modulo = terrariumCollector.STORE_MODULO logtype = parameters[0] del(parameters[0]) @@ -310,20 +301,22 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): if len(parameters) > 0 and parameters[-1] in periods.keys(): stoptime = starttime - periods[parameters[-1]] * 60 * 60 - modulo = (periods[parameters[-1]] / 24) * self.modulo + modulo = (periods[parameters[-1]] / 24) * terrariumCollector.STORE_MODULO del(parameters[-1]) sql = '' filters = (stoptime,starttime,) if logtype == 'sensors': fields = { 'current' : [], 'alarm_min' : [], 'alarm_max' : [] , 'limit_min' : [], 'limit_max' : []} - sql = 'SELECT id, type, timestamp,' + ', '.join(fields.keys()) + ' FROM sensor_data WHERE timestamp >= ? and timestamp <= ? AND timestamp % ' + str(modulo) + ' = 0' + sql = 'SELECT id, type, timestamp,' + ', '.join(fields.keys()) + ' FROM sensor_data WHERE timestamp >= ? and timestamp <= ?' + #AND timestamp % ' + str(modulo) + ' = 0' if len(parameters) > 0 and parameters[0] == 'average': sql = 'SELECT "average" as id, type, timestamp' for field in fields: sql = sql + ', AVG(' + field + ') as ' + field - sql = sql + ' FROM sensor_data WHERE timestamp >= ? and timestamp <= ? AND timestamp % ' + str(modulo) + ' = 0' + sql = sql + ' FROM sensor_data WHERE timestamp >= ? and timestamp <= ?' + #AND timestamp % ' + str(modulo) + ' = 0' if len(parameters) == 2: sql = sql + ' and type = ?' @@ -345,25 +338,13 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): elif logtype == 'switches': fields = { 'power_wattage' : [], 'water_flow' : [] , 'state' : []} sql = 'SELECT id, "switches" as type, timestamp, ' + ', '.join(fields.keys()) + ' FROM switch_data WHERE timestamp >= ? and timestamp <= ? ' - if len(parameters) > 0 and parameters[0] == 'summary': - fields = ['total_power', 'total_water', 'duration'] - filters = ('total',) - # Temporary overrule.... :P - sql = ''' - SELECT ''' + str(stoptime) + ''' as timestamp, - IFNULL(MAX(timestamp) - MIN(timestamp),0) as duration, - IFNULL(SUM(power_wattage),0) as total_power, - IFNULL(SUM(water_flow),0) as total_water - FROM switch_data - WHERE id = ? ''' - - elif len(parameters) > 0 and parameters[0] is not None: + if len(parameters) > 0 and parameters[0] is not None: sql = sql + ' and id = ?' filters = (stoptime,starttime,parameters[0],) elif logtype == 'doors': fields = { 'state' : []} - sql = 'SELECT id, "door" as type, timestamp, ' + ', '.join(fields.keys()) + ' FROM door_data WHERE timestamp >= ? and timestamp <= ? ' + sql = 'SELECT id, "doors" as type, timestamp, ' + ', '.join(fields.keys()) + ' FROM door_data WHERE timestamp >= ? and timestamp <= ? ' if len(parameters) > 0 and parameters[0] is not None: sql = sql + ' and id = ?' @@ -372,7 +353,9 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): elif logtype == 'weather': fields = { 'wind_speed' : [], 'temperature' : [], 'pressure' : [] , 'wind_direction' : [], 'rain' : [], 'weather' : [], 'icon' : []} - sql = 'SELECT "city" as id, "weather" as type, timestamp, ' + ', '.join(fields.keys()) + ' FROM weather_data WHERE timestamp >= ? and timestamp <= ? AND timestamp % ' + str(modulo) + ' = 0' + sql = 'SELECT "city" as id, "weather" as type, timestamp, ' + ', '.join(fields.keys()) + ' FROM weather_data WHERE timestamp >= ? and timestamp <= ?' +# AND timestamp % ' +# + str(modulo) + ' = 0' elif logtype == 'system': fields = ['load_load1', 'load_load5','load_load15','uptime', 'temperature','cores', 'memory_total', 'memory_used' , 'memory_free'] @@ -388,7 +371,8 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): elif len(parameters) > 0 and parameters[0] == 'memory': fields = ['memory_total', 'memory_used' , 'memory_free'] - sql = 'SELECT "system" as type, timestamp, ' + ', '.join(fields) + ' FROM system_data WHERE timestamp >= ? and timestamp <= ? AND timestamp % ' + str(modulo) + ' = 0' + sql = 'SELECT "system" as type, timestamp, ' + ', '.join(fields) + ' FROM system_data WHERE timestamp >= ? and timestamp <= ?' + #AND timestamp % ' + str(modulo) + ' = 0' sql = sql + ' ORDER BY timestamp ASC' @@ -399,6 +383,7 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): cur = self.db.cursor() cur.execute(sql, filters) rows = cur.fetchall() + logger.debug('TerrariumPI Collecter history query: %s seconds, %s records -> %s, %s' % (time.time()-timer,len(rows),sql,filters)) except sqlite3.DatabaseError as ex: logger.error('TerrariumPI Collecter exception! %s', (ex,)) if 'database disk image is malformed' == str(ex): @@ -435,4 +420,9 @@ def get_history(self, parameters = [], starttime = None, stoptime = None): for field in fields: history[row['type']][row['id']][field].append([row['timestamp'] * 1000,row[field]]) + if logtype == 'switches': + self.__calculate_power_and_water_usage(history) + elif logtype == 'doors': + self.__calculate_door_usage(history) + return history diff --git a/terrariumConfig.py b/terrariumConfig.py index 7e3c1ec2a..70f6acc32 100644 --- a/terrariumConfig.py +++ b/terrariumConfig.py @@ -291,6 +291,28 @@ def save_power_switch(self,data): if 'state' in data: del(data['state']) + if 'current_power_wattage' in data: + del(data['current_power_wattage']) + + if 'current_water_flow' in data: + del(data['current_water_flow']) + + if data['hardwaretype'] != 'pwm-dimmer': + if 'dimmer_duration' in data: + del(data['dimmer_duration']) + + if 'dimmer_off_duration' in data: + del(data['dimmer_off_duration']) + + if 'dimmer_off_percentage' in data: + del(data['dimmer_off_percentage']) + + if 'dimmer_on_duration' in data: + del(data['dimmer_on_duration']) + + if 'dimmer_on_percentage' in data: + del(data['dimmer_on_percentage']) + return self.__update_config('switch' + str(data['id']),data) def save_power_switches(self,data): diff --git a/terrariumEngine.py b/terrariumEngine.py index bebb4a418..3e8f2ddf2 100644 --- a/terrariumEngine.py +++ b/terrariumEngine.py @@ -24,6 +24,8 @@ class terrariumEngine(): + LOOP_TIMEOUT = 30 + def __init__(self): # List of queues for websocket communication self.subscribed_queues = [] @@ -112,28 +114,36 @@ def __load_power_switches(self,reloading = False): seen_switches = [] for power_switch_config in switch_config: - seen_switches.append(switch_config[power_switch_config]['id']) - if switch_config[power_switch_config]['id'] in self.power_switches: - # Update switch - self.power_switches[switch_config[power_switch_config]['id']].set_name(switch_config[power_switch_config]['name']) - self.power_switches[switch_config[power_switch_config]['id']].set_power_wattage(switch_config[power_switch_config]['power_wattage']) - self.power_switches[switch_config[power_switch_config]['id']].set_water_flow(switch_config[power_switch_config]['water_flow']) - - else: - power_switch = terrariumSwitch( - switch_config[power_switch_config]['id'], - switch_config[power_switch_config]['hardwaretype'], - switch_config[power_switch_config]['address'], - switch_config[power_switch_config]['name'], - switch_config[power_switch_config]['power_wattage'], - switch_config[power_switch_config]['water_flow'], - self.toggle_switch - ) - self.power_switches[power_switch.get_id()] = power_switch + power_switch_id = switch_config[power_switch_config]['id'] + seen_switches.append(power_switch_id) + if not power_switch_id in self.power_switches: + # Add new switch + power_switch = terrariumSwitch(switch_config[power_switch_config]['id'], + switch_config[power_switch_config]['hardwaretype'], + switch_config[power_switch_config]['address'], + callback=self.toggle_switch) + power_switch_id = power_switch.get_id() + self.power_switches[power_switch_id] = power_switch + + # Update switch + self.power_switches[power_switch_id].set_name(switch_config[power_switch_config]['name']) + self.power_switches[power_switch_id].set_power_wattage(switch_config[power_switch_config]['power_wattage']) + self.power_switches[power_switch_id].set_water_flow(switch_config[power_switch_config]['water_flow']) + + if 'dimmer_duration' in switch_config[power_switch_config]: + self.power_switches[power_switch_id].set_dimmer_duration(switch_config[power_switch_config]['dimmer_duration']) + if 'dimmer_on_duration' in switch_config[power_switch_config]: + self.power_switches[power_switch_id].set_dimmer_on_duration(switch_config[power_switch_config]['dimmer_on_duration']) + if 'dimmer_on_percentage' in switch_config[power_switch_config]: + self.power_switches[power_switch_id].set_dimmer_on_percentage(switch_config[power_switch_config]['dimmer_on_percentage']) + if 'dimmer_off_duration' in switch_config[power_switch_config]: + self.power_switches[power_switch_id].set_dimmer_off_duration(switch_config[power_switch_config]['dimmer_off_duration']) + if 'dimmer_off_percentage' in switch_config[power_switch_config]: + self.power_switches[power_switch_id].set_dimmer_off_percentage(switch_config[power_switch_config]['dimmer_off_percentage']) for power_switch_id in set(self.power_switches) - set(seen_switches): # clean up old deleted switches - del(self.power_switches[switch_config[power_switch_config]['id']]) + del(self.power_switches[power_switch_id]) if reloading: self.environment.set_power_switches(self.power_switches) @@ -158,6 +168,9 @@ def __load_doors(self, reloading = False): ) self.doors[door.get_id()] = door + if not reloading: + self.toggle_door_status(door.get_data()) + logger.info('Done %s terrariumPI doors. Found %d doors in %.3f seconds' % ('reloading' if reloading else 'loading', len(self.doors), time.time()-starttime)) @@ -181,21 +194,41 @@ def __load_webcams(self, reloading = False): len(self.webcams), time.time()-starttime)) - def __get_power_usage_water_flow(self, socket = False): + def __get_current_power_usage_water_flow(self, socket = False): data = {'power' : {'current' : self.pi_power_wattage , 'max' : self.pi_power_wattage}, 'water' : {'current' : 0.0 , 'max' : 0.0}} for switchid in self.power_switches: - data['power']['current'] += self.power_switches[switchid].get_power_wattage() if self.power_switches[switchid].is_on() else 0.0 + data['power']['current'] += self.power_switches[switchid].get_current_power_wattage() if self.power_switches[switchid].is_on() else 0.0 data['power']['max'] += self.power_switches[switchid].get_power_wattage() - data['water']['current'] += self.power_switches[switchid].get_water_flow() if self.power_switches[switchid].is_on() else 0.0 + data['water']['current'] += self.power_switches[switchid].get_current_water_flow() if self.power_switches[switchid].is_on() else 0.0 data['water']['max'] += self.power_switches[switchid].get_water_flow() return data - def __calculate_power_usage_water_flow(self): - return self.collector.get_history(['switches','summary']) + def __get_total_power_usage_water_flow(self): + totals = {'power_wattage' : {'duration' : int(time.time()) , 'wattage' : 0.0, 'price' : 0.0}, + 'water_flow' : {'duration' : int(time.time()) , 'water' : 0.0, 'price' : 0.0}} + + history = self.collector.get_history(['switches'],int(time.time()),0) + + for switchid in history['switches']: + totals['power_wattage']['wattage'] += history['switches'][switchid]['totals']['power_wattage']['wattage'] + totals['water_flow']['water'] += history['switches'][switchid]['totals']['water_flow']['water'] + + if history['switches'][switchid]['power_wattage'][0][0] / 1000.0 < totals['power_wattage']['duration']: + totals['power_wattage']['duration'] = history['switches'][switchid]['power_wattage'][0][0] / 1000.0 + + if history['switches'][switchid]['water_flow'][0][0] / 1000.0 < totals['water_flow']['duration']: + totals['water_flow']['duration'] = history['switches'][switchid]['water_flow'][0][0] / 1000.0 + + totals['power_wattage']['duration'] = max(self.get_uptime()['uptime'],int(time.time()) - totals['power_wattage']['duration'],int(time.time()) - totals['water_flow']['duration']) + totals['water_flow']['duration'] = totals['power_wattage']['duration'] + + totals['power_wattage']['wattage'] += totals['power_wattage']['duration'] * self.pi_power_wattage + + return totals def __engine_loop(self): while True: @@ -210,11 +243,11 @@ def __engine_loop(self): # Update the current sensor. self.sensors[sensorid].update() # Save new data to database - self.collector.log_sensor_data(self.sensors[sensorid]) + self.collector.log_sensor_data(self.sensors[sensorid].get_data()) # Websocket callback self.get_sensors([sensorid],socket=True) # Make time for other web request - sleep(0.2) + sleep(0.1) # Get the current average temperatures average_data = self.get_sensors(['average'])['sensors'] @@ -222,18 +255,6 @@ def __engine_loop(self): # Websocket callback self.__send_message({'type':'sensor_gauge','data':average_data}) - # Calculate power and water usage per day every 9th minute - # Disabled again! - ''' - if int(time.strftime('%M')) % 10 == 9: - for powerswitchid in self.power_switches: - self.collector.log_switch_data(self.power_switches[powerswitchid].get_data()) - self.collector.log_total_power_and_water_usage(self.pi_power_wattage) - - for doorid in self.doors: - self.collector.log_door_data(self.doors[doorid].get_data()) - ''' - # Websocket messages back self.get_uptime(socket=True) self.get_power_usage_water_flow(socket=True) @@ -247,8 +268,12 @@ def __engine_loop(self): self.webcams[webcamid].update() sleep(0.2) - logger.info('Engine loop done in %s seconds' % (time.time() - starttime,)) - sleep(30) # TODO: Config setting + duration = time.time() - starttime + if duration < terrariumEngine.LOOP_TIMEOUT: + logger.info('Engine loop done in %.5f seconds. Waiting for %.5f seconds for next round' % (duration,terrariumEngine.LOOP_TIMEOUT - duration)) + sleep(terrariumEngine.LOOP_TIMEOUT - duration) # TODO: Config setting + else: + logger.warning('Engine took to much time. Needed %.5f seconds which is %.5f more then the limit %s' % (duration,duration-terrariumEngine.LOOP_TIMEOUT,terrariumEngine.LOOP_TIMEOUT)) def __send_message(self,message): for queue in self.subscribed_queues: @@ -406,6 +431,16 @@ def set_switches_config(self, data): power_switch.set_name(switchdata['name']) power_switch.set_power_wattage(switchdata['power_wattage']) power_switch.set_water_flow(switchdata['water_flow']) + if 'dimmer_duration' in switchdata: + power_switch.set_dimmer_duration(switchdata['dimmer_duration']) + if 'dimmer_on_duration' in switchdata: + power_switch.set_dimmer_on_duration(switchdata['dimmer_on_duration']) + if 'dimmer_on_percentage' in switchdata: + power_switch.set_dimmer_on_percentage(switchdata['dimmer_on_percentage']) + if 'dimmer_off_duration' in switchdata: + power_switch.set_dimmer_off_duration(switchdata['dimmer_off_duration']) + if 'dimmer_off_percentage' in switchdata: + power_switch.set_dimmer_off_percentage(switchdata['dimmer_off_percentage']) new_switches[power_switch.get_id()] = power_switch @@ -424,8 +459,6 @@ def toggle_switch(self,data): if self.environment is not None: self.get_environment(socket=True) - if data['state'] is False: - self.collector.log_total_power_and_water_usage(self.pi_power_wattage) # End switches part @@ -688,15 +721,16 @@ def get_uptime(self, socket = False): return data def get_power_usage_water_flow(self, socket = False): - data = self.__get_power_usage_water_flow() - totaldata = self.__calculate_power_usage_water_flow() - - data['power']['total'] = totaldata['total_power'] - data['power']['duration'] = totaldata['duration'] - data['power']['price'] = self.config.get_power_price() - data['water']['total'] = totaldata['total_water'] - data['water']['duration'] = totaldata['duration'] - data['water']['price'] = self.config.get_water_price() + data = self.__get_current_power_usage_water_flow() + totaldata = self.__get_total_power_usage_water_flow() + + data['power']['total'] = totaldata['power_wattage']['wattage'] + data['power']['duration'] = totaldata['power_wattage']['duration'] + data['power']['price'] = self.config.get_power_price() * (totaldata['power_wattage']['wattage'] / (3600 * 1000)) + + data['water']['total'] = totaldata['water_flow']['water'] + data['water']['duration'] = totaldata['water_flow']['duration'] + data['water']['price'] = self.config.get_water_price() * totaldata['water_flow']['water'] if socket: self.__send_message({'type':'power_usage_water_flow','data':data}); diff --git a/terrariumSwitch.py b/terrariumSwitch.py index b2f0bd20e..14bf2c157 100644 --- a/terrariumSwitch.py +++ b/terrariumSwitch.py @@ -4,15 +4,25 @@ from pylibftdi import Driver, BitBangDevice, SerialDevice, Device import RPi.GPIO as GPIO +GPIO.setwarnings(False) +import pigpio from hashlib import md5 +import thread +import time +import math class terrariumSwitch(): - valid_hardware_types = ['ftdi','gpio','gpio-inverse'] + valid_hardware_types = ['ftdi','gpio','gpio-inverse','pwm-dimmer'] OFF = False ON = True + # PWM Dimmer settings + PWM_DIMMER_MAXDIM = 880 # http://www.esp8266-projects.com/2017/04/raspberry-pi-domoticz-ac-dimmer-part-1/ + PWM_DIMMER_MIN_TIMEOUT=0.2 + PWM_DIMMER_MIN_STEP=1 + bitbang_addresses = { "1":"2", "2":"8", @@ -25,7 +35,7 @@ class terrariumSwitch(): "all":"FF" } - def __init__(self, id, hardware_type, address, name = '', power_wattage = 0, water_flow = 0, callback = None): + def __init__(self, id, hardware_type, address, name = '', power_wattage = 0.0, water_flow = 0.0, dimmer_duration = 5.0, dimmer_on_duration = 0.0, dimmer_on_percentage = 100.0, dimmer_off_duration = 0.0, dimmer_off_percentage = 0.0, callback = None): self.id = id self.callback = callback @@ -33,6 +43,8 @@ def __init__(self, id, hardware_type, address, name = '', power_wattage = 0, wat if self.get_hardware_type() == 'ftdi': self.__load_ftdi_device() + elif self.get_hardware_type() == 'pwm-dimmer': + self.__load_pwm_device() elif 'gpio' in self.get_hardware_type(): self.__load_gpio_device() @@ -41,6 +53,12 @@ def __init__(self, id, hardware_type, address, name = '', power_wattage = 0, wat self.set_power_wattage(power_wattage) self.set_water_flow(water_flow) + self.set_dimmer_duration(dimmer_duration) + self.set_dimmer_on_duration(dimmer_on_duration) + self.set_dimmer_on_percentage(dimmer_on_percentage) + self.set_dimmer_off_duration(dimmer_off_duration) + self.set_dimmer_off_percentage(dimmer_off_percentage) + if self.id is None: self.id = md5(b'' + self.get_hardware_type() + self.get_address()).hexdigest() @@ -50,7 +68,6 @@ def __init__(self, id, hardware_type, address, name = '', power_wattage = 0, wat self.get_water_flow())) # Force to off state! - self.init = True self.state = None self.set_state(terrariumSwitch.OFF,True) @@ -64,6 +81,60 @@ def __load_ftdi_device(self): def __load_gpio_device(self): GPIO.setmode(GPIO.BOARD) + def __load_pwm_device(self): + self.__dimmer_running = False + # localhost will not work always due to IPv6. Explicit 127.0.0.1 host + self.__pigpio = pigpio.pi('127.0.0.1',8888) + if not self.__pigpio.connected: + logger.error('PiGPIOd process is not running') + self.__pigpio = False + + def __dim_switch(self,from_value,to_value,duration): + # When the dimmer is working, ignore new state changes. + if not self.__dimmer_running: + self.__pigpio.set_pull_up_down(int(self.get_address()), pigpio.PUD_OFF) + self.__dimmer_running = True + + if from_value is None or duration == 0: + logger.info('Switching dimmer \'%s\' from %s%% to %s%% instantly', + self.get_name(),from_value,to_value) + # Geen animatie, gelijk to_value + dim_value = terrariumSwitch.PWM_DIMMER_MAXDIM * ((100.0 - float(to_value)) / 100.0) + self.__pigpio.hardware_PWM(int(self.get_address()), 5000, int(dim_value) * 1000) # 5000Hz state*1000% dutycycle + else: + from_value = float(from_value) + to_value = float(to_value) + direction = (1.0 if from_value < to_value else -1.0) + + logger.info('Changing dimmer \'%s\' from %s%% to %s%% in %s seconds',self.get_name(),from_value,to_value,duration) + + distance = abs(from_value - to_value) + if duration == 0.0 or distance == 0.0: + steps = 1.0 + else: + steps = math.floor(min( (abs(duration) / terrariumSwitch.PWM_DIMMER_MIN_TIMEOUT), + (distance / terrariumSwitch.PWM_DIMMER_MIN_STEP))) + distance /= steps + duration /= steps + + logger.debug('Dimmer settings: Steps: %s, Distance per step: %s%%, Time per step: %s, Direction: %s',steps, distance, duration, direction) + + for counter in range(int(steps)): + from_value += (direction * distance) + dim_value = terrariumSwitch.PWM_DIMMER_MAXDIM * ((100.0 - from_value) / 100.0) + logger.debug('Dimmer animation: Step: %s, value %s%%, Dim value: %s, timeout %s',counter+1, from_value, dim_value, duration) + self.__pigpio.hardware_PWM(int(self.get_address()), 5000, int(dim_value) * 1000) # 5000Hz state*1000% dutycycle + time.sleep(duration) + + # For impatient people... Put the dimmer at the current state value if it has changed during the animation + dim_value = terrariumSwitch.PWM_DIMMER_MAXDIM * ((100.0 - self.get_state()) / 100.0) + self.__pigpio.hardware_PWM(int(self.get_address()), 5000, int(dim_value) * 1000) # 5000Hz state*1000% dutycycle + + self.__dimmer_running = False + logger.info('Dimmer \'%s\' is done at value %s%%',self.get_name(),self.get_state()) + else: + logger.warning('Dimmer %s is already working. Ignoring state change!. Will switch to latest state value when done', self.get_name()) + def set_state(self, state, force = False): if self.get_state() is not state or force: if self.get_hardware_type() == 'ftdi': @@ -91,18 +162,28 @@ def set_state(self, state, force = False): elif self.get_hardware_type() == 'gpio': GPIO.output(int(self.get_address()), ( GPIO.HIGH if state is terrariumSwitch.ON else GPIO.LOW )) + elif self.get_hardware_type() == 'gpio-inverse': GPIO.output(int(self.get_address()), ( GPIO.LOW if state is terrariumSwitch.ON else GPIO.HIGH )) + elif self.get_hardware_type() == 'pwm-dimmer' and self.__pigpio is not False: + duration = self.get_dimmer_duration() + # State 100 = full on which means 0 dim. + # State is inverse of dim + if state is terrariumSwitch.ON: + state = self.get_dimmer_on_percentage() + duration = self.get_dimmer_on_duration() + elif state is terrariumSwitch.OFF or not (0 <= state <= 100): + state = self.get_dimmer_off_percentage() + duration = self.get_dimmer_off_duration() + + thread.start_new_thread(self.__dim_switch, (self.state,state,duration)) + self.state = state - logger.info('Toggle switch \'%s\' from %s',self.get_name(),('off to on' if self.is_on() else 'on to off')) + if self.get_hardware_type() != 'pwm-dimmer': + logger.info('Toggle switch \'%s\' from %s',self.get_name(),('off to on' if self.is_on() else 'on to off')) if self.callback is not None: data = self.get_data() - - if self.init: - data['init'] = 1 - self.init = False - self.callback(data) return self.get_state() == state @@ -116,8 +197,15 @@ def get_data(self): 'address' : self.get_address(), 'name' : self.get_name(), 'power_wattage' : self.get_power_wattage(), + 'current_power_wattage' : self.get_current_power_wattage(), 'water_flow' : self.get_water_flow(), - 'state' : self.get_state() + 'current_water_flow' : self.get_current_water_flow(), + 'state' : self.get_state(), + 'dimmer_duration': self.get_dimmer_duration(), + 'dimmer_on_duration': self.get_dimmer_on_duration(), + 'dimmer_on_percentage' : self.get_dimmer_on_percentage(), + 'dimmer_off_duration': self.get_dimmer_off_duration(), + 'dimmer_off_percentage': self.get_dimmer_off_percentage() } return data @@ -153,6 +241,15 @@ def set_name(self,name): def get_power_wattage(self): return self.power_wattage + def get_current_power_wattage(self): + wattage = 0.0 + if self.get_hardware_type() == 'pwm-dimmer': + wattage = self.get_power_wattage() * (self.get_state() / 100.0) + else: + wattage = self.get_power_wattage() + + return wattage + def set_power_wattage(self,value): try: self.power_wattage = float(value) @@ -162,6 +259,15 @@ def set_power_wattage(self,value): def get_water_flow(self): return self.water_flow + def get_current_water_flow(self): + waterflow = 0.0 + if self.get_hardware_type() == 'pwm-dimmer': + waterflow = self.get_water_flow() * (self.get_state() / 100.0) + else: + waterflow = self.get_water_flow() + + return waterflow + def set_water_flow(self,value): try: self.water_flow = float(value) @@ -170,17 +276,25 @@ def set_water_flow(self,value): def toggle(self): if self.get_state() is not None: - old_state = self.get_state() - self.set_state(not old_state) - return self.get_state() is not old_state + if self.is_on(): + self.off() + else: + self.on() + return True return None def is_on(self): - return self.get_state() is terrariumSwitch.ON + if self.get_hardware_type() == 'pwm-dimmer': + return self.get_state() > self.get_dimmer_off_percentage() + else: + return self.get_state() is terrariumSwitch.ON def is_off(self): - return self.get_state() is terrariumSwitch.OFF + if self.get_hardware_type() == 'pwm-dimmer': + return self.get_state() == self.get_dimmer_off_percentage() + else: + return self.get_state() is terrariumSwitch.OFF def on(self): if self.get_state() is None or self.is_off(): @@ -191,3 +305,37 @@ def off(self): if self.get_state() is None or self.is_on(): self.set_state(terrariumSwitch.OFF) return self.is_off() + + def dim(self,value): + if 0 <= value <= 100: + self.set_state(100 - value) + + def set_dimmer_duration(self,value): + self.__dimmer_duration = float(value if float(value) >= 0.0 else 0) + + def get_dimmer_duration(self): + return (self.__dimmer_duration if self.get_hardware_type() == 'pwm-dimmer' else 0.0) + + def set_dimmer_on_duration(self,value): + self.__dimmer_on_duration = float(value if float(value) >= 0.0 else 0) + + def get_dimmer_on_duration(self): + return (self.__dimmer_on_duration if self.get_hardware_type() == 'pwm-dimmer' else 0.0) + + def set_dimmer_off_duration(self,value): + self.__dimmer_off_duration = float(value if float(value) >= 0.0 else 0) + + def get_dimmer_off_duration(self): + return (self.__dimmer_off_duration if self.get_hardware_type() == 'pwm-dimmer' else 0.0) + + def set_dimmer_on_percentage(self,value): + self.__dimmer_on_percentage = float(value if (0.0 <= float(value) <= 100.0) else 100) + + def get_dimmer_on_percentage(self): + return (self.__dimmer_on_percentage if self.get_hardware_type() == 'pwm-dimmer' else 100.0) + + def set_dimmer_off_percentage(self,value): + self.__dimmer_off_percentage = float(value if (0.0 <= float(value) <= 100.0) else 100) + + def get_dimmer_off_percentage(self): + return (self.__dimmer_off_percentage if self.get_hardware_type() == 'pwm-dimmer' else 0.0) diff --git a/terrariumTranslations.py b/terrariumTranslations.py index 1d563f525..1ab88983f 100644 --- a/terrariumTranslations.py +++ b/terrariumTranslations.py @@ -40,6 +40,11 @@ def __load(self): self.translations['switch_field_name'] = _('Holds the switch name.') self.translations['switch_field_power_wattage'] = _('Holds the switch power usage in Watt when switched on.') self.translations['switch_field_water_flow'] = _('Holds the switch water flow in liters per minute when switched on') + self.translations['switch_field_dimmer_duration'] = _('Holds the amount of seconds for the duration in which the dimmer changes to the new value.') + self.translations['switch_field_dimmer_on_duration'] = _('Holds the amount of seconds for the duration in which it increases the power.') + self.translations['switch_field_dimmer_on_percentage'] = _('Holds the amount in percentage to go to when switched on.') + self.translations['switch_field_dimmer_off_duration'] = _('Holds the amount of seconds for the duration in which it decresses the power.') + self.translations['switch_field_dimmer_off_percentage'] = _('Holds the amount in percentage to go to when switched off.') # End switches # Doors diff --git a/terrariumWebcam.py b/terrariumWebcam.py index 5ab8c0582..d4ab4eaac 100644 --- a/terrariumWebcam.py +++ b/terrariumWebcam.py @@ -24,6 +24,7 @@ class terrariumWebcam(): OFFLINE = 'offline' ONLINE = 'online' + UPDATE_TIMEOUT = 60 def __init__(self, id, location, name = '', rotation = None): self.id = id @@ -299,11 +300,12 @@ def __tile_image(self): def update(self): starttime = time() - logger.info('Updating webcam \'%s\' at location %s' % (self.get_name(), self.get_location(),)) - self.__get_raw_image() - if self.get_state() == 'online': - self.__tile_image() - logger.info('Done updating webcam \'%s\' at location %s in %.5f seconds' % (self.get_name(), self.get_location(),time()-starttime)) + if self.last_update is None or (int(starttime) - self.get_last_update()) > terrariumWebcam.UPDATE_TIMEOUT: + logger.info('Updating webcam \'%s\' at location %s' % (self.get_name(), self.get_location(),)) + self.__get_raw_image() + if self.get_state() == 'online': + self.__tile_image() + logger.info('Done updating webcam \'%s\' at location %s in %.5f seconds' % (self.get_name(), self.get_location(),time()-starttime)) def get_data(self): return {'id': self.get_id(), diff --git a/terrariumWebserver.py b/terrariumWebserver.py index 6d369ca44..235740364 100644 --- a/terrariumWebserver.py +++ b/terrariumWebserver.py @@ -78,6 +78,12 @@ def __routes(self): apply=auth_basic(self.__authenticate,_('TerrariumPI') + ' ' + _('Authentication'),_('Authenticate to make any changes')) ) + self.__app.route('/api/switch/state//', + method=['GET'], + callback=self.__state_switch, + apply=auth_basic(self.__authenticate,_('TerrariumPI') + ' ' + _('Authentication'),_('Authenticate to make any changes')) + ) + self.__app.route('/api/config/', method=['PUT','POST','DELETE'], callback=self.__update_api_call, @@ -229,6 +235,13 @@ def __toggle_switch(self,switchid): return {'ok' : False} + def __state_switch(self,switchid,value): + if switchid in self.__terrariumEngine.power_switches: + self.__terrariumEngine.power_switches[switchid].set_state(value) + return {'ok' : True} + + return {'ok' : False} + def __logout_url(self): return {'ok' : True, 'title' : _('Log out'), diff --git a/views/hardware.tpl b/views/hardware.tpl index 38f1a8179..18a40623e 100644 --- a/views/hardware.tpl +++ b/views/hardware.tpl @@ -101,6 +101,15 @@
  • {{_('Denkovi')}}: ...
  • +
    + {{_('Universal AC MAINS Dimmer - MPDMv4.1')}} +

    {{_('PWM')}}

    +

    {{_('With PWM controlled boards it is possible to support dimming devices.')}}

    +

    {{_('The following boards are tested')}}

    + +
    Dashboard screenshot diff --git a/views/inc/footer.tpl b/views/inc/footer.tpl index 9d41db035..46b20c61b 100644 --- a/views/inc/footer.tpl +++ b/views/inc/footer.tpl @@ -25,6 +25,8 @@ + + diff --git a/views/switch_settings.tpl b/views/switch_settings.tpl index ae001da9d..b43dd4214 100644 --- a/views/switch_settings.tpl +++ b/views/switch_settings.tpl @@ -74,32 +74,57 @@
    -
    - -
    - +
    +
    + +
    + +
    +
    +
    + * + + +
    +
    + * + +
    +
    + + +
    +
    + +
    -
    - * - - -
    -
    - * - -
    -
    - - -
    -
    - - +
    @@ -120,6 +145,8 @@ placeholder: '{{_('Select an option')}}', allowClear: false, minimumResultsForSearch: Infinity + }).on('change',function() { + $(this).parents('.x_content').find('.row.dimmer').toggle(this.value === 'pwm-dimmer'); }); $.get($('form').attr('action'),function(data){ $.each(data.switches, function(index,power_switch) { @@ -129,9 +156,15 @@ power_switch.address, power_switch.name, power_switch.power_wattage, - power_switch.water_flow); + power_switch.water_flow, + power_switch.dimmer_duration, + power_switch.dimmer_on_duration, + power_switch.dimmer_on_percentage, + power_switch.dimmer_off_duration, + power_switch.dimmer_off_percentage); update_power_switch(power_switch.id,power_switch); }); + reload_reload_theme(); }); }); diff --git a/views/switch_status.tpl b/views/switch_status.tpl index 73236ca90..c6cad83ec 100644 --- a/views/switch_status.tpl +++ b/views/switch_status.tpl @@ -44,7 +44,7 @@
    - +