4 Commits

Author SHA1 Message Date
Av3m
9b21b59c81 initial commit for ecowitt binding 2022-01-08 21:26:34 +01:00
Av3m
48969db2b6 initial commit for ecowitt binding 2022-01-07 13:56:40 +01:00
Av3m
0350e715a4 initial commit for ecowitt binding 2022-01-07 13:56:23 +01:00
openhab-bot
23382848e8 Preparation for tag creation (Tag name: '3.2.0'). 2021-12-20 00:25:45 +00:00
16540 changed files with 195798 additions and 552203 deletions

View File

@@ -3,32 +3,27 @@ Thanks for contributing to the openHAB project!
Please describe the goal and effect of your PR here. Please describe the goal and effect of your PR here.
Pay attention to the below notes and to *the guidelines* for this repository. Pay attention to the below notes and to *the guidelines* for this repository.
Feel free to delete any comment sections in the template (starting with "<!--"). Feel free to delete any comment sections in the template (starting with "<!--").
-->
ATTENTION: Don't use "git merge" when working with your pull request branch! <!-- TITLE -->
This can clutter your Git history and make your PR unusable.
Use "git rebase" instead. See this forum post for further details:
https://community.openhab.org/t/rebase-your-code-or-how-to-fix-your-git-history-before-requesting-a-pull/129358
All PRs should be created using the "main" branch as base. <!--
Important bugfixes are cherry-picked by maintainers to the patch release branch after a PR has been merged. Please provide a PR summary in the *Title* above, according to the following schema:
- If related to one specific add-on: Mention the add-on shortname in square brackets
e.g. "[exec]", "[netatmo]" or "[tesla]"
- If the PR is work in progress: Add "[WIP]"
- Give a short meaningful description in imperative mood
e.g. "Add support for device XYZ" or "Fix wrongly handled exception"
for a new add-on/binding: "Initial contribution"
Examples:
- "[homematic] Improve communication with weak signal devices"
- "[timemachine][WIP] Initial contribution"
- "Update contribution guidelines on new signing rules"
-->
Add one or more appropriate labels to make your PR show up in the release notes. <!-- DESCRIPTION -->
E.g. enhancement, bug, documentation, new binding
This can only be done by yourself if you already contributed to this repo.
If your PR's code is not backward compatible with previous releases (which
should be avoided), add a message to the release notes by filing another PR:
https://github.com/openhab/openhab-distro/blob/main/distributions/openhab/src/main/resources/bin/update.lst
# Title
Provide a short summary in the *Title* above. It will show up in the release notes.
For example:
- [homematic] Improve communication with weak signal devices
- [timemachine][WIP] Initial contribution
# Description
<!--
Please give a few sentences describing the overall goals of the pull request. Please give a few sentences describing the overall goals of the pull request.
Give enough details to make the improvement and changes of the PR understandable Give enough details to make the improvement and changes of the PR understandable
to both developers and tech-savy users. to both developers and tech-savy users.
@@ -46,18 +41,21 @@ Please keep the following in mind:
https://www.openhab.org/docs/developer/bindings/#include-the-binding-in-the-build https://www.openhab.org/docs/developer/bindings/#include-the-binding-in-the-build
- Did you sign-off your work: - Did you sign-off your work:
https://www.openhab.org/docs/developer/contributing.html#sign-your-work https://www.openhab.org/docs/developer/contributing.html#sign-your-work
-->
# Testing <!-- TESTING -->
Your pull request will automatically be built and available under the following folder: <!--
https://openhab.jfrog.io/ui/native/libs-pullrequest-local/org/openhab/addons/bundles/ Your Pull Request will automatically be built and available under the following folder:
https://openhab.jfrog.io/openhab/libs-pullrequest-local/org/openhab/
It is a good practice to add a URL to your built JAR in this pull request description, It is a good practice to add a URL to your built JAR in this Pull Request description,
so it is easier for the community to test your Add-on. so it is easier for the community to test your Add-on.
If your pull request contains a new binding, it will likely take some time If your Pull Request contains a new binding, it will likely take some time
before it is reviewed and processed by maintainers. before it is reviewed and processed by maintainers.
That said, consider submitting your Add-on in the Marketplace: That said, consider submitting your Add-on in the Eclipse IoT Marketplace
https://community.openhab.org/c/marketplace/69 See this thread for more info:
https://community.openhab.org/t/24491
Don't forget to submit a thread about your Add-on in the openHAB community: Don't forget to submit a thread about your Add-on in the openHAB community:
https://community.openhab.org/c/add-ons https://community.openhab.org/c/add-ons

View File

@@ -66,7 +66,7 @@ function addon_projects() {
local addon="$1" local addon="$1"
# include add-on projects # include add-on projects
local projects=":$(find . -mindepth 2 -maxdepth 2 -type d -regextype egrep -regex "./(bundles|itests)/$addon(\..*)?$" | sort | sed -E 's#./(bundles|itests)/##g' | xargs | sed 's# #,:#g')" local projects=":$(find . -mindepth 2 -maxdepth 2 -type d -regextype egrep -regex "./(bundles|itests)/$addon(\..*)?$" | grep -Ev 'org.openhab.binding.mqtt.homeassistant.tests|org.openhab.binding.mqtt.homie.tests' | sort | sed -E 's#./(bundles|itests)/##g' | xargs | sed 's# #,:#g')"
# include BOMs # include BOMs
projects="$projects,:org.openhab.addons.bom.openhab-core-index,:org.openhab.addons.bom.runtime-index,:org.openhab.addons.bom.test-index" projects="$projects,:org.openhab.addons.bom.openhab-core-index,:org.openhab.addons.bom.runtime-index,:org.openhab.addons.bom.test-index"
@@ -94,10 +94,7 @@ function build_addon() {
function build_based_on_changes() { function build_based_on_changes() {
local changed_addon=$(changed_addons | xargs) local changed_addon=$(changed_addons | xargs)
if [ $(echo $changed_addon | wc -w) -eq 1 ] && \ if [ $(echo $changed_addon | wc -w) -eq 1 ] && [ $(addon_unrelated_changed_files $changed_addon | wc -l) -eq 0 ]; then
[ $(addon_unrelated_changed_files $changed_addon | wc -l) -eq 0 ] && \
[ -d "./bundles/$changed_addon" ]
then
build_addon $changed_addon build_addon $changed_addon
else else
build_all build_all

23
.github/stale.yml vendored
View File

@@ -1,23 +0,0 @@
# Number of days of inactivity before an issue becomes stale (two month)
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed (another six month)
daysUntilClose: 180
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- "PR pending"
# Only issues with all of these labels are checked if stale.
onlyLabels:
- "awaiting feedback"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
# Limit to only `issues``
only: issues

View File

@@ -16,26 +16,25 @@ on:
jobs: jobs:
build: build:
strategy: strategy:
fail-fast: false
matrix: matrix:
java: [ '17' ] java: [ '11' ]
maven: [ '3.9.4' ] maven: [ '3.8.4']
os: [ 'ubuntu-22.04' ] os: [ 'ubuntu-20.04' ]
name: Build (Java ${{ matrix.java }}, ${{ matrix.os }}) name: Build (Java ${{ matrix.java }}, ${{ matrix.os }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout
if: github.head_ref == '' if: github.head_ref == ''
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Checkout merge - name: Checkout merge
if: github.head_ref != '' if: github.head_ref != ''
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
ref: refs/pull/${{github.event.pull_request.number}}/merge ref: refs/pull/${{github.event.pull_request.number}}/merge
- name: Set up Cache - name: Set up Cache
uses: actions/cache@v3 uses: actions/cache@v2
with: with:
path: | path: |
~/.m2/repository ~/.m2/repository
@@ -45,26 +44,24 @@ jobs:
${{ runner.os }}-maven- ${{ runner.os }}-maven-
- name: Set up Java ${{ matrix.java }} - name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v3 uses: actions/setup-java@v2
with: with:
distribution: 'temurin' distribution: 'zulu'
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
- name: Set up Maven ${{ matrix.maven }} - name: Set up Maven ${{ matrix.maven }}
uses: stCarolas/setup-maven@v4.5 uses: stCarolas/setup-maven@v4.2
with: with:
maven-version: ${{ matrix.maven }} maven-version: ${{ matrix.maven }}
- name: Register Problem Matchers - name: Register Problem Matchers
if: ${{ matrix.java == '17' }}
id: problem_matchers id: problem_matchers
run: | run: |
echo "::add-matcher::.github/openhab-compile-problems.json" echo "::add-matcher::.github/openhab-compile-problems.json"
- name: Get Changed Files - name: Get Changed Files
if: github.head_ref != ''
id: files id: files
uses: Ana06/get-changed-files@v2.2.0 uses: Ana06/get-changed-files@v2.0.0
with: with:
format: 'csv' format: 'csv'
@@ -81,21 +78,21 @@ jobs:
- name: Upload Build Log - name: Upload Build Log
if: ${{ always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }} if: ${{ always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: build-log-java-${{ matrix.java }}-${{ matrix.os }} name: build-log-java-${{ matrix.java }}-${{ matrix.os }}
path: build.log path: build.log
- name: Upload SAT Summary Report - name: Upload SAT Summary Report
if: ${{ always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }} if: ${{ always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: sat-summary-report name: sat-summary-report
path: target/summary_report.html path: target/summary_report.html
- name: Report SAT Errors as Annotations - name: Report SAT Errors as Annotations
if: ${{ matrix.java == '17' && always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }}
uses: ghys/checkstyle-github-action@main uses: ghys/checkstyle-github-action@main
if: ${{ always() && ((steps.build.outcome == 'success') || (steps.build.outcome == 'failure')) }}
with: with:
title: CheckStyle Violations title: CheckStyle Violations
path: '**/checkstyle-result.xml' path: '**/checkstyle-result.xml'

1
.gitignore vendored
View File

@@ -3,7 +3,6 @@
.idea .idea
.project .project
.DS_Store .DS_Store
.gradle
*.iml *.iml
npm-debug.log npm-debug.log
.build.log .build.log

View File

@@ -6,34 +6,29 @@
# Add-on maintainers: # Add-on maintainers:
/bundles/org.openhab.automation.groovyscripting/ @wborn /bundles/org.openhab.automation.groovyscripting/ @wborn
/bundles/org.openhab.automation.jrubyscripting/ @ccutrer @jimtng /bundles/org.openhab.automation.jrubyscripting/ @boc-tothefuture
/bundles/org.openhab.automation.jsscripting/ @jpg0 @florian-h05 /bundles/org.openhab.automation.jsscripting/ @jpg0
/bundles/org.openhab.automation.jsscriptingnashorn/ @wborn
/bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers /bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers
/bundles/org.openhab.automation.pidcontroller/ @fwolter /bundles/org.openhab.automation.pidcontroller/ @fwolter
/bundles/org.openhab.automation.pwm/ @fwolter /bundles/org.openhab.automation.pwm/ @fwolter
/bundles/org.openhab.binding.adorne/ @theiding /bundles/org.openhab.binding.adorne/ @theiding
/bundles/org.openhab.binding.ahawastecollection/ @soenkekueper /bundles/org.openhab.binding.ahawastecollection/ @soenkekueper
/bundles/org.openhab.binding.airq/ @aurelio1 /bundles/org.openhab.binding.airq/ @aurelio1
/bundles/org.openhab.binding.airquality/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.airquality/ @kubawolanin
/bundles/org.openhab.binding.airvisualnode/ @3cky /bundles/org.openhab.binding.airvisualnode/ @3cky
/bundles/org.openhab.binding.alarmdecoder/ @bobadair @billfor /bundles/org.openhab.binding.alarmdecoder/ @bobadair @billfor
/bundles/org.openhab.binding.allplay/ @dominicdesu /bundles/org.openhab.binding.allplay/ @dominicdesu
/bundles/org.openhab.binding.amazondashbutton/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.amazondashbutton/ @OLibutzki
/bundles/org.openhab.binding.amazonechocontrol/ @mgeramb /bundles/org.openhab.binding.amazonechocontrol/ @mgeramb
/bundles/org.openhab.binding.ambientweather/ @mhilbush /bundles/org.openhab.binding.ambientweather/ @mhilbush
/bundles/org.openhab.binding.amplipi/ @kaikreuzer /bundles/org.openhab.binding.amplipi/ @kaikreuzer
/bundles/org.openhab.binding.androiddebugbridge/ @GiviMAD /bundles/org.openhab.binding.androiddebugbridge/ @GiviMAD
/bundles/org.openhab.binding.androidtv/ @morph166955
/bundles/org.openhab.binding.anel/ @paphko /bundles/org.openhab.binding.anel/ @paphko
/bundles/org.openhab.binding.anthem/ @mhilbush
/bundles/org.openhab.binding.asuswrt/ @wildcs
/bundles/org.openhab.binding.astro/ @gerrieg /bundles/org.openhab.binding.astro/ @gerrieg
/bundles/org.openhab.binding.atlona/ @mlobstein /bundles/org.openhab.binding.atlona/ @tmrobert8
/bundles/org.openhab.binding.autelis/ @digitaldan /bundles/org.openhab.binding.autelis/ @digitaldan
/bundles/org.openhab.binding.automower/ @maxpg /bundles/org.openhab.binding.automower/ @maxpg
/bundles/org.openhab.binding.avmfritz/ @cweitkamp /bundles/org.openhab.binding.avmfritz/ @cweitkamp
/bundles/org.openhab.binding.awattar/ @Wolfgang1966
/bundles/org.openhab.binding.benqprojector/ @mlobstein /bundles/org.openhab.binding.benqprojector/ @mlobstein
/bundles/org.openhab.binding.bigassfan/ @mhilbush /bundles/org.openhab.binding.bigassfan/ @mhilbush
/bundles/org.openhab.binding.bluetooth/ @cdjackson @cpmeister /bundles/org.openhab.binding.bluetooth/ @cdjackson @cpmeister
@@ -46,13 +41,11 @@
/bundles/org.openhab.binding.bluetooth.enoceanble/ @pfink /bundles/org.openhab.binding.bluetooth.enoceanble/ @pfink
/bundles/org.openhab.binding.bluetooth.generic/ @cpmeister /bundles/org.openhab.binding.bluetooth.generic/ @cpmeister
/bundles/org.openhab.binding.bluetooth.govee/ @cpmeister /bundles/org.openhab.binding.bluetooth.govee/ @cpmeister
/bundles/org.openhab.binding.bluetooth.grundfosalpha/ @tisoft
/bundles/org.openhab.binding.bluetooth.radoneye/ @petero-dk
/bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister /bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister
/bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen /bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen
/bundles/org.openhab.binding.bondhome/ @ccutrer /bundles/org.openhab.binding.bmwconnecteddrive/ @weymann @ntruchsess
/bundles/org.openhab.binding.boschindego/ @jofleck @jlaur /bundles/org.openhab.binding.boschindego/ @jofleck
/bundles/org.openhab.binding.boschshc/ @david-pace @GerdZanker /bundles/org.openhab.binding.boschshc/ @stefan-kaestle @coeing @GerdZanker
/bundles/org.openhab.binding.bosesoundtouch/ @marvkis @tratho /bundles/org.openhab.binding.bosesoundtouch/ @marvkis @tratho
/bundles/org.openhab.binding.broadlinkthermostat/ @flo-02-mu /bundles/org.openhab.binding.broadlinkthermostat/ @flo-02-mu
/bundles/org.openhab.binding.bsblan/ @hypetsch /bundles/org.openhab.binding.bsblan/ @hypetsch
@@ -60,7 +53,6 @@
/bundles/org.openhab.binding.buienradar/ @gedejong /bundles/org.openhab.binding.buienradar/ @gedejong
/bundles/org.openhab.binding.caddx/ @jossuar /bundles/org.openhab.binding.caddx/ @jossuar
/bundles/org.openhab.binding.cbus/ @jpharvey /bundles/org.openhab.binding.cbus/ @jpharvey
/bundles/org.openhab.binding.chatgpt/ @kaikreuzer
/bundles/org.openhab.binding.chromecast/ @kaikreuzer /bundles/org.openhab.binding.chromecast/ @kaikreuzer
/bundles/org.openhab.binding.cm11a/ @BobRak /bundles/org.openhab.binding.cm11a/ @BobRak
/bundles/org.openhab.binding.comfoair/ @boehan /bundles/org.openhab.binding.comfoair/ @boehan
@@ -69,12 +61,13 @@
/bundles/org.openhab.binding.daikin/ @caffineehacker /bundles/org.openhab.binding.daikin/ @caffineehacker
/bundles/org.openhab.binding.dali/ @rs22 /bundles/org.openhab.binding.dali/ @rs22
/bundles/org.openhab.binding.danfossairunit/ @pravussum /bundles/org.openhab.binding.danfossairunit/ @pravussum
/bundles/org.openhab.binding.darksky/ @cweitkamp
/bundles/org.openhab.binding.dbquery/ @lujop /bundles/org.openhab.binding.dbquery/ @lujop
/bundles/org.openhab.binding.deconz/ @J-N-K /bundles/org.openhab.binding.deconz/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.denonmarantz/ @jwveldhuis /bundles/org.openhab.binding.denonmarantz/ @jwveldhuis
/bundles/org.openhab.binding.deutschebahn/ @soenkekueper /bundles/org.openhab.binding.deutschebahn/ @soenkekueper
/bundles/org.openhab.binding.digiplex/ @rmichalak /bundles/org.openhab.binding.digiplex/ @rmichalak
/bundles/org.openhab.binding.digitalstrom/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.digitalstrom/ @MichaelOchel @msiegele
/bundles/org.openhab.binding.dlinksmarthome/ @MikeJMajor /bundles/org.openhab.binding.dlinksmarthome/ @MikeJMajor
/bundles/org.openhab.binding.dmx/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.dmx/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.dominoswiss/ @Friesoch /bundles/org.openhab.binding.dominoswiss/ @Friesoch
@@ -84,75 +77,62 @@
/bundles/org.openhab.binding.dsmr/ @Hilbrand /bundles/org.openhab.binding.dsmr/ @Hilbrand
/bundles/org.openhab.binding.dwdpollenflug/ @DerOetzi /bundles/org.openhab.binding.dwdpollenflug/ @DerOetzi
/bundles/org.openhab.binding.dwdunwetter/ @limdul79 /bundles/org.openhab.binding.dwdunwetter/ @limdul79
/bundles/org.openhab.binding.easee/ @alexf2015
/bundles/org.openhab.binding.echonetlite/ @mikeb01
/bundles/org.openhab.binding.ecobee/ @mhilbush /bundles/org.openhab.binding.ecobee/ @mhilbush
/bundles/org.openhab.binding.ecotouch/ @sibbi77 /bundles/org.openhab.binding.ecotouch/ @sibbi77
/bundles/org.openhab.binding.ecovacs/ @maniac103 /bundles/org.openhab.binding.ecowitt/ @Av3m
/bundles/org.openhab.binding.ecowatt/ @lolodomo
/bundles/org.openhab.binding.ekey/ @hmerk /bundles/org.openhab.binding.ekey/ @hmerk
/bundles/org.openhab.binding.electroluxair/ @jannegpriv
/bundles/org.openhab.binding.elerotransmitterstick/ @vbier /bundles/org.openhab.binding.elerotransmitterstick/ @vbier
/bundles/org.openhab.binding.elroconnects/ @mherwege
/bundles/org.openhab.binding.energenie/ @hmerk /bundles/org.openhab.binding.energenie/ @hmerk
/bundles/org.openhab.binding.energidataservice/ @jlaur
/bundles/org.openhab.binding.enigma2/ @gdolfen /bundles/org.openhab.binding.enigma2/ @gdolfen
/bundles/org.openhab.binding.enocean/ @fruggy83 /bundles/org.openhab.binding.enocean/ @fruggy83
/bundles/org.openhab.binding.enphase/ @Hilbrand /bundles/org.openhab.binding.enphase/ @Hilbrand
/bundles/org.openhab.binding.enturno/ @klocsson /bundles/org.openhab.binding.enturno/ @klocsson
/bundles/org.openhab.binding.epsonprojector/ @mlobstein /bundles/org.openhab.binding.epsonprojector/ @mlobstein
/bundles/org.openhab.binding.etherrain/ @dfad1469 /bundles/org.openhab.binding.etherrain/ @dfad1469
/bundles/org.openhab.binding.evcc/ @florian-h05
/bundles/org.openhab.binding.evohome/ @Nebula83 /bundles/org.openhab.binding.evohome/ @Nebula83
/bundles/org.openhab.binding.exec/ @kgoderis /bundles/org.openhab.binding.exec/ @kgoderis
/bundles/org.openhab.binding.feed/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.feed/ @svilenvul
/bundles/org.openhab.binding.feican/ @Hilbrand /bundles/org.openhab.binding.feican/ @Hilbrand
/bundles/org.openhab.binding.fineoffsetweatherstation/ @Andy2003
/bundles/org.openhab.binding.flicbutton/ @pfink
/bundles/org.openhab.binding.fmiweather/ @ssalonen /bundles/org.openhab.binding.fmiweather/ @ssalonen
/bundles/org.openhab.binding.folderwatcher/ @goopilot /bundles/org.openhab.binding.folderwatcher/ @goopilot
/bundles/org.openhab.binding.folding/ @fa2k /bundles/org.openhab.binding.folding/ @fa2k
/bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand /bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand
/bundles/org.openhab.binding.freebox/ @lolodomo /bundles/org.openhab.binding.freebox/ @lolodomo
/bundles/org.openhab.binding.freeboxos/ @clinique
/bundles/org.openhab.binding.fronius/ @trokohl /bundles/org.openhab.binding.fronius/ @trokohl
/bundles/org.openhab.binding.fsinternetradio/ @paphko /bundles/org.openhab.binding.fsinternetradio/ @paphko
/bundles/org.openhab.binding.ftpupload/ @paulianttila /bundles/org.openhab.binding.ftpupload/ @paulianttila
/bundles/org.openhab.binding.gardena/ @gerrieg @andrewfg /bundles/org.openhab.binding.gardena/ @gerrieg
/bundles/org.openhab.binding.gce/ @clinique /bundles/org.openhab.binding.gce/ @clinique
/bundles/org.openhab.binding.generacmobilelink/ @digitaldan /bundles/org.openhab.binding.generacmobilelink/ @digitaldan
/bundles/org.openhab.binding.globalcache/ @mhilbush /bundles/org.openhab.binding.globalcache/ @mhilbush
/bundles/org.openhab.binding.goecharger/ @SamuelBrucksch /bundles/org.openhab.binding.goecharger/ @SamuelBrucksch
/bundles/org.openhab.binding.govee/ @stefan-hoehn
/bundles/org.openhab.binding.gpio/ @nils-bauer /bundles/org.openhab.binding.gpio/ @nils-bauer
/bundles/org.openhab.binding.gpstracker/ @gbicskei /bundles/org.openhab.binding.gpstracker/ @gbicskei
/bundles/org.openhab.binding.gree/ @markus7017 /bundles/org.openhab.binding.gree/ @markus7017
/bundles/org.openhab.binding.groheondus/ @FlorianSW /bundles/org.openhab.binding.groheondus/ @FlorianSW
/bundles/org.openhab.binding.groupepsa/ @arjanmels
/bundles/org.openhab.binding.guntamatic/ @MikeTheTux
/bundles/org.openhab.binding.haassohnpelletstove/ @chingon007 /bundles/org.openhab.binding.haassohnpelletstove/ @chingon007
/bundles/org.openhab.binding.harmonyhub/ @digitaldan /bundles/org.openhab.binding.harmonyhub/ @digitaldan
/bundles/org.openhab.binding.haywardomnilogic/ @matchews /bundles/org.openhab.binding.haywardomnilogic/ @matchews
/bundles/org.openhab.binding.hccrubbishcollection/ @cossey /bundles/org.openhab.binding.hccrubbishcollection/ @cossey
/bundles/org.openhab.binding.hdanywhere/ @kgoderis /bundles/org.openhab.binding.hdanywhere/ @kgoderis
/bundles/org.openhab.binding.hdpowerview/ @andylintner @jlaur @andrewfg /bundles/org.openhab.binding.hdpowerview/ @beowulfe
/bundles/org.openhab.binding.helios/ @kgoderis /bundles/org.openhab.binding.helios/ @kgoderis
/bundles/org.openhab.binding.heliosventilation/ @ramack /bundles/org.openhab.binding.heliosventilation/ @ramack
/bundles/org.openhab.binding.heos/ @Wire82 /bundles/org.openhab.binding.heos/ @Wire82
/bundles/org.openhab.binding.herzborg/ @Sonic-Amiga
/bundles/org.openhab.binding.homeconnect/ @bruestel /bundles/org.openhab.binding.homeconnect/ @bruestel
/bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s /bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s
/bundles/org.openhab.binding.homewizard/ @Daniel-42 /bundles/org.openhab.binding.homewizard/ @Daniel-42
/bundles/org.openhab.binding.hpprinter/ @cossey /bundles/org.openhab.binding.hpprinter/ @cossey
/bundles/org.openhab.binding.http/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.http/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.hue/ @cweitkamp @andrewfg /bundles/org.openhab.binding.hue/ @cweitkamp
/bundles/org.openhab.binding.hydrawise/ @digitaldan /bundles/org.openhab.binding.hydrawise/ @digitaldan
/bundles/org.openhab.binding.hyperion/ @tavalin /bundles/org.openhab.binding.hyperion/ @tavalin
/bundles/org.openhab.binding.iammeter/ @lewei50 /bundles/org.openhab.binding.iammeter/ @lewei50
/bundles/org.openhab.binding.iaqualink/ @digitaldan /bundles/org.openhab.binding.iaqualink/ @digitaldan
/bundles/org.openhab.binding.icalendar/ @daMihe /bundles/org.openhab.binding.icalendar/ @daMihe
/bundles/org.openhab.binding.icloud/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.icloud/ @pgfeller
/bundles/org.openhab.binding.ihc/ @paulianttila /bundles/org.openhab.binding.ihc/ @paulianttila
/bundles/org.openhab.binding.innogysmarthome/ @ollie-dev
/bundles/org.openhab.binding.insteon/ @robnielsen /bundles/org.openhab.binding.insteon/ @robnielsen
/bundles/org.openhab.binding.intesis/ @hmerk /bundles/org.openhab.binding.intesis/ @hmerk
/bundles/org.openhab.binding.ipcamera/ @Skinah /bundles/org.openhab.binding.ipcamera/ @Skinah
@@ -163,12 +143,10 @@
/bundles/org.openhab.binding.ism8/ @hans-reiner /bundles/org.openhab.binding.ism8/ @hans-reiner
/bundles/org.openhab.binding.jablotron/ @octa22 /bundles/org.openhab.binding.jablotron/ @octa22
/bundles/org.openhab.binding.jeelink/ @vbier /bundles/org.openhab.binding.jeelink/ @vbier
/bundles/org.openhab.binding.jellyfin/ @GiviMAD
/bundles/org.openhab.binding.juicenet/ @jsjames
/bundles/org.openhab.binding.kaleidescape/ @mlobstein /bundles/org.openhab.binding.kaleidescape/ @mlobstein
/bundles/org.openhab.binding.keba/ @kgoderis /bundles/org.openhab.binding.keba/ @kgoderis
/bundles/org.openhab.binding.km200/ @Markinus /bundles/org.openhab.binding.km200/ @Markinus
/bundles/org.openhab.binding.knx/ @kaikreuzer @holgerfriedrich /bundles/org.openhab.binding.knx/ @sjka
/bundles/org.openhab.binding.kodi/ @pail23 @cweitkamp /bundles/org.openhab.binding.kodi/ @pail23 @cweitkamp
/bundles/org.openhab.binding.konnected/ @volfan6415 /bundles/org.openhab.binding.konnected/ @volfan6415
/bundles/org.openhab.binding.kostalinverter/ @cschneider /bundles/org.openhab.binding.kostalinverter/ @cschneider
@@ -182,23 +160,18 @@
/bundles/org.openhab.binding.lifx/ @wborn /bundles/org.openhab.binding.lifx/ @wborn
/bundles/org.openhab.binding.linky/ @clinique @lolodomo /bundles/org.openhab.binding.linky/ @clinique @lolodomo
/bundles/org.openhab.binding.linuxinput/ @t-8ch /bundles/org.openhab.binding.linuxinput/ @t-8ch
/bundles/org.openhab.binding.liquidcheck/ @marcelGoerentz
/bundles/org.openhab.binding.lirc/ @kabili207 /bundles/org.openhab.binding.lirc/ @kabili207
/bundles/org.openhab.binding.livisismarthome/ @Novanic
/bundles/org.openhab.binding.logreader/ @paulianttila /bundles/org.openhab.binding.logreader/ @paulianttila
/bundles/org.openhab.binding.loxone/ @ppieczul /bundles/org.openhab.binding.loxone/ @ppieczul
/bundles/org.openhab.binding.luftdateninfo/ @weymann
/bundles/org.openhab.binding.lutron/ @actong @bobadair /bundles/org.openhab.binding.lutron/ @actong @bobadair
/bundles/org.openhab.binding.luxom/ @jesperskriasoft
/bundles/org.openhab.binding.luxtronikheatpump/ @sgiehl /bundles/org.openhab.binding.luxtronikheatpump/ @sgiehl
/bundles/org.openhab.binding.magentatv/ @markus7017 /bundles/org.openhab.binding.magentatv/ @markus7017
/bundles/org.openhab.binding.mail/ @J-N-K /bundles/org.openhab.binding.mail/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.max/ @marcelrv /bundles/org.openhab.binding.max/ @marcelrv
/bundles/org.openhab.binding.mcd/ @simon-dengler
/bundles/org.openhab.binding.mcp23017/ @aogorek /bundles/org.openhab.binding.mcp23017/ @aogorek
/bundles/org.openhab.binding.meater/ @jannegpriv
/bundles/org.openhab.binding.mecmeter/ @kaikreuzer /bundles/org.openhab.binding.mecmeter/ @kaikreuzer
/bundles/org.openhab.binding.melcloud/ @lucacalcaterra @paulianttila @thewiep /bundles/org.openhab.binding.melcloud/ @lucacalcaterra @paulianttila @thewiep
/bundles/org.openhab.binding.mercedesme/ @weymann
/bundles/org.openhab.binding.meteoalerte/ @clinique /bundles/org.openhab.binding.meteoalerte/ @clinique
/bundles/org.openhab.binding.meteoblue/ @9037568 /bundles/org.openhab.binding.meteoblue/ @9037568
/bundles/org.openhab.binding.meteostick/ @cdjackson /bundles/org.openhab.binding.meteostick/ @cdjackson
@@ -207,7 +180,7 @@
/bundles/org.openhab.binding.mihome/ @pboos /bundles/org.openhab.binding.mihome/ @pboos
/bundles/org.openhab.binding.miio/ @marcelrv /bundles/org.openhab.binding.miio/ @marcelrv
/bundles/org.openhab.binding.mikrotik/ @duhast /bundles/org.openhab.binding.mikrotik/ @duhast
/bundles/org.openhab.binding.milight/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.milight/ @davidgraeff
/bundles/org.openhab.binding.millheat/ @seime /bundles/org.openhab.binding.millheat/ @seime
/bundles/org.openhab.binding.minecraft/ @ibaton /bundles/org.openhab.binding.minecraft/ @ibaton
/bundles/org.openhab.binding.modbus/ @ssalonen /bundles/org.openhab.binding.modbus/ @ssalonen
@@ -219,29 +192,25 @@
/bundles/org.openhab.binding.modbus.sunspec/ @mrbig /bundles/org.openhab.binding.modbus.sunspec/ @mrbig
/bundles/org.openhab.binding.monopriceaudio/ @mlobstein /bundles/org.openhab.binding.monopriceaudio/ @mlobstein
/bundles/org.openhab.binding.mpd/ @stefanroellin /bundles/org.openhab.binding.mpd/ @stefanroellin
/bundles/org.openhab.binding.mqtt/ @ccutrer /bundles/org.openhab.binding.mqtt/ @davidgraeff
/bundles/org.openhab.binding.mqtt.espmilighthub/ @Skinah /bundles/org.openhab.binding.mqtt.espmilighthub/ @Skinah
/bundles/org.openhab.binding.mqtt.generic/ @ccutrer /bundles/org.openhab.binding.mqtt.generic/ @davidgraeff
/bundles/org.openhab.binding.mqtt.homeassistant/ @antroids @ccutrer /bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff @antroids
/bundles/org.openhab.binding.mqtt.homie/ @ccutrer /bundles/org.openhab.binding.mqtt.homie/ @davidgraeff
/bundles/org.openhab.binding.mqtt.ruuvigateway/ @ssalonen /bundles/org.openhab.binding.myq/ @digitaldan
/bundles/org.openhab.binding.mycroft/ @dalgwen
/bundles/org.openhab.binding.mybmw/ @ntruchsess @mherwege @martingrassl
/bundles/org.openhab.binding.mynice/ @clinique
/bundles/org.openhab.binding.mystrom/ @pail23 /bundles/org.openhab.binding.mystrom/ @pail23
/bundles/org.openhab.binding.nanoleaf/ @stefan-hoehn /bundles/org.openhab.binding.nanoleaf/ @raepple @stefan-hoehn
/bundles/org.openhab.binding.neato/ @jjlauterbach /bundles/org.openhab.binding.neato/ @jjlauterbach
/bundles/org.openhab.binding.neeo/ @morph166955 /bundles/org.openhab.binding.neeo/ @tmrobert8
/bundles/org.openhab.binding.neohub/ @andrewfg /bundles/org.openhab.binding.neohub/ @andrewfg
/bundles/org.openhab.binding.nest/ @wborn /bundles/org.openhab.binding.nest/ @wborn
/bundles/org.openhab.binding.netatmo/ @clinique @lolodomo /bundles/org.openhab.binding.netatmo/ @clinique @cweitkamp @lolodomo
/bundles/org.openhab.binding.network/ @mettke /bundles/org.openhab.binding.network/ @davidgraeff @mettke
/bundles/org.openhab.binding.networkupstools/ @Hilbrand /bundles/org.openhab.binding.networkupstools/ @Hilbrand
/bundles/org.openhab.binding.nibeheatpump/ @paulianttila /bundles/org.openhab.binding.nibeheatpump/ @paulianttila
/bundles/org.openhab.binding.nibeuplink/ @alexf2015 /bundles/org.openhab.binding.nibeuplink/ @alexf2015
/bundles/org.openhab.binding.nikobus/ @crnjan /bundles/org.openhab.binding.nikobus/ @crnjan
/bundles/org.openhab.binding.nikohomecontrol/ @mherwege /bundles/org.openhab.binding.nikohomecontrol/ @mherwege
/bundles/org.openhab.binding.nobohub/ @espenaf
/bundles/org.openhab.binding.novafinedust/ @t2000 /bundles/org.openhab.binding.novafinedust/ @t2000
/bundles/org.openhab.binding.ntp/ @marcelrv /bundles/org.openhab.binding.ntp/ @marcelrv
/bundles/org.openhab.binding.nuki/ @janvyb /bundles/org.openhab.binding.nuki/ @janvyb
@@ -252,12 +221,12 @@
/bundles/org.openhab.binding.omnikinverter/ @hansbogert /bundles/org.openhab.binding.omnikinverter/ @hansbogert
/bundles/org.openhab.binding.omnilink/ @ecdye /bundles/org.openhab.binding.omnilink/ @ecdye
/bundles/org.openhab.binding.onebusaway/ @sdwilsh /bundles/org.openhab.binding.onebusaway/ @sdwilsh
/bundles/org.openhab.binding.onewire/ @J-N-K /bundles/org.openhab.binding.onewire/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.onewiregpio/ @aogorek /bundles/org.openhab.binding.onewiregpio/ @aogorek
/bundles/org.openhab.binding.onkyo/ @pail23 @paulianttila /bundles/org.openhab.binding.onkyo/ @pail23 @paulianttila
/bundles/org.openhab.binding.opengarage/ @psmedley /bundles/org.openhab.binding.opengarage/ @psmedley
/bundles/org.openhab.binding.opensprinkler/ @CrackerStealth @FlorianSW /bundles/org.openhab.binding.opensprinkler/ @CrackerStealth @FlorianSW
/bundles/org.openhab.binding.openthermgateway/ @ArjenKorevaar @andrewfg /bundles/org.openhab.binding.openthermgateway/ @ArjenKorevaar
/bundles/org.openhab.binding.openuv/ @clinique /bundles/org.openhab.binding.openuv/ @clinique
/bundles/org.openhab.binding.openweathermap/ @cweitkamp /bundles/org.openhab.binding.openweathermap/ @cweitkamp
/bundles/org.openhab.binding.openwebnet/ @mvalla /bundles/org.openhab.binding.openwebnet/ @mvalla
@@ -273,19 +242,15 @@
/bundles/org.openhab.binding.pjlinkdevice/ @nils /bundles/org.openhab.binding.pjlinkdevice/ @nils
/bundles/org.openhab.binding.playstation/ @FluBBaOfWard /bundles/org.openhab.binding.playstation/ @FluBBaOfWard
/bundles/org.openhab.binding.plclogo/ @falkena /bundles/org.openhab.binding.plclogo/ @falkena
/bundles/org.openhab.binding.plex/ @aronbeurskens
/bundles/org.openhab.binding.plugwise/ @wborn /bundles/org.openhab.binding.plugwise/ @wborn
/bundles/org.openhab.binding.plugwiseha/ @lsiepel /bundles/org.openhab.binding.plugwiseha/ @lsiepel
/bundles/org.openhab.binding.powermax/ @lolodomo /bundles/org.openhab.binding.powermax/ @lolodomo
/bundles/org.openhab.binding.proteusecometer/ @2chilled /bundles/org.openhab.binding.proteusecometer/ @2chilled
/bundles/org.openhab.binding.prowl/ @octa22
/bundles/org.openhab.binding.publictransportswitzerland/ @jeremystucki
/bundles/org.openhab.binding.pulseaudio/ @peuter /bundles/org.openhab.binding.pulseaudio/ @peuter
/bundles/org.openhab.binding.pushbullet/ @hakan42 /bundles/org.openhab.binding.pushbullet/ @hakan42
/bundles/org.openhab.binding.pushover/ @cweitkamp /bundles/org.openhab.binding.pushover/ @cweitkamp
/bundles/org.openhab.binding.pushsafer/ @appzer @cweitkamp /bundles/org.openhab.binding.pushsafer/ @appzer @cweitkamp
/bundles/org.openhab.binding.qbus/ @QbusKoen /bundles/org.openhab.binding.qbus/ @QbusKoen
/bundles/org.openhab.binding.qolsysiq/ @digitaldan
/bundles/org.openhab.binding.radiothermostat/ @mlobstein /bundles/org.openhab.binding.radiothermostat/ @mlobstein
/bundles/org.openhab.binding.regoheatpump/ @crnjan /bundles/org.openhab.binding.regoheatpump/ @crnjan
/bundles/org.openhab.binding.revogi/ @andibraeu /bundles/org.openhab.binding.revogi/ @andibraeu
@@ -297,7 +262,7 @@
/bundles/org.openhab.binding.robonect/ @reyem /bundles/org.openhab.binding.robonect/ @reyem
/bundles/org.openhab.binding.roku/ @mlobstein /bundles/org.openhab.binding.roku/ @mlobstein
/bundles/org.openhab.binding.rotel/ @lolodomo /bundles/org.openhab.binding.rotel/ @lolodomo
/bundles/org.openhab.binding.russound/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.russound/ @tmrobert8
/bundles/org.openhab.binding.sagercaster/ @clinique /bundles/org.openhab.binding.sagercaster/ @clinique
/bundles/org.openhab.binding.samsungtv/ @paulianttila /bundles/org.openhab.binding.samsungtv/ @paulianttila
/bundles/org.openhab.binding.satel/ @druciak /bundles/org.openhab.binding.satel/ @druciak
@@ -306,46 +271,37 @@
/bundles/org.openhab.binding.seneye/ @nikotanghe /bundles/org.openhab.binding.seneye/ @nikotanghe
/bundles/org.openhab.binding.sensebox/ @hakan42 /bundles/org.openhab.binding.sensebox/ @hakan42
/bundles/org.openhab.binding.sensibo/ @seime /bundles/org.openhab.binding.sensibo/ @seime
/bundles/org.openhab.binding.sensorcommunity/ @weymann
/bundles/org.openhab.binding.serial/ @MikeJMajor /bundles/org.openhab.binding.serial/ @MikeJMajor
/bundles/org.openhab.binding.serialbutton/ @kaikreuzer /bundles/org.openhab.binding.serialbutton/ @kaikreuzer
/bundles/org.openhab.binding.shelly/ @markus7017 /bundles/org.openhab.binding.shelly/ @markus7017
/bundles/org.openhab.binding.siemensrds/ @andrewfg /bundles/org.openhab.binding.siemensrds/ @andrewfg
/bundles/org.openhab.binding.silvercrestwifisocket/ @jmvaz /bundles/org.openhab.binding.silvercrestwifisocket/ @jmvaz
/bundles/org.openhab.binding.sinope/ @chaton78 /bundles/org.openhab.binding.sinope/ @chaton78
/bundles/org.openhab.binding.sleepiq/ @syphr42 @mhilbush /bundles/org.openhab.binding.sleepiq/ @syphr42
/bundles/org.openhab.binding.smaenergymeter/ @monnimeter /bundles/org.openhab.binding.smaenergymeter/ @monnimeter
/bundles/org.openhab.binding.smartmeter/ @msteigenberger /bundles/org.openhab.binding.smartmeter/ @msteigenberger
/bundles/org.openhab.binding.smartthings/ @BobRak /bundles/org.openhab.binding.smartthings/ @BobRak
/bundles/org.openhab.binding.smgw/ @J-N-K
/bundles/org.openhab.binding.smhi/ @pacive /bundles/org.openhab.binding.smhi/ @pacive
/bundles/org.openhab.binding.smsmodem/ @dalgwen
/bundles/org.openhab.binding.sncf/ @clinique /bundles/org.openhab.binding.sncf/ @clinique
/bundles/org.openhab.binding.snmp/ @J-N-K /bundles/org.openhab.binding.snmp/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.solaredge/ @alexf2015 /bundles/org.openhab.binding.solaredge/ @alexf2015
/bundles/org.openhab.binding.solarlog/ @johannrichard /bundles/org.openhab.binding.solarlog/ @johannrichard
/bundles/org.openhab.binding.solarmax/ @jamietownsend
/bundles/org.openhab.binding.solarwatt/ @sven-carstens /bundles/org.openhab.binding.solarwatt/ @sven-carstens
/bundles/org.openhab.binding.solax/ @theater
/bundles/org.openhab.binding.somfymylink/ @loungeflyz /bundles/org.openhab.binding.somfymylink/ @loungeflyz
/bundles/org.openhab.binding.somfytahoma/ @octa22 /bundles/org.openhab.binding.somfytahoma/ @octa22
/bundles/org.openhab.binding.somneo/ @0x4d4d
/bundles/org.openhab.binding.sonnen/ @chingon007
/bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo /bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo
/bundles/org.openhab.binding.sonyaudio/ @freke /bundles/org.openhab.binding.sonyaudio/ @freke
/bundles/org.openhab.binding.sonyprojector/ @lolodomo /bundles/org.openhab.binding.sonyprojector/ @lolodomo
/bundles/org.openhab.binding.souliss/ @lucacalcaterra @fazioa /bundles/org.openhab.binding.souliss/ @lucacalcaterra @fazioa
/bundles/org.openhab.binding.speedtest/ @MikeTheTux
/bundles/org.openhab.binding.spotify/ @Hilbrand /bundles/org.openhab.binding.spotify/ @Hilbrand
/bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush /bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush
/bundles/org.openhab.binding.surepetcare/ @renescherer @HerzScheisse /bundles/org.openhab.binding.surepetcare/ @renescherer @HerzScheisse
/bundles/org.openhab.binding.synopanalyzer/ @clinique /bundles/org.openhab.binding.synopanalyzer/ @clinique
/bundles/org.openhab.binding.systeminfo/ @mherwege /bundles/org.openhab.binding.systeminfo/ @svilenvul
/bundles/org.openhab.binding.tacmi/ @twendt @Wolfgang1966 @marvkis /bundles/org.openhab.binding.tacmi/ @twendt @Wolfgang1966 @marvkis
/bundles/org.openhab.binding.tado/ @dfrommi @andrewfg /bundles/org.openhab.binding.tado/ @dfrommi
/bundles/org.openhab.binding.tankerkoenig/ @dolic @JueBag /bundles/org.openhab.binding.tankerkoenig/ @dolic @JueBag
/bundles/org.openhab.binding.tapocontrol/ @wildcs /bundles/org.openhab.binding.tapocontrol/ @wildcs
/bundles/org.openhab.binding.tasmotaplug/ @mlobstein
/bundles/org.openhab.binding.telegram/ @ZzetT /bundles/org.openhab.binding.telegram/ @ZzetT
/bundles/org.openhab.binding.teleinfo/ @Nokyyz @olivierkeke /bundles/org.openhab.binding.teleinfo/ @Nokyyz @olivierkeke
/bundles/org.openhab.binding.tellstick/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.tellstick/ @openhab/add-ons-maintainers
@@ -353,101 +309,85 @@
/bundles/org.openhab.binding.tibber/ @kjoglum /bundles/org.openhab.binding.tibber/ @kjoglum
/bundles/org.openhab.binding.tivo/ @mlobstein /bundles/org.openhab.binding.tivo/ @mlobstein
/bundles/org.openhab.binding.touchwand/ @roieg /bundles/org.openhab.binding.touchwand/ @roieg
/bundles/org.openhab.binding.tplinkrouter/ @olivierkeke
/bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand /bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand
/bundles/org.openhab.binding.tr064/ @J-N-K /bundles/org.openhab.binding.tr064/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer /bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
/bundles/org.openhab.binding.unifi/ @mgbowman @Hilbrand /bundles/org.openhab.binding.twitter/ @computergeek1507
/bundles/org.openhab.binding.unifi/ @mgbowman
/bundles/org.openhab.binding.unifiedremote/ @GiviMAD /bundles/org.openhab.binding.unifiedremote/ @GiviMAD
/bundles/org.openhab.binding.upb/ @marcusb /bundles/org.openhab.binding.upb/ @marcusb
/bundles/org.openhab.binding.upnpcontrol/ @mherwege /bundles/org.openhab.binding.upnpcontrol/ @mherwege
/bundles/org.openhab.binding.urtsi/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.urtsi/ @OLibutzki
/bundles/org.openhab.binding.valloxmv/ @bjoernbrings /bundles/org.openhab.binding.valloxmv/ @bjoernbrings
/bundles/org.openhab.binding.vdr/ @MatthiasKlocke /bundles/org.openhab.binding.vdr/ @MatthiasKlocke
/bundles/org.openhab.binding.vektiva/ @octa22 /bundles/org.openhab.binding.vektiva/ @octa22
/bundles/org.openhab.binding.velbus/ @cedricboon /bundles/org.openhab.binding.velbus/ @cedricboon
/bundles/org.openhab.binding.velux/ @gs4711 @andrewfg /bundles/org.openhab.binding.velux/ @gs4711
/bundles/org.openhab.binding.venstarthermostat/ @hww3 @digitaldan /bundles/org.openhab.binding.venstarthermostat/ @hww3 @digitaldan
/bundles/org.openhab.binding.ventaair/ @t2000 /bundles/org.openhab.binding.ventaair/ @t2000
/bundles/org.openhab.binding.verisure/ @jannegpriv /bundles/org.openhab.binding.verisure/ @jannegpriv
/bundles/org.openhab.binding.vesync/ @dag81
/bundles/org.openhab.binding.vigicrues/ @clinique /bundles/org.openhab.binding.vigicrues/ @clinique
/bundles/org.openhab.binding.vitotronic/ @steand /bundles/org.openhab.binding.vitotronic/ @steand
/bundles/org.openhab.binding.vizio/ @mlobstein /bundles/org.openhab.binding.volvooncall/ @clinique @Jamstah
/bundles/org.openhab.binding.volvooncall/ @Jamstah
/bundles/org.openhab.binding.warmup/ @jamesmelville /bundles/org.openhab.binding.warmup/ @jamesmelville
/bundles/org.openhab.binding.weathercompany/ @mhilbush /bundles/org.openhab.binding.weathercompany/ @mhilbush
/bundles/org.openhab.binding.weatherunderground/ @lolodomo /bundles/org.openhab.binding.weatherunderground/ @lolodomo
/bundles/org.openhab.binding.webexteams/ @tdeckers
/bundles/org.openhab.binding.webthing/ @grro /bundles/org.openhab.binding.webthing/ @grro
/bundles/org.openhab.binding.wemo/ @hmerk @jlaur /bundles/org.openhab.binding.wemo/ @hmerk
/bundles/org.openhab.binding.wifiled/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.wifiled/ @rvt @xylo
/bundles/org.openhab.binding.windcentrale/ @marcelrv @wborn /bundles/org.openhab.binding.windcentrale/ @marcelrv
/bundles/org.openhab.binding.wlanthermo/ @CSchlipp /bundles/org.openhab.binding.wlanthermo/ @CSchlipp
/bundles/org.openhab.binding.wled/ @Skinah /bundles/org.openhab.binding.wled/ @Skinah
/bundles/org.openhab.binding.wolfsmartset/ @BoBiene /bundles/org.openhab.binding.wolfsmartset/ @BoBiene
/bundles/org.openhab.binding.wundergroundupdatereceiver/ @danieldemus
/bundles/org.openhab.binding.x/ @computergeek1507
/bundles/org.openhab.binding.xmltv/ @clinique /bundles/org.openhab.binding.xmltv/ @clinique
/bundles/org.openhab.binding.xmppclient/ @pavel-gololobov /bundles/org.openhab.binding.xmppclient/ @pavel-gololobov
/bundles/org.openhab.binding.yamahamusiccast/ @coop-git /bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz
/bundles/org.openhab.binding.yamahareceiver/ @zarusz
/bundles/org.openhab.binding.yeelight/ @claell /bundles/org.openhab.binding.yeelight/ @claell
/bundles/org.openhab.binding.yioremote/ @miloit /bundles/org.openhab.binding.yioremote/ @miloit
/bundles/org.openhab.binding.volumio/ @miloit
/bundles/org.openhab.binding.zoneminder/ @mhilbush /bundles/org.openhab.binding.zoneminder/ @mhilbush
/bundles/org.openhab.binding.zway/ @pathec /bundles/org.openhab.binding.zway/ @pathec
/bundles/org.openhab.io.homekit/ @andylintner @ccutrer @yfre /bundles/org.openhab.io.homekit/ @beowulfe @yfre
/bundles/org.openhab.io.hueemulation/ @digitaldan /bundles/org.openhab.io.hueemulation/ @davidgraeff @digitaldan
/bundles/org.openhab.io.imperihome/ @pdegeus
/bundles/org.openhab.io.metrics/ @pravussum /bundles/org.openhab.io.metrics/ @pravussum
/bundles/org.openhab.io.neeo/ @morph166955 /bundles/org.openhab.io.neeo/ @tmrobert8
/bundles/org.openhab.io.openhabcloud/ @kaikreuzer /bundles/org.openhab.io.openhabcloud/ @kaikreuzer
/bundles/org.openhab.persistence.dynamodb/ @ssalonen /bundles/org.openhab.persistence.dynamodb/ @ssalonen
/bundles/org.openhab.persistence.influxdb/ @lujop /bundles/org.openhab.persistence.influxdb/ @lujop
/bundles/org.openhab.persistence.inmemory/ @J-N-K
/bundles/org.openhab.persistence.jdbc/ @openhab/add-ons-maintainers /bundles/org.openhab.persistence.jdbc/ @openhab/add-ons-maintainers
/bundles/org.openhab.persistence.jpa/ @openhab/add-ons-maintainers /bundles/org.openhab.persistence.jpa/ @openhab/add-ons-maintainers
/bundles/org.openhab.persistence.mapdb/ @openhab/add-ons-maintainers /bundles/org.openhab.persistence.mapdb/ @mkhl
/bundles/org.openhab.persistence.mongodb/ @openhab/add-ons-maintainers /bundles/org.openhab.persistence.mongodb/ @openhab/add-ons-maintainers
/bundles/org.openhab.persistence.rrd4j/ @openhab/add-ons-maintainers /bundles/org.openhab.persistence.rrd4j/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.bin2json/ @paulianttila /bundles/org.openhab.transform.bin2json/ @paulianttila
/bundles/org.openhab.transform.exec/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.exec/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.javascript/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.jinja/ @jochen314 /bundles/org.openhab.transform.jinja/ @jochen314
/bundles/org.openhab.transform.jsonpath/ @clinique /bundles/org.openhab.transform.jsonpath/ @clinique
/bundles/org.openhab.transform.map/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.map/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.regex/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.regex/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.rollershutterposition/ @jsjames
/bundles/org.openhab.transform.scale/ @clinique /bundles/org.openhab.transform.scale/ @clinique
/bundles/org.openhab.transform.vat/ @jlaur
/bundles/org.openhab.transform.xpath/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.xpath/ @openhab/add-ons-maintainers
/bundles/org.openhab.transform.xslt/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.xslt/ @openhab/add-ons-maintainers
/bundles/org.openhab.voice.googlestt/ @GiviMAD
/bundles/org.openhab.voice.googletts/ @gbicskei /bundles/org.openhab.voice.googletts/ @gbicskei
/bundles/org.openhab.voice.mactts/ @kaikreuzer /bundles/org.openhab.voice.mactts/ @kaikreuzer
/bundles/org.openhab.voice.marytts/ @kaikreuzer /bundles/org.openhab.voice.marytts/ @kaikreuzer
/bundles/org.openhab.voice.mimictts/ @dalgwen
/bundles/org.openhab.voice.picotts/ @FlorianSW /bundles/org.openhab.voice.picotts/ @FlorianSW
/bundles/org.openhab.voice.pollytts/ @openhab/add-ons-maintainers /bundles/org.openhab.voice.pollytts/ @hillmanr
/bundles/org.openhab.voice.rustpotterks/ @GiviMAD /bundles/org.openhab.voice.voicerss/ @JochenHiller
/bundles/org.openhab.voice.voicerss/ @lolodomo
/bundles/org.openhab.voice.voskstt/ @GiviMAD
/bundles/org.openhab.voice.watsonstt/ @GiviMAD
/itests/org.openhab.automation.groovyscripting.tests/ @wborn
/itests/org.openhab.automation.jsscriptingnashorn.tests/ @wborn
/itests/org.openhab.binding.astro.tests/ @gerrieg /itests/org.openhab.binding.astro.tests/ @gerrieg
/itests/org.openhab.binding.avmfritz.tests/ @cweitkamp /itests/org.openhab.binding.avmfritz.tests/ @cweitkamp
/itests/org.openhab.binding.feed.tests/ @openhab/add-ons-maintainers /itests/org.openhab.binding.feed.tests/ @svilenvul
/itests/org.openhab.binding.hue.tests/ @cweitkamp /itests/org.openhab.binding.hue.tests/ @cweitkamp
/itests/org.openhab.binding.max.tests/ @marcelrv /itests/org.openhab.binding.max.tests/ @marcelrv
/itests/org.openhab.binding.mielecloud.tests/ @BjoernLange
/itests/org.openhab.binding.modbus.tests/ @ssalonen /itests/org.openhab.binding.modbus.tests/ @ssalonen
/itests/org.openhab.binding.mqtt.homeassistant.tests/ @antroids /itests/org.openhab.binding.mqtt.homeassistant.tests/ @davidgraeff
/itests/org.openhab.binding.mqtt.homie.tests/ @openhab/add-ons-maintainers /itests/org.openhab.binding.mqtt.homie.tests/ @davidgraeff
/itests/org.openhab.binding.mqtt.ruuvigateway.tests/ @ssalonen /itests/org.openhab.binding.nest.tests/ @wborn
/itests/org.openhab.binding.ntp.tests/ @marcelrv /itests/org.openhab.binding.ntp.tests/ @marcelrv
/itests/org.openhab.binding.systeminfo.tests/ @mherwege /itests/org.openhab.binding.systeminfo.tests/ @svilenvul
/itests/org.openhab.binding.tradfri.tests/ @cweitkamp @kaikreuzer /itests/org.openhab.binding.tradfri.tests/ @cweitkamp @kaikreuzer
/itests/org.openhab.binding.wemo.tests/ @hmerk /itests/org.openhab.binding.wemo.tests/ @hmerk
/itests/org.openhab.persistence.mapdb.tests/ @openhab/add-ons-maintainers /itests/org.openhab.persistence.mapdb.tests/ @mkhl
# PLEASE HELP ADDING FURTHER LINES HERE! # PLEASE HELP ADDING FURTHER LINES HERE!

View File

@@ -2,10 +2,10 @@
<img align="right" width="220" src="./logo.png" /> <img align="right" width="220" src="./logo.png" />
[![GitHub Actions Build Status](https://github.com/openhab/openhab-addons/actions/workflows/ci-build.yml/badge.svg?branch=main)](https://github.com/openhab/openhab-addons/actions/workflows/ci-build.yml) [![Build Status](https://ci.openhab.org/job/openHAB-Addons/badge/icon)](https://ci.openhab.org/job/openHAB-Addons/)
[![Jenkins Build Status](https://ci.openhab.org/job/openHAB-Addons/badge/icon)](https://ci.openhab.org/job/openHAB-Addons/)
[![EPL-2.0](https://img.shields.io/badge/license-EPL%202-green.svg)](https://opensource.org/licenses/EPL-2.0) [![EPL-2.0](https://img.shields.io/badge/license-EPL%202-green.svg)](https://opensource.org/licenses/EPL-2.0)
[![Crowdin](https://badges.crowdin.net/openhab-addons/localized.svg)](https://crowdin.com/project/openhab-addons) [![Crowdin](https://badges.crowdin.net/openhab-addons/localized.svg)](https://crowdin.com/project/openhab-addons)
[![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=2164344)](https://www.bountysource.com/teams/openhab/issues?tracker_ids=2164344)
This repository contains the official set of add-ons that are implemented on top of openHAB Core APIs. This repository contains the official set of add-ons that are implemented on top of openHAB Core APIs.
Add-ons that got accepted in here will be maintained (e.g. adapted to new core APIs) Add-ons that got accepted in here will be maintained (e.g. adapted to new core APIs)
@@ -62,25 +62,7 @@ You find the following repository structure:
To build all add-ons from the command-line, type in: To build all add-ons from the command-line, type in:
```shell `mvn clean install`
mvn clean install
```
Most of the time you do not need to build all bindings, but only the binding you are working on.
To simply build only your binding use the `-pl` option.
For example to build only the astro binding:
```shell
mvn clean install -pl :org.openhab.binding.astro
```
If you have a binding that has dependencies that are dynamically as specified in the feature.xml you can create a `.kar` instead of a `.jar` file.
A `.kar` file will include the feature.xml and when added to openHAB will load and activate any dependencies specified in the feature.xml file.
To create a `.kar` file run maven with the goal `karaf:kar`:
```shell
mvn clean install karaf:kar -pl :org.openhab.binding.astro
```
To improve build times you can add the following options to the command: To improve build times you can add the following options to the command:
@@ -93,63 +75,19 @@ To improve build times you can add the following options to the command:
| `-Dspotless.check.skip=true` | Skip the Spotless code style checks | | `-Dspotless.check.skip=true` | Skip the Spotless code style checks |
| `-o` | Work offline so Maven does not download any updates | | `-o` | Work offline so Maven does not download any updates |
| `-T 1C` | Build in parallel, using 1 thread per core | | `-T 1C` | Build in parallel, using 1 thread per core |
| `-pl :<add-on directory>` | Build a single add-on |
For example you can skip checks and tests during development with: For example you can skip checks and tests during development with:
```shell `mvn clean install -DskipChecks -DskipTests`
mvn clean install -DskipChecks -DskipTests -pl :org.openhab.binding.astro
```
Adding these options improves the build time but could hide problems in your code. Adding these options improves the build time but could hide problems in your code.
Parallel builds are also less easy to debug and the increased load may cause timing sensitive tests to fail. Parallel builds are also less easy to debug and the increased load may cause timing sensitive tests to fail.
#### Translations To check if your code is following the [code style](https://www.openhab.org/docs/developer/guidelines.html#b-code-formatting-rules-style) run: `mvn spotless:check`
To reformat your code so it conforms to the code style you can run: `mvn spotless:apply`
Add-on translations are managed via [Crowdin](https://crowdin.com/project/openhab-addons).
The English translation is taken from the openHAB-addons GitHub repo and automatically imported in Crowdin when changes are made to the English i18n properties file.
When translations are added or updated and approved in Crowdin, a pull request is automatically created by Crowdin.
Therefore translations should not be edited in the openHAB-addons repo, but only in Crowdin.
Otherwise translation are overridden by the automatic process.
To fill the English properties file run the following maven command on an add-on:
```shell
mvn i18n:generate-default-translations
```
This command can also update the file when things or channel are added or updated.
In some cases the command does not work, and requires the full plug-in name.
In that case use:
```shell
mvn org.openhab.core.tools:i18n-maven-plugin:3.4.0:generate-default-translations
```
#### Code Quality
To check if your code is following the [code style](https://www.openhab.org/docs/developer/guidelines.html#b-code-formatting-rules-style) run:
```shell
mvn spotless:check
```
To reformat your code so it conforms to the code style you can run:
```shell
mvn spotless:apply
```
### Integration Tests
When your add-on also has an integration test in the `itests` directory, you may need to update the runbundles in the `itest.bndrun` file when the Maven dependencies change. When your add-on also has an integration test in the `itests` directory, you may need to update the runbundles in the `itest.bndrun` file when the Maven dependencies change.
Maven can resolve the integration test dependencies automatically by executing: Maven can resolve the integration test dependencies automatically by executing: `mvn clean install -DwithResolver -DskipChecks`
```shell
mvn clean install -DwithResolver -DskipChecks
```
The build generates a `.jar` file per bundle in the respective bundle `/target` directory. The build generates a `.jar` file per bundle in the respective bundle `/target` directory.

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bom</groupId> <groupId>org.openhab.addons.bom</groupId>
<artifactId>org.openhab.addons.reactor.bom</artifactId> <artifactId>org.openhab.addons.reactor.bom</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.addons.bom.openhab-addons</artifactId> <artifactId>org.openhab.addons.bom.openhab-addons</artifactId>
@@ -29,11 +29,6 @@
<artifactId>org.openhab.automation.jsscripting</artifactId> <artifactId>org.openhab.automation.jsscripting</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.automation.jsscriptingnashorn</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.automation.jythonscripting</artifactId> <artifactId>org.openhab.automation.jythonscripting</artifactId>
@@ -109,31 +104,16 @@
<artifactId>org.openhab.binding.androiddebugbridge</artifactId> <artifactId>org.openhab.binding.androiddebugbridge</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.androidtv</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.anel</artifactId> <artifactId>org.openhab.binding.anel</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.anthem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.astro</artifactId> <artifactId>org.openhab.binding.astro</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.asuswrt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.atlona</artifactId> <artifactId>org.openhab.binding.atlona</artifactId>
@@ -154,11 +134,6 @@
<artifactId>org.openhab.binding.avmfritz</artifactId> <artifactId>org.openhab.binding.avmfritz</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.awattar</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.benqprojector</artifactId> <artifactId>org.openhab.binding.benqprojector</artifactId>
@@ -219,16 +194,6 @@
<artifactId>org.openhab.binding.bluetooth.govee</artifactId> <artifactId>org.openhab.binding.bluetooth.govee</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.grundfosalpha</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.radoneye</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.roaming</artifactId> <artifactId>org.openhab.binding.bluetooth.roaming</artifactId>
@@ -241,7 +206,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bondhome</artifactId> <artifactId>org.openhab.binding.bmwconnecteddrive</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -289,11 +254,6 @@
<artifactId>org.openhab.binding.cbus</artifactId> <artifactId>org.openhab.binding.cbus</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.chatgpt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.chromecast</artifactId> <artifactId>org.openhab.binding.chromecast</artifactId>
@@ -334,6 +294,11 @@
<artifactId>org.openhab.binding.danfossairunit</artifactId> <artifactId>org.openhab.binding.danfossairunit</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.darksky</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.dbquery</artifactId> <artifactId>org.openhab.binding.dbquery</artifactId>
@@ -409,16 +374,6 @@
<artifactId>org.openhab.binding.dwdunwetter</artifactId> <artifactId>org.openhab.binding.dwdunwetter</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.easee</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.echonetlite</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ecobee</artifactId> <artifactId>org.openhab.binding.ecobee</artifactId>
@@ -431,12 +386,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ecovacs</artifactId> <artifactId>org.openhab.binding.ecowitt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ecowatt</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -444,31 +394,16 @@
<artifactId>org.openhab.binding.ekey</artifactId> <artifactId>org.openhab.binding.ekey</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.electroluxair</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.elerotransmitterstick</artifactId> <artifactId>org.openhab.binding.elerotransmitterstick</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.elroconnects</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.energenie</artifactId> <artifactId>org.openhab.binding.energenie</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.energidataservice</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.enigma2</artifactId> <artifactId>org.openhab.binding.enigma2</artifactId>
@@ -499,11 +434,6 @@
<artifactId>org.openhab.binding.etherrain</artifactId> <artifactId>org.openhab.binding.etherrain</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.evcc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.evohome</artifactId> <artifactId>org.openhab.binding.evohome</artifactId>
@@ -524,16 +454,6 @@
<artifactId>org.openhab.binding.feican</artifactId> <artifactId>org.openhab.binding.feican</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.fineoffsetweatherstation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.flicbutton</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.fmiweather</artifactId> <artifactId>org.openhab.binding.fmiweather</artifactId>
@@ -559,11 +479,6 @@
<artifactId>org.openhab.binding.freebox</artifactId> <artifactId>org.openhab.binding.freebox</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.freeboxos</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.fronius</artifactId> <artifactId>org.openhab.binding.fronius</artifactId>
@@ -604,11 +519,6 @@
<artifactId>org.openhab.binding.goecharger</artifactId> <artifactId>org.openhab.binding.goecharger</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.govee</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.gpio</artifactId> <artifactId>org.openhab.binding.gpio</artifactId>
@@ -629,16 +539,6 @@
<artifactId>org.openhab.binding.groheondus</artifactId> <artifactId>org.openhab.binding.groheondus</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.groupepsa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.guntamatic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.haassohnpelletstove</artifactId> <artifactId>org.openhab.binding.haassohnpelletstove</artifactId>
@@ -684,11 +584,6 @@
<artifactId>org.openhab.binding.heos</artifactId> <artifactId>org.openhab.binding.heos</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.herzborg</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.homeconnect</artifactId> <artifactId>org.openhab.binding.homeconnect</artifactId>
@@ -754,6 +649,11 @@
<artifactId>org.openhab.binding.ihc</artifactId> <artifactId>org.openhab.binding.ihc</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.innogysmarthome</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.insteon</artifactId> <artifactId>org.openhab.binding.insteon</artifactId>
@@ -804,16 +704,6 @@
<artifactId>org.openhab.binding.jeelink</artifactId> <artifactId>org.openhab.binding.jeelink</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.jellyfin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.juicenet</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.kaleidescape</artifactId> <artifactId>org.openhab.binding.kaleidescape</artifactId>
@@ -899,21 +789,11 @@
<artifactId>org.openhab.binding.linuxinput</artifactId> <artifactId>org.openhab.binding.linuxinput</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.liquidcheck</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.lirc</artifactId> <artifactId>org.openhab.binding.lirc</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.livisismarthome</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.logreader</artifactId> <artifactId>org.openhab.binding.logreader</artifactId>
@@ -926,12 +806,12 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.lutron</artifactId> <artifactId>org.openhab.binding.luftdateninfo</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.luxom</artifactId> <artifactId>org.openhab.binding.lutron</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -954,21 +834,11 @@
<artifactId>org.openhab.binding.max</artifactId> <artifactId>org.openhab.binding.max</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mcd</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mcp23017</artifactId> <artifactId>org.openhab.binding.mcp23017</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.meater</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mecmeter</artifactId> <artifactId>org.openhab.binding.mecmeter</artifactId>
@@ -979,11 +849,6 @@
<artifactId>org.openhab.binding.melcloud</artifactId> <artifactId>org.openhab.binding.melcloud</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mercedesme</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.meteoalerte</artifactId> <artifactId>org.openhab.binding.meteoalerte</artifactId>
@@ -1111,17 +976,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mycroft</artifactId> <artifactId>org.openhab.binding.myq</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mybmw</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mynice</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -1189,11 +1044,6 @@
<artifactId>org.openhab.binding.nikohomecontrol</artifactId> <artifactId>org.openhab.binding.nikohomecontrol</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.nobohub</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.novafinedust</artifactId> <artifactId>org.openhab.binding.novafinedust</artifactId>
@@ -1349,11 +1199,6 @@
<artifactId>org.openhab.binding.plclogo</artifactId> <artifactId>org.openhab.binding.plclogo</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.plex</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.plugwise</artifactId> <artifactId>org.openhab.binding.plugwise</artifactId>
@@ -1374,16 +1219,6 @@
<artifactId>org.openhab.binding.proteusecometer</artifactId> <artifactId>org.openhab.binding.proteusecometer</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.prowl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.publictransportswitzerland</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.pulseaudio</artifactId> <artifactId>org.openhab.binding.pulseaudio</artifactId>
@@ -1409,11 +1244,6 @@
<artifactId>org.openhab.binding.qbus</artifactId> <artifactId>org.openhab.binding.qbus</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.qolsysiq</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.radiothermostat</artifactId> <artifactId>org.openhab.binding.radiothermostat</artifactId>
@@ -1514,11 +1344,6 @@
<artifactId>org.openhab.binding.sensibo</artifactId> <artifactId>org.openhab.binding.sensibo</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.sensorcommunity</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.serial</artifactId> <artifactId>org.openhab.binding.serial</artifactId>
@@ -1569,21 +1394,11 @@
<artifactId>org.openhab.binding.smartthings</artifactId> <artifactId>org.openhab.binding.smartthings</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.smgw</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.smhi</artifactId> <artifactId>org.openhab.binding.smhi</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.smsmodem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.sncf</artifactId> <artifactId>org.openhab.binding.sncf</artifactId>
@@ -1604,21 +1419,11 @@
<artifactId>org.openhab.binding.solarlog</artifactId> <artifactId>org.openhab.binding.solarlog</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solarmax</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solarwatt</artifactId> <artifactId>org.openhab.binding.solarwatt</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solax</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.somfymylink</artifactId> <artifactId>org.openhab.binding.somfymylink</artifactId>
@@ -1629,16 +1434,6 @@
<artifactId>org.openhab.binding.somfytahoma</artifactId> <artifactId>org.openhab.binding.somfytahoma</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.somneo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.sonnen</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.sonos</artifactId> <artifactId>org.openhab.binding.sonos</artifactId>
@@ -1659,11 +1454,6 @@
<artifactId>org.openhab.binding.souliss</artifactId> <artifactId>org.openhab.binding.souliss</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.speedtest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.spotify</artifactId> <artifactId>org.openhab.binding.spotify</artifactId>
@@ -1709,11 +1499,6 @@
<artifactId>org.openhab.binding.tapocontrol</artifactId> <artifactId>org.openhab.binding.tapocontrol</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.tasmotaplug</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.telegram</artifactId> <artifactId>org.openhab.binding.telegram</artifactId>
@@ -1749,11 +1534,6 @@
<artifactId>org.openhab.binding.touchwand</artifactId> <artifactId>org.openhab.binding.touchwand</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.tplinkrouter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.tplinksmarthome</artifactId> <artifactId>org.openhab.binding.tplinksmarthome</artifactId>
@@ -1769,6 +1549,11 @@
<artifactId>org.openhab.binding.tradfri</artifactId> <artifactId>org.openhab.binding.tradfri</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.twitter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.unifi</artifactId> <artifactId>org.openhab.binding.unifi</artifactId>
@@ -1834,11 +1619,6 @@
<artifactId>org.openhab.binding.verisure</artifactId> <artifactId>org.openhab.binding.verisure</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.vesync</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.vigicrues</artifactId> <artifactId>org.openhab.binding.vigicrues</artifactId>
@@ -1849,21 +1629,11 @@
<artifactId>org.openhab.binding.vitotronic</artifactId> <artifactId>org.openhab.binding.vitotronic</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.vizio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.volvooncall</artifactId> <artifactId>org.openhab.binding.volvooncall</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.volumio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.warmup</artifactId> <artifactId>org.openhab.binding.warmup</artifactId>
@@ -1879,11 +1649,6 @@
<artifactId>org.openhab.binding.weatherunderground</artifactId> <artifactId>org.openhab.binding.weatherunderground</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.webexteams</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.webthing</artifactId> <artifactId>org.openhab.binding.webthing</artifactId>
@@ -1919,16 +1684,6 @@
<artifactId>org.openhab.binding.wolfsmartset</artifactId> <artifactId>org.openhab.binding.wolfsmartset</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.wundergroundupdatereceiver</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.x</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.xmltv</artifactId> <artifactId>org.openhab.binding.xmltv</artifactId>
@@ -1939,11 +1694,6 @@
<artifactId>org.openhab.binding.xmppclient</artifactId> <artifactId>org.openhab.binding.xmppclient</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.yamahamusiccast</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.yamahareceiver</artifactId> <artifactId>org.openhab.binding.yamahareceiver</artifactId>
@@ -1979,6 +1729,11 @@
<artifactId>org.openhab.io.hueemulation</artifactId> <artifactId>org.openhab.io.hueemulation</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.imperihome</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.metrics</artifactId> <artifactId>org.openhab.io.metrics</artifactId>
@@ -2004,11 +1759,6 @@
<artifactId>org.openhab.persistence.influxdb</artifactId> <artifactId>org.openhab.persistence.influxdb</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.persistence.inmemory</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.persistence.jdbc</artifactId> <artifactId>org.openhab.persistence.jdbc</artifactId>
@@ -2044,6 +1794,11 @@
<artifactId>org.openhab.transform.exec</artifactId> <artifactId>org.openhab.transform.exec</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.javascript</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.jinja</artifactId> <artifactId>org.openhab.transform.jinja</artifactId>
@@ -2064,21 +1819,11 @@
<artifactId>org.openhab.transform.regex</artifactId> <artifactId>org.openhab.transform.regex</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.rollershutterposition</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.scale</artifactId> <artifactId>org.openhab.transform.scale</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.vat</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.transform.xpath</artifactId> <artifactId>org.openhab.transform.xpath</artifactId>
@@ -2089,11 +1834,6 @@
<artifactId>org.openhab.transform.xslt</artifactId> <artifactId>org.openhab.transform.xslt</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.googlestt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.googletts</artifactId> <artifactId>org.openhab.voice.googletts</artifactId>
@@ -2109,11 +1849,6 @@
<artifactId>org.openhab.voice.marytts</artifactId> <artifactId>org.openhab.voice.marytts</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.mimictts</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.picotts</artifactId> <artifactId>org.openhab.voice.picotts</artifactId>
@@ -2124,26 +1859,11 @@
<artifactId>org.openhab.voice.pollytts</artifactId> <artifactId>org.openhab.voice.pollytts</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.rustpotterks</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.voicerss</artifactId> <artifactId>org.openhab.voice.voicerss</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.voskstt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.voice.watsonstt</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bom</groupId> <groupId>org.openhab.addons.bom</groupId>
<artifactId>org.openhab.addons.reactor.bom</artifactId> <artifactId>org.openhab.addons.reactor.bom</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.addons.bom.openhab-core-index</artifactId> <artifactId>org.openhab.addons.bom.openhab-core-index</artifactId>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons</groupId> <groupId>org.openhab.addons</groupId>
<artifactId>org.openhab.addons.reactor</artifactId> <artifactId>org.openhab.addons.reactor</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<groupId>org.openhab.addons.bom</groupId> <groupId>org.openhab.addons.bom</groupId>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bom</groupId> <groupId>org.openhab.addons.bom</groupId>
<artifactId>org.openhab.addons.reactor.bom</artifactId> <artifactId>org.openhab.addons.reactor.bom</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.addons.bom.runtime-index</artifactId> <artifactId>org.openhab.addons.bom.runtime-index</artifactId>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bom</groupId> <groupId>org.openhab.addons.bom</groupId>
<artifactId>org.openhab.addons.reactor.bom</artifactId> <artifactId>org.openhab.addons.reactor.bom</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.addons.bom.test-index</artifactId> <artifactId>org.openhab.addons.bom.test-index</artifactId>

View File

@@ -10,7 +10,7 @@ IF %ARGC% NEQ 3 (
exit /B 1 exit /B 1
) )
SET OpenhabVersion="4.1.0-SNAPSHOT" SET OpenhabVersion="3.2.0-SNAPSHOT"
SET BindingIdInCamelCase=%~1 SET BindingIdInCamelCase=%~1
SET BindingIdInLowerCase=%BindingIdInCamelCase% SET BindingIdInLowerCase=%BindingIdInCamelCase%

View File

@@ -2,7 +2,7 @@
[ $# -lt 3 ] && { echo "Usage: $0 <BindingIdInCamelCase> <Author> <GitHub Username>"; exit 1; } [ $# -lt 3 ] && { echo "Usage: $0 <BindingIdInCamelCase> <Author> <GitHub Username>"; exit 1; }
openHABVersion=4.1.0-SNAPSHOT openHABVersion=3.2.0-SNAPSHOT
camelcaseId=$1 camelcaseId=$1
id=`echo $camelcaseId | tr '[:upper:]' '[:lower:]'` id=`echo $camelcaseId | tr '[:upper:]' '[:lower:]'`

View File

@@ -1,6 +1,6 @@
# Groovy Scripting # Groovy Scripting
This add-on provides support for [Groovy](https://groovy-lang.org/) 4.0.11 that can be used as a scripting language within automation rules and which eliminates the need to manually install Groovy. This add-on provides support for [Groovy](https://groovy-lang.org/) 3.0.9 that can be used as a scripting language within automation rules and which eliminates the need to manually install Groovy.
## Creating Groovy Scripts ## Creating Groovy Scripts
@@ -31,7 +31,7 @@ The openHAB server uses the [SLF4J](https://www.slf4j.org/) library for logging.
```groovy ```groovy
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello, World!") LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello world!")
``` ```
Depending on the openHAB logging configuration, you may need to prefix logger names with `org.openhab.core.automation` for them to show up in the log file (or you modify the logging configuration). Depending on the openHAB logging configuration, you may need to prefix logger names with `org.openhab.core.automation` for them to show up in the log file (or you modify the logging configuration).
@@ -39,5 +39,5 @@ Depending on the openHAB logging configuration, you may need to prefix logger na
The script uses the [LoggerFactory](https://www.slf4j.org/apidocs/org/slf4j/Logger.html) to obtain a named logger and then logs a message like: The script uses the [LoggerFactory](https://www.slf4j.org/apidocs/org/slf4j/Logger.html) to obtain a named logger and then logs a message like:
```text ```text
... [INFO ] [.openhab.core.automation.examples:-2 ] - Hello, World! ... [INFO ] [.openhab.core.automation.examples:-2 ] - Hello world!
``` ```

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.automation.groovyscripting</artifactId> <artifactId>org.openhab.automation.groovyscripting</artifactId>
@@ -13,65 +13,23 @@
<name>openHAB Add-ons :: Bundles :: Automation :: Groovy Scripting</name> <name>openHAB Add-ons :: Bundles :: Automation :: Groovy Scripting</name>
<properties> <properties>
<bnd.importpackage>com.ibm.icu.*;resolution:=optional,groovy.runtime.metaclass;resolution:=optional,groovyjarjarantlr4.stringtemplate;resolution:=optional,org.abego.treelayout.*;resolution:=optional,org.apache.ivy.*;resolution:=optional,org.fusesource.jansi.*;resolution:=optional,org.stringtemplate.v4.*;resolution:=optional</bnd.importpackage> <bnd.importpackage>com.ibm.icu.*;resolution:=optional,groovy.runtime.metaclass;resolution:=optional,groovyjarjarantlr4.stringtemplate;resolution:=optional,org.abego.treelayout.*;resolution:=optional,org.apache.ivy.*;resolution:=optional,org.stringtemplate.v4.*;resolution:=optional</bnd.importpackage>
<groovy.version>4.0.11</groovy.version> <groovy.version>3.0.9</groovy.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.apache.groovy</groupId> <groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId> <artifactId>groovy</artifactId>
<version>${groovy.version}</version> <version>${groovy.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.groovy</groupId> <groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-jsr223</artifactId> <artifactId>groovy-jsr223</artifactId>
<version>${groovy.version}</version> <version>${groovy.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-json</artifactId>
<version>${groovy.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
<version>${groovy.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-yaml</artifactId>
<version>${groovy.version}</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
<configuration>
<bnd><![CDATA[${oh.bndDefaults}
Require-Capability:
osgi.extender:=
filter:="(osgi.extender=osgi.serviceloader.processor)",
osgi.serviceloader:=
filter:="(osgi.serviceloader=org.apache.groovy.json.FastStringServiceFactory)";
cardinality:=multiple
SPI-Provider: *
SPI-Consumer: *
]]>
</bnd>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project> </project>

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -12,7 +12,6 @@
*/ */
package org.openhab.automation.groovyscripting.internal; package org.openhab.automation.groovyscripting.internal;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -21,13 +20,10 @@ import javax.script.ScriptEngine;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.OpenHAB;
import org.openhab.core.automation.module.script.AbstractScriptEngineFactory; import org.openhab.core.automation.module.script.AbstractScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import groovy.lang.GroovyClassLoader;
/** /**
* This is an implementation of a {@link ScriptEngineFactory} for Groovy. * This is an implementation of a {@link ScriptEngineFactory} for Groovy.
* *
@@ -37,21 +33,12 @@ import groovy.lang.GroovyClassLoader;
@NonNullByDefault @NonNullByDefault
public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory { public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory {
private static final String FILE_DIRECTORY = "automation" + File.separator + "groovy";
private final org.codehaus.groovy.jsr223.GroovyScriptEngineFactory factory = new org.codehaus.groovy.jsr223.GroovyScriptEngineFactory(); private final org.codehaus.groovy.jsr223.GroovyScriptEngineFactory factory = new org.codehaus.groovy.jsr223.GroovyScriptEngineFactory();
private final List<String> scriptTypes = (List<String>) Stream.of(factory.getExtensions(), factory.getMimeTypes()) private final List<String> scriptTypes = (List<String>) Stream.of(factory.getExtensions(), factory.getMimeTypes())
.flatMap(List::stream) // .flatMap(List::stream) //
.collect(Collectors.toUnmodifiableList()); .collect(Collectors.toUnmodifiableList());
private final GroovyClassLoader gcl = new GroovyClassLoader(GroovyScriptEngineFactory.class.getClassLoader());
public GroovyScriptEngineFactory() {
String scriptDir = OpenHAB.getConfigFolder() + File.separator + FILE_DIRECTORY;
logger.debug("Adding script directory {} to the GroovyScriptEngine class path.", scriptDir);
gcl.addClasspath(scriptDir);
}
@Override @Override
public List<String> getScriptTypes() { public List<String> getScriptTypes() {
return scriptTypes; return scriptTypes;
@@ -59,9 +46,6 @@ public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory {
@Override @Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) { public @Nullable ScriptEngine createScriptEngine(String scriptType) {
if (scriptTypes.contains(scriptType)) { return scriptTypes.contains(scriptType) ? factory.getScriptEngine() : null;
return new org.codehaus.groovy.jsr223.GroovyScriptEngineImpl(gcl);
}
return null;
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="groovyscripting" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>automation</type>
<name>Groovy Scripting</name>
<description>This adds a Groovy script engine.</description>
<connection>none</connection>
</addon:addon>

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.automation.jrubyscripting</artifactId> <artifactId>org.openhab.automation.jrubyscripting</artifactId>
@@ -13,8 +13,8 @@
<name>openHAB Add-ons :: Bundles :: Automation :: JRuby Scripting</name> <name>openHAB Add-ons :: Bundles :: Automation :: JRuby Scripting</name>
<properties> <properties>
<bnd.importpackage>com.sun.nio.*;resolution:=optional,com.sun.security.*;resolution:=optional,org.apache.tools.ant.*;resolution:=optional,org.bouncycastle.*;resolution:=optional,org.joda.*;resolution:=optional,sun.management.*;resolution:=optional,sun.nio.*;resolution:=optional,jakarta.annotation;resolution:=optional</bnd.importpackage> <bnd.importpackage>com.sun.nio.*;resolution:=optional,com.sun.security.*;resolution:=optional,org.apache.tools.ant.*;resolution:=optional,org.bouncycastle.*;resolution:=optional,org.joda.*;resolution:=optional,sun.management.*;resolution:=optional,sun.nio.*;resolution:=optional</bnd.importpackage>
<jruby.version>9.4.5.0</jruby.version> <jruby.version>9.3.1.0</jruby.version>
</properties> </properties>
<dependencies> <dependencies>

View File

@@ -1,218 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal;
import java.io.Reader;
import java.util.Objects;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.jruby.embed.jsr223.JRubyEngine;
/**
* This is a wrapper for {@link JRubyEngine}.
*
* The purpose of this class is to intercept the call to eval and save the context into
* a global variable for use in the helper library.
*
* @author Jimmy Tanagra - Initial contribution
*/
@NonNullByDefault
public class JRubyEngineWrapper implements Compilable, Invocable, ScriptEngine {
private final JRubyEngine engine;
private static final String CONTEXT_VAR_NAME = "ctx";
private static final String GLOBAL_VAR_NAME = "$" + CONTEXT_VAR_NAME;
JRubyEngineWrapper(JRubyEngine engine) {
this.engine = Objects.requireNonNull(engine);
}
@Override
public CompiledScript compile(@Nullable String script) throws ScriptException {
return engine.compile(script);
}
@Override
public CompiledScript compile(@Nullable Reader reader) throws ScriptException {
return engine.compile(reader);
}
@Override
public Object eval(@Nullable String script, @Nullable ScriptContext context) throws ScriptException {
Object ctx = Objects.requireNonNull(context).getBindings(ScriptContext.ENGINE_SCOPE).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(script, context);
}
context.setAttribute(GLOBAL_VAR_NAME, ctx, ScriptContext.ENGINE_SCOPE);
try {
return engine.eval(script, context);
} finally {
context.removeAttribute(GLOBAL_VAR_NAME, ScriptContext.ENGINE_SCOPE);
}
}
@Override
public Object eval(@Nullable Reader reader, @Nullable ScriptContext context) throws ScriptException {
Object ctx = Objects.requireNonNull(context).getBindings(ScriptContext.ENGINE_SCOPE).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(reader, context);
}
context.setAttribute(GLOBAL_VAR_NAME, ctx, ScriptContext.ENGINE_SCOPE);
try {
return engine.eval(reader, context);
} finally {
context.removeAttribute(GLOBAL_VAR_NAME, ScriptContext.ENGINE_SCOPE);
}
}
@Override
public Object eval(@Nullable String script, @Nullable Bindings bindings) throws ScriptException {
Object ctx = Objects.requireNonNull(bindings).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(script, bindings);
}
bindings.put(GLOBAL_VAR_NAME, ctx);
try {
return engine.eval(script, bindings);
} finally {
bindings.remove(GLOBAL_VAR_NAME);
}
}
@Override
public Object eval(@Nullable Reader reader, @Nullable Bindings bindings) throws ScriptException {
Object ctx = Objects.requireNonNull(bindings).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(reader, bindings);
}
bindings.put(GLOBAL_VAR_NAME, ctx);
try {
return engine.eval(reader, bindings);
} finally {
bindings.remove(GLOBAL_VAR_NAME);
}
}
@Override
public Object eval(@Nullable String script) throws ScriptException {
Object ctx = getBindings(ScriptContext.ENGINE_SCOPE).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(script);
}
getContext().setAttribute(GLOBAL_VAR_NAME, ctx, ScriptContext.ENGINE_SCOPE);
try {
return engine.eval(script);
} finally {
getContext().removeAttribute(GLOBAL_VAR_NAME, ScriptContext.ENGINE_SCOPE);
}
}
@Override
public Object eval(@Nullable Reader reader) throws ScriptException {
Object ctx = getBindings(ScriptContext.ENGINE_SCOPE).get(CONTEXT_VAR_NAME);
if (ctx == null) {
return engine.eval(reader);
}
getContext().setAttribute(GLOBAL_VAR_NAME, ctx, ScriptContext.ENGINE_SCOPE);
try {
return engine.eval(reader);
} finally {
getContext().removeAttribute(GLOBAL_VAR_NAME, ScriptContext.ENGINE_SCOPE);
}
}
@Override
public Object get(@Nullable String key) {
return engine.get(key);
}
@Override
public void put(@Nullable String key, @Nullable Object value) {
engine.put(key, value);
}
@Override
public Bindings getBindings(int scope) {
return engine.getBindings(scope);
}
@Override
public void setBindings(@Nullable Bindings bindings, int scope) {
engine.setBindings(bindings, scope);
}
@Override
public Bindings createBindings() {
return engine.createBindings();
}
@Override
public ScriptContext getContext() {
return engine.getContext();
}
@Override
public void setContext(@Nullable ScriptContext context) {
engine.setContext(context);
}
@Override
public ScriptEngineFactory getFactory() {
return engine.getFactory();
}
@Override
public Object invokeMethod(@Nullable Object receiver, @Nullable String method, Object @Nullable... args)
throws ScriptException, NoSuchMethodException {
return engine.invokeMethod(receiver, method, args);
}
@Override
public Object invokeFunction(@Nullable String method, Object @Nullable... args)
throws ScriptException, NoSuchMethodException {
return engine.invokeFunction(method, args);
}
@Override
public <T> T getInterface(@Nullable Class<T> returnType) {
return engine.getInterface(returnType);
}
@Override
public <T> T getInterface(@Nullable Object receiver, @Nullable Class<T> returnType) {
return engine.getInterface(receiver, returnType);
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -13,21 +13,20 @@
package org.openhab.automation.jrubyscripting.internal; package org.openhab.automation.jrubyscripting.internal;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Collectors;
import javax.script.ScriptContext;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineFactory;
import javax.script.ScriptException; import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.jruby.runtime.Constants;
import org.openhab.core.OpenHAB; import org.openhab.core.OpenHAB;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -36,56 +35,41 @@ import org.slf4j.LoggerFactory;
* Processes JRuby Configuration Parameters. * Processes JRuby Configuration Parameters.
* *
* @author Brian O'Connell - Initial contribution * @author Brian O'Connell - Initial contribution
* @author Jimmy Tanagra - Add $LOAD_PATH, require injection
*/ */
@NonNullByDefault @NonNullByDefault
public class JRubyScriptEngineConfiguration { public class JRubyScriptEngineConfiguration {
private final Logger logger = LoggerFactory.getLogger(JRubyScriptEngineConfiguration.class); private final Logger logger = LoggerFactory.getLogger(JRubyScriptEngineConfiguration.class);
private static final String RUBY_ENGINE_REPLACEMENT = "{RUBY_ENGINE}"; private static final Path DEFAULT_GEM_HOME = Paths.get(OpenHAB.getConfigFolder(), "scripts", "lib", "ruby",
private static final String RUBY_ENGINE_VERSION_REPLACEMENT = "{RUBY_ENGINE_VERSION}"; "gem_home");
private static final String RUBY_VERSION_REPLACEMENT = "{RUBY_VERSION}";
private static final List<String> REPLACEMENTS = List.of(RUBY_ENGINE_REPLACEMENT, RUBY_ENGINE_VERSION_REPLACEMENT,
RUBY_VERSION_REPLACEMENT);
private static final String DEFAULT_GEM_HOME = Paths private static final Path DEFAULT_RUBYLIB = Paths.get(OpenHAB.getConfigFolder(), "automation", "lib", "ruby");
.get(OpenHAB.getConfigFolder(), "automation", "ruby", ".gem", RUBY_ENGINE_VERSION_REPLACEMENT).toString();
private static final String DEFAULT_RUBYLIB = Paths.get(OpenHAB.getConfigFolder(), "automation", "ruby", "lib")
.toString();
private static final String GEM_HOME_CONFIG_KEY = "gem_home"; private static final String GEM_HOME = "gem_home";
private static final String RUBYLIB_CONFIG_KEY = "rubylib";
private static final String GEMS_CONFIG_KEY = "gems";
private static final String REQUIRE_CONFIG_KEY = "require";
private static final String CHECK_UPDATE_CONFIG_KEY = "check_update";
private static final String DEPENDENCY_TRACKING_CONFIG_KEY = "dependency_tracking";
// Map of configuration parameters // Map of configuration parameters
private final Map<String, OptionalConfigurationElement> configurationParameters = Map.ofEntries( private static final Map<String, OptionalConfigurationElement> CONFIGURATION_PARAMETERS = Map.ofEntries(
Map.entry("local_context", Map.entry("local_context",
new OptionalConfigurationElement(OptionalConfigurationElement.Type.SYSTEM_PROPERTY, "singlethread", new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.SYSTEM_PROPERTY)
"org.jruby.embed.localcontext.scope")), .mappedTo("org.jruby.embed.localcontext.scope").defaultValue("singlethread").build()),
Map.entry("local_variable", Map.entry("local_variable",
new OptionalConfigurationElement(OptionalConfigurationElement.Type.SYSTEM_PROPERTY, "transient", new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.SYSTEM_PROPERTY)
"org.jruby.embed.localvariable.behavior")), .mappedTo("org.jruby.embed.localvariable.behavior").defaultValue("transient").build()),
Map.entry(GEM_HOME_CONFIG_KEY, Map.entry(GEM_HOME,
new OptionalConfigurationElement(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT, new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT)
DEFAULT_GEM_HOME, "GEM_HOME")), .mappedTo("GEM_HOME").defaultValue(DEFAULT_GEM_HOME.toString()).build()),
Map.entry(RUBYLIB_CONFIG_KEY, Map.entry("rubylib",
new OptionalConfigurationElement(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT, new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT)
DEFAULT_RUBYLIB, "RUBYLIB")), .mappedTo("RUBYLIB").defaultValue(DEFAULT_RUBYLIB.toString()).build()),
Map.entry(GEMS_CONFIG_KEY, new OptionalConfigurationElement("openhab-scripting=~>5.0")), Map.entry("gems", new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.GEM).build()));
Map.entry(REQUIRE_CONFIG_KEY, new OptionalConfigurationElement("openhab/dsl")), private static final Map<OptionalConfigurationElement.Type, List<OptionalConfigurationElement>> CONFIGURATION_TYPE_MAP = CONFIGURATION_PARAMETERS
.values().stream().collect(Collectors.groupingBy(v -> v.type));
Map.entry(CHECK_UPDATE_CONFIG_KEY, new OptionalConfigurationElement("true")),
Map.entry(DEPENDENCY_TRACKING_CONFIG_KEY, new OptionalConfigurationElement("true")));
/** /**
* Update configuration * Update configuration
@@ -95,14 +79,8 @@ public class JRubyScriptEngineConfiguration {
*/ */
void update(Map<String, Object> config, ScriptEngineFactory factory) { void update(Map<String, Object> config, ScriptEngineFactory factory) {
logger.trace("JRuby Script Engine Configuration: {}", config); logger.trace("JRuby Script Engine Configuration: {}", config);
configurationParameters.forEach((k, v) -> v.clearValue());
config.forEach(this::processConfigValue); config.forEach(this::processConfigValue);
configureScriptEngine(factory);
configureSystemProperties();
ScriptEngine engine = factory.getScriptEngine();
configureRubyEnvironment(engine);
configureGems(engine);
} }
/** /**
@@ -112,300 +90,202 @@ public class JRubyScriptEngineConfiguration {
* @param value Configuration value * @param value Configuration value
*/ */
private void processConfigValue(String key, Object value) { private void processConfigValue(String key, Object value) {
OptionalConfigurationElement configurationElement = configurationParameters.get(key); OptionalConfigurationElement configurationElement = CONFIGURATION_PARAMETERS.get(key);
if (configurationElement != null) { if (configurationElement != null) {
configurationElement.setValue(value.toString().trim()); configurationElement.setValue(value.toString());
} else { } else {
logger.debug("Ignoring unexpected configuration key: {}", key); logger.debug("Ignoring unexpected configuration key: {}", key);
} }
} }
/** /**
* Gets a single configuration element. * Configure the ScriptEngine
*/
private String get(String key) {
OptionalConfigurationElement configElement = configurationParameters.get(key);
return Objects.requireNonNull(configElement).getValue();
}
/**
* Gets the concrete gem home to install gems into for this version of JRuby.
* *
* {RUBY_ENGINE} and {RUBY_VERSION} are replaced with their current actual values. * @param factory Script Engine to configure
*/ */
public String getSpecificGemHome() { void configureScriptEngine(ScriptEngineFactory factory) {
String gemHome = get(GEM_HOME_CONFIG_KEY); configureSystemProperties(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.SYSTEM_PROPERTY,
if (gemHome.isEmpty()) { Collections.<OptionalConfigurationElement> emptyList()));
return gemHome;
}
gemHome = gemHome.replace(RUBY_ENGINE_REPLACEMENT, Constants.ENGINE); ScriptEngine engine = factory.getScriptEngine();
gemHome = gemHome.replace(RUBY_ENGINE_VERSION_REPLACEMENT, Constants.VERSION);
gemHome = gemHome.replace(RUBY_VERSION_REPLACEMENT, Constants.RUBY_VERSION);
return new File(gemHome).toString();
}
/** configureRubyEnvironment(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT,
* Get the base for all possible gem homes. Collections.<OptionalConfigurationElement> emptyList()), engine);
*
* If the configured gem home contains {RUBY_ENGINE} or {RUBY_VERSION},
* the path is cut off at that point. This means a single configuration
* value will include the gem homes for all parallel-installed ruby
* versions.
*
*/
public String getGemHomeBase() {
String gemHome = get(GEM_HOME_CONFIG_KEY);
for (String replacement : REPLACEMENTS) { configureGems(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.GEM,
int loc = gemHome.indexOf(replacement); Collections.<OptionalConfigurationElement> emptyList()), engine);
if (loc != -1) {
gemHome = gemHome.substring(0, loc);
}
}
return new File(gemHome).toString();
} }
/** /**
* Makes Gem home directory if it does not exist * Makes Gem home directory if it does not exist
*/ */
private boolean ensureGemHomeExists(String gemHome) { private void ensureGemHomeExists() {
File gemHomeDirectory = new File(gemHome); OptionalConfigurationElement gemHomeConfigElement = CONFIGURATION_PARAMETERS.get(GEM_HOME);
if (!gemHomeDirectory.exists()) { if (gemHomeConfigElement != null) {
logger.debug("gem_home directory does not exist, creating"); Optional<String> gemHome = gemHomeConfigElement.getValue();
if (!gemHomeDirectory.mkdirs()) { if (gemHome.isPresent()) {
logger.warn("Error creating gem_home directory"); File gemHomeDirectory = new File(gemHome.get());
return false; if (!gemHomeDirectory.exists()) {
logger.debug("gem_home directory does not exist, creating");
if (!gemHomeDirectory.mkdirs()) {
logger.debug("Error creating gem_home direcotry");
}
}
} else {
logger.debug("Gem install requested without gem_home specified, not ensuring gem_home path exists");
} }
} }
return true;
} }
/** /**
* Install a gems in ScriptEngine * Install a gems in ScriptEngine
* *
* @param gemsDirectives List of gems to install
* @param engine Engine to install gems * @param engine Engine to install gems
*/ */
private synchronized void configureGems(ScriptEngine engine) { private synchronized void configureGems(List<OptionalConfigurationElement> gemDirectives, ScriptEngine engine) {
String gems = get(GEMS_CONFIG_KEY); for (OptionalConfigurationElement gemDirective : gemDirectives) {
if (gems.isEmpty()) { if (gemDirective.getValue().isPresent()) {
return; ensureGemHomeExists();
}
String gemHome = getSpecificGemHome(); String[] gems = gemDirective.getValue().get().split(",");
if (gemHome.isEmpty()) { for (String gem : gems) {
logger.warn("Gem install requested with empty gem_home, not installing gems."); gem = gem.trim();
return; String gemCommand;
} if (gem.contains("=")) {
String[] gemParts = gem.split("=");
gem = gemParts[0];
String version = gemParts[1];
gemCommand = "Gem.install('" + gem + "',version='" + version + "')\n";
} else {
gemCommand = "Gem.install('" + gem + "')\n";
}
if (!ensureGemHomeExists(gemHome)) { try {
return; logger.debug("Installing Gem: {} ", gem);
} logger.trace("Gem install code:\n{}\n", gemCommand);
engine.eval(gemCommand);
boolean checkUpdate = "true".equals(get(CHECK_UPDATE_CONFIG_KEY)); } catch (Exception e) {
logger.error("Error installing Gem", e);
String[] gemsArray = gems.split(","); }
// Set update_native_env_enabled to false so that bundler doesn't leak
// into other script engines
String gemCommand = "require 'jruby'\nJRuby.runtime.instance_config.update_native_env_enabled = false\nrequire 'bundler/inline'\nrequire 'openssl'\n\ngemfile("
+ checkUpdate + ") do\n" + " source 'https://rubygems.org/'\n";
int validGems = 0;
for (String gem : gemsArray) {
gem = gem.trim();
String[] versions = {};
if (gem.contains("=")) {
String[] gemParts = gem.split("=", 2);
gem = gemParts[0].trim();
versions = gemParts[1].split(";");
}
if (gem.isEmpty()) {
continue;
}
gemCommand += " gem '" + gem + "'";
for (String version : versions) {
version = version.trim();
if (!version.isEmpty()) {
gemCommand += ", '" + version + "'";
} }
} else {
logger.debug("Ruby gem property has no value");
} }
gemCommand += ", require: false\n";
validGems += 1;
}
if (validGems == 0) {
return;
}
gemCommand += "end\n";
try {
logger.debug("Installing Gems");
logger.trace("Gem install code:\n{}", gemCommand);
engine.eval(gemCommand);
} catch (ScriptException e) {
logger.warn("Error installing Gems", unwrap(e));
} }
} }
/** /**
* Execute ruby require statement in the ScriptEngine * Configure the base Ruby Environment
* *
* @param engine Engine to insert the require statements * @param engine Engine to configure
*/ */
public void injectRequire(ScriptEngine engine) { public ScriptEngine configureRubyEnvironment(ScriptEngine engine) {
String requires = get(REQUIRE_CONFIG_KEY); configureRubyEnvironment(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT,
Collections.<OptionalConfigurationElement> emptyList()), engine);
if (requires.isEmpty()) { return engine;
return;
}
Stream.of(requires.split(",")).map(s -> s.trim()).filter(s -> !s.isEmpty()).forEach(script -> {
final String requireStatement = String.format("require '%s'", script);
try {
logger.trace("Injecting require statement: {}", requireStatement);
engine.eval(requireStatement);
} catch (ScriptException e) {
logger.warn("Error evaluating `{}`", requireStatement, unwrap(e));
}
});
} }
/** /**
* Configure the optional elements of the Ruby Environment * Configure the optional elements of the Ruby Environment
* *
* @param scriptEngine Engine in which to configure environment * @param optionalConfigurationElements Optional elements to configure in the ruby environment
*/
public void configureRubyEnvironment(ScriptEngine scriptEngine) {
getConfigurationElements(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT).forEach(configElement -> {
String value;
if ("GEM_HOME".equals(configElement.mappedTo().get())) {
// this value has to be post-processed to handle replacements.
value = getSpecificGemHome();
} else {
value = configElement.getValue();
}
scriptEngine.put("__key", configElement.mappedTo().get());
scriptEngine.put("__value", value);
logger.trace("Setting Ruby environment ENV['{}''] = '{}'", configElement.mappedTo().get(), value);
try {
scriptEngine.eval("ENV[__key] = __value");
} catch (ScriptException e) {
logger.warn("Error setting Ruby environment", unwrap(e));
}
// clean up our temporary variables
scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).remove("__key");
scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).remove("__value");
});
configureRubyLib(scriptEngine);
}
/**
* Split up and insert ENV['RUBYLIB'] into Ruby's $LOAD_PATH
* This needs to be called after ENV['RUBYLIB'] has been set by configureRubyEnvironment
*
* @param engine Engine in which to configure environment * @param engine Engine in which to configure environment
*/ */
private void configureRubyLib(ScriptEngine engine) { private void configureRubyEnvironment(List<OptionalConfigurationElement> optionalConfigurationElements,
String rubyLib = get(RUBYLIB_CONFIG_KEY); ScriptEngine engine) {
if (!rubyLib.isEmpty()) { for (OptionalConfigurationElement configElement : optionalConfigurationElements) {
final String code = "$LOAD_PATH.unshift *ENV['RUBYLIB']&.split(File::PATH_SEPARATOR)" + // String environmentProperty = configElement.mappedTo().get();
"&.reject(&:empty?)" + // if (configElement.getValue().isPresent()) {
"&.reject { |path| $LOAD_PATH.include?(path) }"; // String environmentSetting = "ENV['" + environmentProperty + "']='" + configElement.getValue().get()
try { + "'";
engine.eval(code); try {
} catch (ScriptException exception) { logger.trace("Setting Ruby environment with code: {} ", environmentSetting);
logger.warn("Error setting $LOAD_PATH from RUBYLIB='{}'", rubyLib, unwrap(exception)); engine.eval(environmentSetting);
} catch (ScriptException e) {
logger.error("Error setting ruby environment", e);
}
} else {
logger.debug("Ruby environment property ({}) has no value", environmentProperty);
} }
} }
} }
public List<String> getRubyLibPaths() {
String rubyLib = get(RUBYLIB_CONFIG_KEY);
if (rubyLib.isEmpty()) {
return List.of();
}
return List.of(rubyLib.split(File.pathSeparator));
}
public boolean enableDependencyTracking() {
return "true".equals(get(DEPENDENCY_TRACKING_CONFIG_KEY));
}
/** /**
* Configure system properties * Configure system properties
* *
* @param optionalConfigurationElements Optional system properties to configure * @param optionalConfigurationElements Optional system properties to configure
*/ */
private void configureSystemProperties() { private void configureSystemProperties(List<OptionalConfigurationElement> optionalConfigurationElements) {
getConfigurationElements(OptionalConfigurationElement.Type.SYSTEM_PROPERTY).forEach(configElement -> { for (OptionalConfigurationElement configElement : optionalConfigurationElements) {
String systemProperty = configElement.mappedTo().get(); String systemProperty = configElement.mappedTo().get();
String propertyValue = configElement.getValue(); if (configElement.getValue().isPresent()) {
logger.trace("Setting system property ({}) to ({})", systemProperty, propertyValue); String propertyValue = configElement.getValue().get();
System.setProperty(systemProperty, propertyValue); logger.trace("Setting system property ({}) to ({})", systemProperty, propertyValue);
}); System.setProperty(systemProperty, propertyValue);
} } else {
logger.warn("System property ({}) has no value", systemProperty);
private Stream<OptionalConfigurationElement> getConfigurationElements(OptionalConfigurationElement.Type type) { }
return configurationParameters.values().stream().filter(element -> element.type.equals(type));
}
/**
* Unwraps the cause of an exception, if it has one.
*
* Since a user cares about the _Ruby_ stack trace of the throwable, not
* the details of where openHAB called it.
*/
private Throwable unwrap(Throwable e) {
Throwable cause = e.getCause();
if (cause != null) {
return cause;
} }
return e;
} }
/** /**
* Inner static companion class for configuration elements * Inner static companion class for configuration elements
*/ */
private static class OptionalConfigurationElement { private static class OptionalConfigurationElement {
private enum Type {
SYSTEM_PROPERTY,
RUBY_ENVIRONMENT,
OTHER
}
private final String defaultValue; private final Optional<String> defaultValue;
private final Optional<String> mappedTo; private final Optional<String> mappedTo;
private final Type type; private final Type type;
private Optional<String> value; private Optional<String> value;
private OptionalConfigurationElement(String defaultValue) { private OptionalConfigurationElement(Type type, @Nullable String mappedTo, @Nullable String defaultValue) {
this(Type.OTHER, defaultValue, null);
}
private OptionalConfigurationElement(Type type, String defaultValue, @Nullable String mappedTo) {
this.type = type; this.type = type;
this.defaultValue = defaultValue; this.defaultValue = Optional.ofNullable(defaultValue);
this.mappedTo = Optional.ofNullable(mappedTo); this.mappedTo = Optional.ofNullable(mappedTo);
value = Optional.empty(); value = Optional.empty();
} }
private String getValue() { private Optional<String> getValue() {
return value.orElse(defaultValue); return value.or(() -> defaultValue);
} }
private void setValue(String value) { private void setValue(String value) {
this.value = Optional.of(value); this.value = Optional.of(value);
} }
private void clearValue() {
this.value = Optional.empty();
}
private Optional<String> mappedTo() { private Optional<String> mappedTo() {
return mappedTo; return mappedTo;
} }
private enum Type {
SYSTEM_PROPERTY,
RUBY_ENVIRONMENT,
GEM
}
private static class Builder {
private final Type type;
private @Nullable String defaultValue = null;
private @Nullable String mappedTo = null;
private Builder(Type type) {
this.type = type;
}
private Builder mappedTo(String mappedTo) {
this.mappedTo = mappedTo;
return this;
}
private Builder defaultValue(String value) {
this.defaultValue = value;
return this;
}
private OptionalConfigurationElement build() {
return new OptionalConfigurationElement(type, mappedTo, defaultValue);
}
}
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -12,88 +12,77 @@
*/ */
package org.openhab.automation.jrubyscripting.internal; package org.openhab.automation.jrubyscripting.internal;
import java.io.File;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.script.ScriptContext;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.automation.jrubyscripting.internal.watch.JRubyDependencyTracker;
import org.openhab.core.automation.module.script.AbstractScriptEngineFactory; import org.openhab.core.automation.module.script.AbstractScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptExtensionManagerWrapper;
import org.openhab.core.config.core.ConfigurableService; import org.openhab.core.config.core.ConfigurableService;
import org.openhab.core.service.WatchService;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
/** /**
* This is an implementation of a {@link ScriptEngineFactory} for Ruby. * This is an implementation of a {@link ScriptEngineFactory} for Ruby.
* *
* @author Brian O'Connell - Initial contribution * @author Brian O'Connell - Initial contribution
* @author Jimmy Tanagra - Add require injection
*/ */
@NonNullByDefault @NonNullByDefault
@Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.automation.jrubyscripting", property = Constants.SERVICE_PID @Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.automation.jrubyscripting")
+ "=org.openhab.automation.jrubyscripting") @ConfigurableService(category = "automation", label = "JRuby Scripting", description_uri = "automation:jruby")
@ConfigurableService(category = "automation", label = "JRuby Scripting", description_uri = "automation:jrubyscripting")
public class JRubyScriptEngineFactory extends AbstractScriptEngineFactory { public class JRubyScriptEngineFactory extends AbstractScriptEngineFactory {
private final JRubyScriptEngineConfiguration configuration = new JRubyScriptEngineConfiguration(); private final JRubyScriptEngineConfiguration configuration = new JRubyScriptEngineConfiguration();
// Filter out the File entry to prevent shadowing the Ruby File class which breaks Ruby in spectacularly
// difficult ways to debug.
private static final Set<String> FILTERED_PRESETS = Set.of("File");
private static final Set<String> INSTANCE_PRESETS = Set.of();
private static final Set<String> GLOBAL_PRESETS = Set.of("scriptExtension", "automationManager", "ruleRegistry",
"items", "voice", "rules", "things", "events", "itemRegistry", "ir", "actions", "se", "audio",
"lifecycleTracker");
private final javax.script.ScriptEngineFactory factory = new org.jruby.embed.jsr223.JRubyEngineFactory(); private final javax.script.ScriptEngineFactory factory = new org.jruby.embed.jsr223.JRubyEngineFactory();
private final List<String> scriptTypes = Stream.concat(Objects.requireNonNull(factory.getExtensions()).stream(), private final List<String> scriptTypes = Stream
Objects.requireNonNull(factory.getMimeTypes()).stream()).toList(); .concat(factory.getExtensions().stream(), factory.getMimeTypes().stream())
.collect(Collectors.toUnmodifiableList());
private final JRubyDependencyTracker jrubyDependencyTracker; // Adds @ in front of a set of variables so that Ruby recogonizes them as instance variables
private static Map.Entry<String, Object> mapInstancePresets(Map.Entry<String, Object> entry) {
if (INSTANCE_PRESETS.contains(entry.getKey())) {
return Map.entry("@" + entry.getKey(), entry.getValue());
} else {
return entry;
}
}
// Adds $ in front of a set of variables so that Ruby recognizes them as global // Adds $ in front of a set of variables so that Ruby recogonizes them as global variables
// variables
private static Map.Entry<String, Object> mapGlobalPresets(Map.Entry<String, Object> entry) { private static Map.Entry<String, Object> mapGlobalPresets(Map.Entry<String, Object> entry) {
if (Character.isLowerCase(entry.getKey().charAt(0)) && !(entry.getValue() instanceof Class) if (GLOBAL_PRESETS.contains(entry.getKey())) {
&& !(entry.getValue() instanceof Enum)) {
return Map.entry("$" + entry.getKey(), entry.getValue()); return Map.entry("$" + entry.getKey(), entry.getValue());
} else { } else {
return entry; return entry;
} }
} }
// The activate call activates the automation and sets its configuration
@Activate @Activate
public JRubyScriptEngineFactory(@Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService, protected void activate(Map<String, Object> config) {
Map<String, Object> config) { configuration.update(config, factory);
jrubyDependencyTracker = new JRubyDependencyTracker(watchService, this);
modified(config);
}
@Deactivate
protected void deactivate() {
jrubyDependencyTracker.deactivate();
} }
// The modified call updates configuration for the automation // The modified call updates configuration for the automation
@Modified @Modified
protected void modified(Map<String, Object> config) { protected void modified(Map<String, Object> config) {
configuration.update(config, factory); configuration.update(config, factory);
// Re-initialize the dependency tracker's watchers.
jrubyDependencyTracker.deactivate();
if (configuration.enableDependencyTracking()) {
jrubyDependencyTracker.activate();
}
} }
@Override @Override
@@ -103,102 +92,22 @@ public class JRubyScriptEngineFactory extends AbstractScriptEngineFactory {
@Override @Override
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) { public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
// Empty comments prevent the formatter from breaking up the correct streams // Empty comments prevent the formatter from breaking up the correct streams chaining
// chaining
logger.debug("Scope Values: {}", scopeValues);
Map<String, Object> filteredScopeValues = // Map<String, Object> filteredScopeValues = //
scopeValues // scopeValues //
.entrySet() // .entrySet() //
.stream() // .stream() //
.filter(map -> !FILTERED_PRESETS.contains(map.getKey())) //
.map(JRubyScriptEngineFactory::mapInstancePresets) //
.map(JRubyScriptEngineFactory::mapGlobalPresets) // .map(JRubyScriptEngineFactory::mapGlobalPresets) //
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); // .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); //
Map<Boolean, Map<String, Object>> partitionedMap = // super.scopeValues(scriptEngine, filteredScopeValues);
filteredScopeValues.entrySet() //
.stream() //
.collect(Collectors.partitioningBy(entry -> (entry.getValue() instanceof Class),
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
super.scopeValues(scriptEngine, partitionedMap.getOrDefault(false, new HashMap<>()));
importClassesToRuby(scriptEngine, partitionedMap.getOrDefault(true, new HashMap<>()));
Object scriptExtension = scopeValues.get("scriptExtension");
if (scriptExtension instanceof ScriptExtensionManagerWrapper wrapper
&& configuration.enableDependencyTracking()) {
// we inject like this instead of using the script context, because
// this is executed _before_ the dependency tracker is added to the script
// context.
// But we need this set up before we inject our requires
scriptEngine.put("$dependencyListener", jrubyDependencyTracker.getTracker(wrapper.getScriptIdentifier()));
}
// scopeValues is called twice. The first call only passed 'se'. The second call
// passed the rest of the
// presets, including 'ir'. We wait for the second call before running the
// require statements.
if (scopeValues.containsKey("ir")) {
configuration.injectRequire(scriptEngine);
}
}
private void importClassesToRuby(ScriptEngine scriptEngine, Map<String, Object> objects) {
try {
scriptEngine.put("__classes", objects);
final String code = "__classes.each { |(name, klass)| Object.const_set(name, klass.ruby_class) unless Object.const_defined?(name, false) }";
scriptEngine.eval(code);
// clean up our temporary variable
scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).remove("__classes");
} catch (ScriptException e) {
logger.debug("Error importing java classes", e);
}
} }
@Override @Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) { public @Nullable ScriptEngine createScriptEngine(String scriptType) {
if (!scriptTypes.contains(scriptType)) { return scriptTypes.contains(scriptType) ? configuration.configureRubyEnvironment(factory.getScriptEngine())
return null; : null;
}
ScriptEngine engine = factory.getScriptEngine();
configuration.configureRubyEnvironment(engine);
return new JRubyEngineWrapper((org.jruby.embed.jsr223.JRubyEngine) engine);
}
@Override
public @Nullable ScriptDependencyTracker getDependencyTracker() {
return jrubyDependencyTracker;
}
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeChangeTracker")
public void addChangeTracker(ScriptDependencyTracker.Listener listener) {
jrubyDependencyTracker.addChangeTracker(listener);
}
public void removeChangeTracker(ScriptDependencyTracker.Listener listener) {
jrubyDependencyTracker.removeChangeTracker(listener);
}
public List<String> getRubyLibPaths() {
return configuration.getRubyLibPaths();
}
public boolean isFileInLoadPath(String file) {
for (String path : getRubyLibPaths()) {
if (file.startsWith(new File(path).toString() + File.separator)) {
return true;
}
}
return false;
}
public String getGemHome() {
return configuration.getSpecificGemHome();
}
public boolean isFileInGemHome(String file) {
String gemHome = configuration.getGemHomeBase();
if (gemHome.isEmpty()) {
return false;
}
return file.startsWith(gemHome + File.separator);
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,111 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.jdt.annotation.NonNullByDefault;
// Copy of org.openhab.core.automation.module.script.rulesupport.internal.loader.collection.BidiSetBag
/**
* Bidirectional bag of unique elements. A map allowing multiple, unique values to be stored against a single key.
* Provides optimized lookup of values for a key, as well as keys referencing a value.
*
* @author Jonathan Gilbert - Initial contribution
* @author Jan N. Klug - Make implementation thread-safe
* @param <K> Type of Key
* @param <V> Type of Value
*/
@NonNullByDefault
public class BidiSetBag<K, V> {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Map<K, Set<V>> keyToValues = new HashMap<>();
private final Map<V, Set<K>> valueToKeys = new HashMap<>();
public void put(K key, V value) {
lock.writeLock().lock();
try {
keyToValues.computeIfAbsent(key, k -> new HashSet<>()).add(value);
valueToKeys.computeIfAbsent(value, v -> new HashSet<>()).add(key);
} finally {
lock.writeLock().unlock();
}
}
public Set<V> getValues(K key) {
lock.readLock().lock();
try {
Set<V> values = keyToValues.getOrDefault(key, Set.of());
return Collections.unmodifiableSet(values);
} finally {
lock.readLock().unlock();
}
}
public Set<K> getKeys(V value) {
lock.readLock().lock();
try {
Set<K> keys = valueToKeys.getOrDefault(value, Set.of());
return Collections.unmodifiableSet(keys);
} finally {
lock.readLock().unlock();
}
}
public Set<V> removeKey(K key) {
lock.writeLock().lock();
try {
Set<V> values = keyToValues.remove(key);
if (values != null) {
for (V value : values) {
valueToKeys.computeIfPresent(value, (k, v) -> {
v.remove(key);
return v;
});
}
return values;
} else {
return Set.of();
}
} finally {
lock.writeLock().unlock();
}
}
public Set<K> removeValue(V value) {
lock.writeLock().lock();
try {
Set<K> keys = valueToKeys.remove(value);
if (keys != null) {
for (K key : keys) {
keyToValues.computeIfPresent(key, (k, v) -> {
v.remove(value);
return v;
});
}
return keys;
} else {
return Set.of();
}
} finally {
lock.writeLock().unlock();
}
}
}

View File

@@ -1,106 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.service.WatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Tracks Ruby dependencies
*
* @author Cody Cutrer - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/
@NonNullByDefault
public class JRubyDependencyTracker implements ScriptDependencyTracker {
private final Logger logger = LoggerFactory.getLogger(JRubyDependencyTracker.class);
private final Set<ScriptDependencyTracker.Listener> dependencyChangeListeners = ConcurrentHashMap.newKeySet();
private final BidiSetBag<String, String> scriptToLibs = new BidiSetBag<>();
private final JRubyScriptEngineFactory scriptEngineFactory;
private final List<JRubyWatchService> dependencyWatchServices = new ArrayList<>();
private final WatchService watchService;
public JRubyDependencyTracker(final WatchService watchService, final JRubyScriptEngineFactory scriptEngineFactory) {
this.watchService = watchService;
this.scriptEngineFactory = scriptEngineFactory;
}
public void activate() {
String gemHome = scriptEngineFactory.getGemHome();
if (!gemHome.isEmpty()) {
dependencyWatchServices.add(new JRubyGemWatchService(watchService, gemHome, this));
}
List<Path> libPaths = scriptEngineFactory.getRubyLibPaths().stream().map(Path::of).toList();
dependencyWatchServices.add(new JRubyLibWatchService(watchService, libPaths, this));
dependencyWatchServices.forEach(JRubyWatchService::activate);
}
public void deactivate() {
dependencyWatchServices.forEach(JRubyWatchService::deactivate);
dependencyWatchServices.clear();
}
void dependencyChanged(String dependency) {
Set<String> scripts = new HashSet<>(scriptToLibs.getKeys(dependency)); // take a copy as it will change as we
logger.debug("{} changed; reimporting {} scripts...", dependency, scripts.size());
for (String scriptUrl : scripts) {
for (ScriptDependencyTracker.Listener listener : dependencyChangeListeners) {
try {
listener.onDependencyChange(scriptUrl);
} catch (Exception e) {
logger.warn("Failed to notify tracker of dependency change: {}: {}", e.getClass(), e.getMessage());
}
}
}
}
@Override
public Consumer<String> getTracker(String scriptId) {
return dependencyPath -> startTracking(scriptId, dependencyPath);
}
@Override
public void removeTracking(String scriptId) {
scriptToLibs.removeKey(scriptId);
}
protected void startTracking(String scriptId, String libPath) {
scriptToLibs.put(scriptId, libPath);
}
public void addChangeTracker(ScriptDependencyTracker.Listener listener) {
logger.trace("adding change tracker listener {}", listener);
dependencyChangeListeners.add(listener);
}
public void removeChangeTracker(ScriptDependencyTracker.Listener listener) {
logger.trace("removing change tracker listener {}", listener);
dependencyChangeListeners.remove(listener);
}
}

View File

@@ -1,72 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import java.nio.file.Path;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.service.WatchService;
/**
* Watches a gem home
*
* @author Cody Cutrer - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/
@NonNullByDefault
public class JRubyGemWatchService implements JRubyWatchService, WatchService.WatchEventListener {
private static final String GEMSPEC = ".gemspec";
private final WatchService watchService;
private final Path path;
private JRubyDependencyTracker dependencyTracker;
JRubyGemWatchService(WatchService watchService, String path, JRubyDependencyTracker dependencyTracker) {
this.watchService = watchService;
this.dependencyTracker = dependencyTracker;
this.path = Path.of(path);
}
@Override
public void activate() {
watchService.registerListener(this, path);
}
@Override
public void deactivate() {
watchService.unregisterListener(this);
}
@Override
public void processWatchEvent(WatchService.Kind kind, Path path) {
String file = path.toFile().getName();
if (file.endsWith(GEMSPEC)) {
// This seems really lazy, but you can't definitively tell the name
// of a gem from the gemspec's filename. It's simply too ambiguous with version
// numbers and platforms allowed to have `-` and `_` characters as well. RubyGems
// doesn't do it either - it either has the name already, and searches for
// `<name>-*.gemspec`, or it completely lists the all files on disk. Either way
// it then executes the gemspec to get full details. We can't do that here in
// pure Java and without a JRubyEngine available. So just punt and invalidate
// _all_ subsets of hyphens. Worst case we invalidate a "parent" gem that didn't
// need to be invalidated, but oh well, that just means a script reloads sometimes
// when it didn't absolutely need to.
String[] parts = file.split("-");
for (int i = 0; i < parts.length - 1; ++i) {
dependencyTracker.dependencyChanged("gem:" + String.join("-", Arrays.copyOf(parts, i + 1)));
}
}
}
}

View File

@@ -1,60 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import static org.openhab.core.service.WatchService.Kind.*;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.service.WatchService;
import org.openhab.core.service.WatchService.Kind;
/**
* Watches a Ruby lib dir
*
* @author Cody Cutrer - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/
@NonNullByDefault
public class JRubyLibWatchService implements JRubyWatchService, WatchService.WatchEventListener {
private final JRubyDependencyTracker dependencyTracker;
private final WatchService watchService;
private final List<Path> paths;
JRubyLibWatchService(WatchService watchService, List<Path> paths, JRubyDependencyTracker dependencyTracker) {
this.watchService = watchService;
this.dependencyTracker = dependencyTracker;
this.paths = paths;
}
@Override
public void activate() {
watchService.registerListener(this, paths);
}
@Override
public void deactivate() {
watchService.unregisterListener(this);
}
@Override
public void processWatchEvent(Kind kind, Path path) {
File file = path.toFile();
if (!file.isHidden() && (kind == DELETE || (file.canRead() && (kind == CREATE || kind == MODIFY)))) {
dependencyTracker.dependencyChanged(file.getPath());
}
}
}

View File

@@ -1,72 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import java.io.File;
import java.nio.file.Path;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptEngineManager;
import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher;
import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.service.WatchService;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Monitors {@code <openHAB-conf>/automation/ruby} for Ruby files, but not libraries in lib or gems
*
* @author Cody Cutrer - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/
@Component(immediate = true, service = { ScriptFileWatcher.class, ScriptDependencyTracker.Listener.class })
@NonNullByDefault
public class JRubyScriptFileWatcher extends AbstractScriptFileWatcher {
private final Logger logger = LoggerFactory.getLogger(JRubyScriptFileWatcher.class);
private static final String FILE_DIRECTORY = "automation" + File.separator + "ruby";
private final JRubyScriptEngineFactory scriptEngineFactory;
@Activate
public JRubyScriptFileWatcher(final @Reference ScriptEngineManager manager,
final @Reference ReadyService readyService, final @Reference StartLevelService startLevelService,
final @Reference(target = "(" + Constants.SERVICE_PID
+ "=org.openhab.automation.jrubyscripting)") ScriptEngineFactory scriptEngineFactory,
final @Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService) {
super(watchService, manager, readyService, startLevelService, FILE_DIRECTORY, true);
this.scriptEngineFactory = (JRubyScriptEngineFactory) scriptEngineFactory;
}
@Override
protected Optional<String> getScriptType(Path scriptFilePath) {
String path = scriptFilePath.toString();
if (scriptEngineFactory.isFileInGemHome(path) || scriptEngineFactory.isFileInLoadPath(path)) {
return Optional.empty();
}
return super.getScriptType(scriptFilePath).filter(type -> scriptEngineFactory.getScriptTypes().contains(type));
}
}

View File

@@ -1,34 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jrubyscripting.internal.watch;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link JRubyWatchService} is an interface for controlling internal watch services
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public interface JRubyWatchService {
/**
* start watching
*/
void activate();
/**
* stop watching
*/
void deactivate();
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="jrubyscripting" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>automation</type>
<name>JRuby Scripting</name>
<description>This adds a JRuby script engine.</description>
<connection>none</connection>
<service-id>org.openhab.automation.jrubyscripting</service-id>
<config-description-ref uri="automation:jrubyscripting"/>
</addon:addon>

View File

@@ -4,18 +4,7 @@
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0" xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd"> https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="automation:jrubyscripting"> <config-description uri="automation:jruby">
<parameter-group name="gems">
<label>Ruby Gems</label>
<description>This group defines the list of Ruby Gems to install.</description>
</parameter-group>
<parameter-group name="environment">
<label>Ruby Environment</label>
<description>This group defines Ruby's environment.</description>
<advanced>true</advanced>
</parameter-group>
<parameter-group name="system"> <parameter-group name="system">
<label>System Properties</label> <label>System Properties</label>
@@ -23,61 +12,22 @@
<advanced>true</advanced> <advanced>true</advanced>
</parameter-group> </parameter-group>
<parameter name="gems" type="text" required="false" groupName="gems"> <parameter-group name="environment">
<label>Ruby Environment</label>
<description>This group defines Ruby's environment.</description>
<advanced>false</advanced>
</parameter-group>
<parameter-group name="gems">
<label>Ruby Gems</label> <label>Ruby Gems</label>
<description><![CDATA[A comma separated list of Ruby Gems to install. Versions may be constrained by separating with an <description>This group defines the list of Ruby Gems to install.</description>
<tt>=</tt> and then the standard RubyGems version constraint, such as "<tt>openhab-scripting=~>5.0</tt>". <advanced>false</advanced>
]]></description> </parameter-group>
<default>openhab-scripting=~>5.0.0</default>
</parameter>
<parameter name="require" type="text" required="false" groupName="gems">
<label>Require Scripts</label>
<description>A comma separated list of script names to be required by the JRuby Scripting Engine before running user
scripts.</description>
<default>openhab/dsl</default>
</parameter>
<parameter name="check_update" type="boolean" required="true" groupName="gems">
<label>Check for Gem Updates</label>
<description>Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed.
Otherwise it will try to fulfill the requirements with locally installed gems, and you can manage them yourself with
an external Ruby by setting the same GEM_HOME.</description>
<default>true</default>
<advanced>true</advanced>
</parameter>
<parameter name="gem_home" type="text" required="false" groupName="environment">
<label>GEM_HOME</label>
<description><![CDATA[Location Ruby Gems will be installed to and loaded from. Directory will be created if necessary.
You can use <tt>{RUBY_ENGINE_VERSION}</tt>, <tt>{RUBY_ENGINE}</tt> and/or <tt>{RUBY_VERSION}</tt> replacements in this value to automatically point to
a new directory when the addon is updated with a new version of JRuby.
Defaults to "<tt>OPENHAB_CONF/automation/ruby/.gem/{RUBY_ENGINE_VERSION}</tt>" when not specified.
]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="rubylib" type="text" required="false" groupName="environment">
<label>RUBYLIB</label>
<description><![CDATA[Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to
"<tt>OPENHAB_CONF/automation/ruby/lib</tt>" when not specified.]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="dependency_tracking" type="boolean" required="true" groupName="environment">
<label>Enable Dependency Tracking</label>
<description>Dependency tracking allows your scripts to automatically reload when one of its dependencies is updated.
You may want to disable dependency tracking if you plan on editing or updating a shared library, but don't want all
your scripts to reload until you can test it.</description>
<default>true</default>
<advanced>true</advanced>
</parameter>
<parameter name="local_context" type="text" required="false" groupName="system"> <parameter name="local_context" type="text" required="false" groupName="system">
<label>Context Instance Type</label> <label>Context Instance Type</label>
<description><![CDATA[The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See <description>The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See
<a href="https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type">the documentation</a> for options and details. https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.</description>
]]></description>
<default>singlethread</default> <default>singlethread</default>
<options> <options>
<option value="singleton">Singleton</option> <option value="singleton">Singleton</option>
@@ -85,21 +35,35 @@
<option value="singlethread">SingleThread</option> <option value="singlethread">SingleThread</option>
<option value="concurrent">Concurrent</option> <option value="concurrent">Concurrent</option>
</options> </options>
<advanced>true</advanced>
</parameter> </parameter>
<parameter name="local_variable" type="text" required="false" groupName="system"> <parameter name="local_variable" type="text" required="false" groupName="system">
<label>Local Variable Behavior</label> <label>Local Variable Behavior</label>
<description><![CDATA[Defines how variables are shared between Ruby and Java. See <description>Defines how variables are shared between Ruby and Java. See
<a href="https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options">the documentation</a> for options and details. https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.</description>
]]></description>
<default>transient</default> <default>transient</default>
<options> <options>
<option value="transient">Transient</option> <option value="transient">Transient</option>
<option value="persistent">Persistent</option> <option value="persistent">Persistent</option>
<option value="global">Global</option> <option value="global">Global</option>
</options> </options>
<advanced>true</advanced> </parameter>
<parameter name="gem_home" type="text" required="false" groupName="environment">
<label>GEM_HOME</label>
<description>Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs
are specified</description>
<default></default>
</parameter>
<parameter name="rubylib" type="text" required="false" groupName="environment">
<label>RUBYLIB</label>
<description>Search path for user libraries. Separate each path with a colon (semicolon in Windows).</description>
</parameter>
<parameter name="gems" type="text" required="false" groupName="gems">
<label>Ruby Gems</label>
<description>Comma separated list of Ruby Gems to install.</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -1,11 +1,14 @@
automation.config.jruby.check_update.label = Check for Gem Updates
automation.config.jruby.check_update.description = Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed. Otherwise it will try to fulfill the requirements with locally installed gems, and you can manage them yourself with an external Ruby by setting the same GEM_HOME. # service
automation.config.jruby.dependency_tracking.label = Enable Dependency Tracking
automation.config.jruby.dependency_tracking.description = Dependency tracking allows your scripts to automatically reload when one of its dependencies is updated. You may want to disable dependency tracking if you plan on editing or updating a shared library, but don't want all your scripts to reload until you can test it. service.automation.jrubyscripting.label = JRuby Scripting
# bundle config
automation.config.jruby.gem_home.label = GEM_HOME automation.config.jruby.gem_home.label = GEM_HOME
automation.config.jruby.gem_home.description = Location Ruby Gems will be installed to and loaded from. Directory will be created if necessary. You can use <tt>{RUBY_ENGINE_VERSION}</tt>, <tt>{RUBY_ENGINE}</tt> and/or <tt>{RUBY_VERSION}</tt> replacements in this value to automatically point to a new directory when the addon is updated with a new version of JRuby. Defaults to "<tt>OPENHAB_CONF/automation/ruby/.gem/{RUBY_ENGINE_VERSION}</tt>" when not specified. automation.config.jruby.gem_home.description = Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs are specified
automation.config.jruby.gems.label = Ruby Gems automation.config.jruby.gems.label = Ruby Gems
automation.config.jruby.gems.description = A comma separated list of Ruby Gems to install. Versions may be constrained by separating with an <tt>=</tt> and then the standard RubyGems version constraint, such as "<tt>openhab-scripting=~>5.0</tt>". automation.config.jruby.gems.description = Comma separated list of Ruby Gems to install.
automation.config.jruby.group.environment.label = Ruby Environment automation.config.jruby.group.environment.label = Ruby Environment
automation.config.jruby.group.environment.description = This group defines Ruby's environment. automation.config.jruby.group.environment.description = This group defines Ruby's environment.
automation.config.jruby.group.gems.label = Ruby Gems automation.config.jruby.group.gems.label = Ruby Gems
@@ -13,21 +16,15 @@ automation.config.jruby.group.gems.description = This group defines the list of
automation.config.jruby.group.system.label = System Properties automation.config.jruby.group.system.label = System Properties
automation.config.jruby.group.system.description = This group defines JRuby system properties. automation.config.jruby.group.system.description = This group defines JRuby system properties.
automation.config.jruby.local_context.label = Context Instance Type automation.config.jruby.local_context.label = Context Instance Type
automation.config.jruby.local_context.description = The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See <a href="https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type">the documentation</a> for options and details. automation.config.jruby.local_context.description = The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.
automation.config.jruby.local_context.option.singleton = Singleton automation.config.jruby.local_context.option.singleton = Singleton
automation.config.jruby.local_context.option.threadsafe = ThreadSafe automation.config.jruby.local_context.option.threadsafe = ThreadSafe
automation.config.jruby.local_context.option.singlethread = SingleThread automation.config.jruby.local_context.option.singlethread = SingleThread
automation.config.jruby.local_context.option.concurrent = Concurrent automation.config.jruby.local_context.option.concurrent = Concurrent
automation.config.jruby.local_variable.label = Local Variable Behavior automation.config.jruby.local_variable.label = Local Variable Behavior
automation.config.jruby.local_variable.description = Defines how variables are shared between Ruby and Java. See <a href="https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options">the documentation</a> for options and details. automation.config.jruby.local_variable.description = Defines how variables are shared between Ruby and Java. See https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.
automation.config.jruby.local_variable.option.transient = Transient automation.config.jruby.local_variable.option.transient = Transient
automation.config.jruby.local_variable.option.persistent = Persistent automation.config.jruby.local_variable.option.persistent = Persistent
automation.config.jruby.local_variable.option.global = Global automation.config.jruby.local_variable.option.global = Global
automation.config.jruby.require.label = Require Scripts
automation.config.jruby.require.description = A comma separated list of script names to be required by the JRuby Scripting Engine before running user scripts.
automation.config.jruby.rubylib.label = RUBYLIB automation.config.jruby.rubylib.label = RUBYLIB
automation.config.jruby.rubylib.description = Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to "<tt>OPENHAB_CONF/automation/ruby/lib</tt>" when not specified. automation.config.jruby.rubylib.description = Search path for user libraries. Separate each path with a colon (semicolon in Windows).
# service
service.automation.jrubyscripting.label = JRuby Scripting

View File

@@ -1,33 +0,0 @@
automation.config.jruby.check_update.label = Nach Gem Updates suchen
automation.config.jruby.check_update.description = Prüfen Sie RubyGems auf Aktualisierungen der oben genannten Gems, wenn OpenHAB startet oder JRuby-Einstellungen geändert werden. Andernfalls wird versucht, die Anforderungen mit lokal installierten Gems zu erfüllen. Sie können auch ein eigenes, externes Ruby einsetzen, indem Sie dasselbe GEM_HOME definieren.
automation.config.jruby.dependency_tracking.label = Abhängigkeitsverfolgung aktivieren
automation.config.jruby.dependency_tracking.description = Die Abhängigkeitsverfolgung ermöglicht es Ihren Skripten, automatisch neu zu laden, wenn eine ihrer Abhängigkeiten aktualisiert wird. Sie können die Abhängigkeitsverfolgung deaktivieren, wenn Sie eine gemeinsam genutzte Bibliothek bearbeiten oder aktualisieren möchten, aber nicht wollen, dass alle Skripte neu geladen werden, bis Sie sie testen können.
automation.config.jruby.gem_home.label = GEM_HOME
automation.config.jruby.gem_home.description = Speicherort Ruby Gems wird installiert und von dort geladen. Das Verzeichnis wird bei Bedarf erstellt. Sie können die Ersetzungen <tt>{RUBY_ENGINE_VERSION}</tt>, <tt>{RUBY_ENGINE}</tt> und/oder <tt>{RUBY_VERSION}</tt> in diesem Wert verwenden, um automatisch auf ein neues Verzeichnis zu verweisen, wenn das Addon wird mit einer neuen Version von JRuby aktualisiert. Standardmäßig „<tt>OPENHAB_CONF/automation/ruby/.gem/{RUBY_ENGINE_VERSION}</tt>“, wenn nicht angegeben.
automation.config.jruby.gems.label = Ruby Gems
automation.config.jruby.gems.description = Eine kommagetrennte Liste von zu installierenden Ruby Gems. Versionen können eingeschränkt werden, indem sie von <tt>\=</tt> getrennt werden, und dann die Standard RubyGems Versionsbeschränkung, wie "<tt>openhab-scripting\=~>5.0.0</tt>".
automation.config.jruby.group.environment.label = Ruby-Umgebung
automation.config.jruby.group.environment.description = Diese Gruppe definiert die Ruby Umgebung.
automation.config.jruby.group.gems.label = Ruby Gems
automation.config.jruby.group.gems.description = Diese Gruppe definiert die Liste der zu installierenden Ruby Gems.
automation.config.jruby.group.system.label = Systemeigenschaften
automation.config.jruby.group.system.description = Diese Gruppe definiert JRuby Systemeigenschaften.
automation.config.jruby.local_context.label = Context Instance Type
automation.config.jruby.local_context.description = Der lokale Kontext enthält Ruby Laufzeitumgebung, Namens-Wert-Paare für das Teilen von Variablen zwischen Java und Ruby. Siehe <a href\="https\://github.com/jruby/jruby/wiki/RedBridge\#Context_Instance_Type">die Dokumentation</a> für Optionen und Details.
automation.config.jruby.local_context.option.singleton = Singleton
automation.config.jruby.local_context.option.threadsafe = ThreadSafe
automation.config.jruby.local_context.option.singlethread = SingleThread
automation.config.jruby.local_context.option.concurrent = Concurrent
automation.config.jruby.local_variable.label = Lokales Variablenverhalten
automation.config.jruby.local_variable.description = Legt fest, wie Variablen zwischen Ruby und Java geteilt werden. Siehe <a href\="https\://github.com/jruby/jruby/wiki/RedBridge\#local-variable-behavior-options">die Dokumentation</a> für Optionen und Details.
automation.config.jruby.local_variable.option.transient = Transient
automation.config.jruby.local_variable.option.persistent = Persistent
automation.config.jruby.local_variable.option.global = Global
automation.config.jruby.require.label = Skripte anfordern
automation.config.jruby.require.description = Eine durch Kommata getrennte Liste von Skriptnamen, die von der JRuby Scripting Engine verlangt werden, bevor Benutzerskripte ausgeführt werden.
automation.config.jruby.rubylib.label = RUBYLIB
automation.config.jruby.rubylib.description = Suchpfad für Benutzerbibliotheken. Trennen Sie jeden Pfad mit einem Doppelpunkt (Semikolon unter Windows). Standardmäßig "<tt>OPENHAB_CONF/automation/ruby/lib</tt>" wenn nicht angegeben.
# service
service.automation.jrubyscripting.label = JRuby Scripting

View File

@@ -1,30 +0,0 @@
# service
service.automation.jrubyscripting.label = JRuby szkriptek
# bundle config
automation.config.jruby.gem_home.label = GEM_HOME
automation.config.jruby.gem_home.description = A Ruby Gems (gyémántok) telepítési és betöltési helye, ha a gyémántok telepítése engedélyezve van. A mappa automatikusan létrehozásra kerül
automation.config.jruby.gems.label = Ruby gyémántok
automation.config.jruby.gems.description = Vesszővel elválasztott lista a telepítendő gyémántokról.
automation.config.jruby.group.environment.label = Ruby környezet
automation.config.jruby.group.environment.description = Ez a csoport adja meg a Ruby környezetet.
automation.config.jruby.group.gems.label = Ruby gyémántok
automation.config.jruby.group.gems.description = Ez a csoport adja meg a Ruby gyémántok telepítendő listáját.
automation.config.jruby.group.system.label = Rendszer tulajdonságok
automation.config.jruby.group.system.description = Ez a csoport adja meg a JRuby rendszer tulajdonságokat.
automation.config.jruby.local_context.label = A kontextus példány típusa
automation.config.jruby.local_context.description = A helyi kontextus tartalmazza a Ruby futtatási környezetét, név-érték párjait a Java és Ruby közti változók átadásához. A lehetőségek és részletek megtekintéséhez nyissa meg a következő linket\: https\://github.com/jruby/jruby/wiki/RedBridge\#Context_Instance_Type.
automation.config.jruby.local_context.option.singleton = Egyke (singleton)
automation.config.jruby.local_context.option.threadsafe = Szálbiztos
automation.config.jruby.local_context.option.singlethread = Egyszálú
automation.config.jruby.local_context.option.concurrent = Párhuzamos
automation.config.jruby.local_variable.label = Helyi változó viselkedés
automation.config.jruby.local_variable.description = Megadja, hogy a változók átadása a Ruby és a Java környezet között miként történjen. Részletek\: https\://github.com/jruby/jruby/wiki/RedBridge\#local-variable-behavior-options.
automation.config.jruby.local_variable.option.transient = Átmeneti
automation.config.jruby.local_variable.option.persistent = Tartós
automation.config.jruby.local_variable.option.global = Teljes körű
automation.config.jruby.rubylib.label = RUBYLIB
automation.config.jruby.rubylib.description = A felhasználói könyvtárak keresési útvonala. Elválasztó a kettőspont (Pontosvessző Windows alatt).

View File

@@ -1,33 +0,0 @@
automation.config.jruby.check_update.label = Controlla per Aggiornamenti di Gem
automation.config.jruby.check_update.description = Controllare RubyGems per gli aggiornamenti alle gem di cui sopra quando OpenHAB inizia o le impostazioni JRuby vengono modificate. Altrimenti proverà a soddisfare i requisiti con gem installate localmente, e puoi gestirli da soli con un Ruby esterno impostando lo stesso GEM_HOME.
automation.config.jruby.dependency_tracking.label = Abilita Monitoraggio Delle Dipendenze
automation.config.jruby.dependency_tracking.description = Il monitoraggio delle dipendenze consente agli script di ricaricare automaticamente quando una delle sue dipendenze viene aggiornata. Si consiglia di disabilitare il tracciamento delle dipendenze se si prevede di modificare o aggiornare una libreria condivisa, ma non vuoi che tutti gli script siano ricaricati fino a quando non puoi provarlo.
automation.config.jruby.gem_home.label = GEM_HOME
automation.config.jruby.gem_home.description = Il percorso da dove le Ruby Gems saranno installate e caricate. La directory verrà creata se necessario. È possibile utilizare i valori <tt>{RUBY_ENGINE_VERSION}</tt>, <tt>{RUBY_ENGINE}</tt> e/o <tt>{RUBY_VERSION}</tt> come sostituzioni per puntare automaticamente a una nuova directory quando l''addon viene aggiornato con una nuova versione di JRuby. Il valore predefinito è "<tt>OPENHAB_CONF/automation/ruby/.gem/{RUBY_ENGINE_VERSION}</tt>" se non specificato.
automation.config.jruby.gems.label = Ruby Gems
automation.config.jruby.gems.description = Un elenco separato da virgola di Ruby Gems da installare. Le versioni possono essere vincolate separando con un <tt>\=</tt> e quindi il vincolo di versione standard di RubyGems, come "<tt>openhab-scripting\=~>5.</tt>".
automation.config.jruby.group.environment.label = Ambiente Di Ruby
automation.config.jruby.group.environment.description = Questo gruppo definisce l'ambiente di Ruby.
automation.config.jruby.group.gems.label = Ruby Gems
automation.config.jruby.group.gems.description = Questo gruppo definisce la lista di Ruby Gems da installare.
automation.config.jruby.group.system.label = Proprietà di sistema
automation.config.jruby.group.system.description = Questo gruppo definisce le proprietà del sistema JRuby.
automation.config.jruby.local_context.label = Tipo Context Instance
automation.config.jruby.local_context.description = Il contesto locale contiene le coppie di runtime Ruby, none-valore per la condivisione delle variabili tra Java e Ruby. Vedere <a href\="https\://github.com/jruby/jruby/wiki/RedBridge\#Context_Instance_Type">la documentazione</a> per le opzioni e i dettagli.
automation.config.jruby.local_context.option.singleton = Singleton
automation.config.jruby.local_context.option.threadsafe = Thread Sicura
automation.config.jruby.local_context.option.singlethread = Thread Singola
automation.config.jruby.local_context.option.concurrent = Concorrente
automation.config.jruby.local_variable.label = Comportamento Variabile Locale
automation.config.jruby.local_variable.description = Definisce come le variabili sono condivise tra Ruby e Java. Vedi <a href\="https\://github.com/jruby/jruby/wiki/RedBridge\#local-variable-behavior-options">la documentazione</a> per le opzioni e i dettagli.
automation.config.jruby.local_variable.option.transient = Transitorio
automation.config.jruby.local_variable.option.persistent = Persistente
automation.config.jruby.local_variable.option.global = Globale
automation.config.jruby.require.label = Richiede Scripts
automation.config.jruby.require.description = Un elenco separato da virgola di nomi di script che devono essere richiesti dal motore di scripting JRuby prima di eseguire gli script utente.
automation.config.jruby.rubylib.label = RUBYLIB
automation.config.jruby.rubylib.description = Percorso di ricerca per le librerie utente. Separare ogni percorso con due punti (con un punto e virgola in Windows). Predefinito "<tt>OPENHAB_CONF/automation/ruby/lib</tt>" se non specificato.
# service
service.automation.jrubyscripting.label = JRuby Scripting

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,9 @@
Bundle-SymbolicName: ${project.artifactId} Bundle-SymbolicName: ${project.artifactId}
DynamicImport-Package: * DynamicImport-Package: *
Import-Package: org.openhab.core.automation.module.script,org.openhab.core.items,org.openhab.core.library.types,javax.management,javax.script,javax.xml.datatype,javax.xml.stream;version="[1.0,2)",org.osgi.framework;version="[1.8,2)",org.slf4j;version="[1.7,2)" Import-Package: org.openhab.core.automation.module.script,javax.management,javax.script,javax.xml.datatype,javax.xml.stream;version="[1.0,2)",org.osgi.framework;version="[1.8,2)",org.slf4j;version="[1.7,2)"
Require-Capability: Require-Capability: osgi.extender;
osgi.extender:=
filter:="(osgi.extender=osgi.serviceloader.processor)", filter:="(osgi.extender=osgi.serviceloader.processor)",
osgi.serviceloader:= osgi.serviceloader;
filter:="(osgi.serviceloader=org.graalvm.polyglot.impl.AbstractPolyglotImpl)"; filter:="(osgi.serviceloader=org.graalvm.polyglot.impl.AbstractPolyglotImpl)";
cardinality:=multiple cardinality:=multiple

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.automation.jsscripting</artifactId> <artifactId>org.openhab.automation.jsscripting</artifactId>
<name>openHAB Add-ons :: Bundles :: Automation :: JavaScript Scripting</name> <name>openHAB Add-ons :: Bundles :: Automation :: JSScripting</name>
<properties> <properties>
<bnd.importpackage> <bnd.importpackage>
@@ -20,9 +20,10 @@
!jdk.internal.reflect.*, !jdk.internal.reflect.*,
!jdk.vm.ci.services !jdk.vm.ci.services
</bnd.importpackage> </bnd.importpackage>
<graal.version>22.0.0.2</graal.version> <!-- DO NOT UPGRADE: 22.0.0.2 is the latest version working on armv7l / OpenJDK 11.0.16 & armv7l / Zulu 17.0.5+8 --> <graal.version>21.3.0</graal.version>
<asm.version>6.2.1</asm.version>
<oh.version>${project.version}</oh.version> <oh.version>${project.version}</oh.version>
<ohjs.version>openhab@4.7.3</ohjs.version> <ohjs.version>openhab@1.2.1</ohjs.version>
</properties> </properties>
<build> <build>
@@ -45,9 +46,9 @@
<plugin> <plugin>
<groupId>com.github.eirslett</groupId> <groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId> <artifactId>frontend-maven-plugin</artifactId>
<version>1.12.1</version> <version>1.12.0</version>
<configuration> <configuration>
<nodeVersion>v16.17.1</nodeVersion> <!-- DO NOT DOWNGRADE: NodeJS < 16 doesn't support Apple Silicon --> <nodeVersion>v12.16.1</nodeVersion>
<workingDirectory>target/js</workingDirectory> <workingDirectory>target/js</workingDirectory>
</configuration> </configuration>
<executions> <executions>
@@ -64,30 +65,16 @@
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
<configuration> <configuration>
<!--suppress UnresolvedMavenProperty --> <arguments>install ${ohjs.version} webpack webpack-cli</arguments>
<arguments>install ${ohjs.version} webpack@^5.87.0 webpack-cli@^5.1.4</arguments> <!-- webpack & webpack-cli versions should match to the ones from openhab-js -->
</configuration> </configuration>
</execution> </execution>
<execution> <execution>
<id>npx webpack (openhab-js globals injection)</id> <id>npx webpack</id>
<goals> <goals>
<goal>npx</goal> <goal>npx</goal>
</goals> </goals>
<configuration> <configuration>
<!--suppress UnresolvedMavenProperty --> <arguments>webpack -c ./node_modules/openhab/webpack.config.js --entry ./node_modules/openhab/ -o ./dist</arguments>
<arguments>webpack -c ./node_modules/openhab/build/@globals-webpack.config.js --entry-reset --entry
./node_modules/openhab/build/@openhab-globals.js -o ./dist</arguments>
</configuration>
</execution>
<execution>
<id>npx webpack (openhab-js)</id>
<goals>
<goal>npx</goal>
</goals>
<configuration>
<!--suppress UnresolvedMavenProperty -->
<arguments>webpack -c ./node_modules/openhab/build/webpack.config.js --entry-reset --entry
./node_modules/openhab/ -o ./dist</arguments>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@@ -112,13 +99,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.openhab.tools.sat</groupId>
<artifactId>sat-plugin</artifactId>
<configuration>
<pmdFilter>${project.basedir}/suppressions.properties</pmdFilter>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -133,6 +113,11 @@
<artifactId>js-scriptengine</artifactId> <artifactId>js-scriptengine</artifactId>
<version>${graal.version}</version> <version>${graal.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-launcher</artifactId>
<version>${graal.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.graalvm.sdk</groupId> <groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId> <artifactId>graal-sdk</artifactId>
@@ -148,7 +133,37 @@
<artifactId>js</artifactId> <artifactId>js</artifactId>
<version>${graal.version}</version> <version>${graal.version}</version>
</dependency> </dependency>
<!-- GraalJS changelog says that com.ibm.icu/icu4j is not required for GraalJS >= 22.0.0 as it moved to org.graalvm.truffle; <dependency>
but GraalJS >= 22.2.0 requires it, so we'll need to add it when we upgrade --> <groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>69.1</version>
</dependency>
<!-- include as version required is older than OH provides -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
<version>${asm.version}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -3,7 +3,7 @@
<features name="org.openhab.automation.jsscripting-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0"> <features name="org.openhab.automation.jsscripting-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository> <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-automation-jsscripting" description="JavaScript Scripting" version="${project.version}"> <feature name="openhab-automation-jsscripting" description="JSScripting" version="${project.version}">
<feature>openhab-runtime-base</feature> <feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jsscripting/${project.version}</bundle> <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jsscripting/${project.version}</bundle>
</feature> </feature>

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -15,9 +15,10 @@ package org.openhab.automation.jsscripting.internal;
import javax.script.Invocable; import javax.script.Invocable;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable; import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -26,8 +27,8 @@ import org.slf4j.LoggerFactory;
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
*/ */
class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable & AutoCloseable> class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable>
extends InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<T> { extends InvocationInterceptingScriptEngineWithInvocable<T> {
private static final Logger STACK_LOGGER = LoggerFactory private static final Logger STACK_LOGGER = LoggerFactory
.getLogger("org.openhab.automation.script.javascript.stack"); .getLogger("org.openhab.automation.script.javascript.stack");
@@ -37,14 +38,11 @@ class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable & AutoClosea
} }
@Override @Override
public Exception afterThrowsInvocation(Exception e) { public ScriptException afterThrowsInvocation(ScriptException se) {
Throwable cause = e.getCause(); Throwable cause = se.getCause();
if (cause instanceof IllegalArgumentException) {
STACK_LOGGER.error("Failed to execute script:", e);
}
if (cause instanceof PolyglotException) { if (cause instanceof PolyglotException) {
STACK_LOGGER.error("Failed to execute script:", cause); STACK_LOGGER.error("Failed to execute script:", cause);
} }
return e; return se;
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -12,26 +12,20 @@
*/ */
package org.openhab.automation.jsscripting.internal; package org.openhab.automation.jsscripting.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.automation.jsscripting.internal.fs.watch.JSDependencyTracker;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.config.core.ConfigParser;
import org.openhab.core.config.core.ConfigurableService; import org.openhab.core.config.core.ConfigurableService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants; import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import com.oracle.truffle.js.scriptengine.GraalJSEngineFactory;
/** /**
* An implementation of {@link ScriptEngineFactory} with customizations for GraalJS ScriptEngines. * An implementation of {@link ScriptEngineFactory} with customizations for GraalJS ScriptEngines.
@@ -42,64 +36,52 @@ import com.oracle.truffle.js.scriptengine.GraalJSEngineFactory;
@Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.jsscripting", property = Constants.SERVICE_PID @Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.jsscripting", property = Constants.SERVICE_PID
+ "=org.openhab.jsscripting") + "=org.openhab.jsscripting")
@ConfigurableService(category = "automation", label = "JS Scripting", description_uri = "automation:jsscripting") @ConfigurableService(category = "automation", label = "JS Scripting", description_uri = "automation:jsscripting")
@NonNullByDefault
public final class GraalJSScriptEngineFactory implements ScriptEngineFactory { public final class GraalJSScriptEngineFactory implements ScriptEngineFactory {
private static final String CFG_INJECTION_ENABLED = "injectionEnabled"; private static final String CFG_INJECTION_ENABLED = "injectionEnabled";
private static final String CFG_INJECTION_CACHING_ENABLED = "injectionCachingEnabled"; private static final String INJECTION_CODE = "Object.assign(this, require('openhab'));";
private static final GraalJSEngineFactory factory = new GraalJSEngineFactory();
private static final List<String> scriptTypes = createScriptTypes();
private static List<String> createScriptTypes() {
// Add those for backward compatibility (existing scripts may rely on those MIME types)
List<String> backwardCompat = List.of("application/javascript;version=ECMAScript-2021", "graaljs");
return Stream.of(factory.getMimeTypes(), factory.getExtensions(), backwardCompat).flatMap(List::stream)
.toList();
}
private boolean injectionEnabled = true; private boolean injectionEnabled = true;
private boolean injectionCachingEnabled = true;
private final JSScriptServiceUtil jsScriptServiceUtil; public static final String MIME_TYPE = "application/javascript;version=ECMAScript-2021";
private final JSDependencyTracker jsDependencyTracker;
@Activate
public GraalJSScriptEngineFactory(final @Reference JSScriptServiceUtil jsScriptServiceUtil,
final @Reference JSDependencyTracker jsDependencyTracker, Map<String, Object> config) {
this.jsDependencyTracker = jsDependencyTracker;
this.jsScriptServiceUtil = jsScriptServiceUtil;
modified(config);
}
@Override @Override
public List<String> getScriptTypes() { public List<String> getScriptTypes() {
return scriptTypes; List<String> scriptTypes = new ArrayList<>();
/*
* Whilst we run in parallel with Nashorn, we use a custom mime-type to avoid
* disrupting Nashorn scripts. When Nashorn is removed, we take over the standard
* JS runtime.
*/
// GraalJSEngineFactory graalJSEngineFactory = new GraalJSEngineFactory();
//
// scriptTypes.addAll(graalJSEngineFactory.getMimeTypes());
// scriptTypes.addAll(graalJSEngineFactory.getExtensions());
scriptTypes.add(MIME_TYPE);
return Collections.unmodifiableList(scriptTypes);
} }
@Override @Override
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) { public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
// noop; they are retrieved via modules, not injected // noop; the are retrieved via modules, not injected
} }
@Override @Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) { public ScriptEngine createScriptEngine(String scriptType) {
if (!scriptTypes.contains(scriptType)) { return new DebuggingGraalScriptEngine<>(
return null; new OpenhabGraalJSScriptEngine(injectionEnabled ? INJECTION_CODE : null));
}
return new DebuggingGraalScriptEngine<>(new OpenhabGraalJSScriptEngine(injectionEnabled,
injectionCachingEnabled, jsScriptServiceUtil, jsDependencyTracker));
} }
@Override @Activate
public @Nullable ScriptDependencyTracker getDependencyTracker() { protected void activate(BundleContext context, Map<String, ?> config) {
return jsDependencyTracker; modified(config);
} }
@Modified @Modified
protected void modified(Map<String, ?> config) { protected void modified(Map<String, ?> config) {
this.injectionEnabled = ConfigParser.valueAsOrElse(config.get(CFG_INJECTION_ENABLED), Boolean.class, true); Object injectionEnabled = config.get(CFG_INJECTION_ENABLED);
this.injectionCachingEnabled = ConfigParser.valueAsOrElse(config.get(CFG_INJECTION_CACHING_ENABLED), this.injectionEnabled = injectionEnabled == null || (Boolean) injectionEnabled;
Boolean.class, true);
} }
} }

View File

@@ -1,58 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.automation.jsscripting.internal.threading.ThreadsafeTimers;
/**
* Abstraction layer to collect all features injected into the JS runtime during the context creation.
*
* @author Florian Hotze - Initial contribution
*/
@NonNullByDefault
public class JSRuntimeFeatures {
/**
* All elements of this Map are injected into the JS runtime using their key as the name.
*/
private final Map<String, Object> features = new HashMap<>();
public final ThreadsafeTimers threadsafeTimers;
JSRuntimeFeatures(Lock lock, JSScriptServiceUtil jsScriptServiceUtil) {
this.threadsafeTimers = new ThreadsafeTimers(lock, jsScriptServiceUtil.getScriptExecution(),
jsScriptServiceUtil.getScheduler());
features.put("ThreadsafeTimers", threadsafeTimers);
}
/**
* Get the features that are to be injected into the JS runtime during context creation.
*
* @return the runtime features
*/
public Map<String, Object> getFeatures() {
return features;
}
/**
* Un-initialization hook, called when the engine is closed.
* Use this method to clean up resources or cancel operations that were created by the JS runtime.
*/
public void close() {
threadsafeTimers.clearAll();
}
}

View File

@@ -1,52 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.automation.module.script.action.ScriptExecution;
import org.openhab.core.scheduler.Scheduler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* OSGi utility service for providing easy access to script services.
*
* @author Florian Hotze - Initial contribution
*/
@Component(immediate = true, service = JSScriptServiceUtil.class)
@NonNullByDefault
public class JSScriptServiceUtil {
private final Scheduler scheduler;
private final ScriptExecution scriptExecution;
@Activate
public JSScriptServiceUtil(final @Reference Scheduler scheduler, final @Reference ScriptExecution scriptExecution) {
this.scheduler = scheduler;
this.scriptExecution = scriptExecution;
}
public Scheduler getScheduler() {
return scheduler;
}
public ScriptExecution getScriptExecution() {
return scriptExecution;
}
public JSRuntimeFeatures getJSRuntimeFeatures(Lock lock) {
return new JSRuntimeFeatures(lock, this);
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -16,8 +16,6 @@ import static org.openhab.core.automation.module.script.ScriptEngineFactory.*;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.channels.SeekableByteChannel; import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode; import java.nio.file.AccessMode;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
@@ -28,150 +26,94 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttribute;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import javax.script.ScriptContext; import javax.script.ScriptContext;
import javax.script.ScriptException; import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine; import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.HostAccess; import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value; import org.graalvm.polyglot.Value;
import org.openhab.automation.jsscripting.internal.fs.DelegatingFileSystem; import org.openhab.automation.jsscripting.internal.fs.DelegatingFileSystem;
import org.openhab.automation.jsscripting.internal.fs.PrefixedSeekableByteChannel; import org.openhab.automation.jsscripting.internal.fs.PrefixedSeekableByteChannel;
import org.openhab.automation.jsscripting.internal.fs.ReadOnlySeekableByteArrayChannel; import org.openhab.automation.jsscripting.internal.fs.ReadOnlySeekableByteArrayChannel;
import org.openhab.automation.jsscripting.internal.fs.watch.JSDependencyTracker; import org.openhab.automation.jsscripting.internal.fs.watch.JSDependencyTracker;
import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable; import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable;
import org.openhab.core.automation.module.script.ScriptExtensionAccessor; import org.openhab.core.automation.module.script.ScriptExtensionAccessor;
import org.openhab.core.items.Item;
import org.openhab.core.library.types.QuantityType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine; import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
/** /**
* GraalJS ScriptEngine implementation * GraalJS Script Engine implementation
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
* @author Dan Cunningham - Script injections * @author Dan Cunningham - Script injections
* @author Florian Hotze - Create lock object for multi-thread synchronization; Inject the {@link JSRuntimeFeatures}
* into the JS context; Fix memory leak caused by HostObject by making HostAccess reference static; Switch to
* {@link Lock} for multi-thread synchronization; globals and openhab-js injection code caching
*/ */
public class OpenhabGraalJSScriptEngine public class OpenhabGraalJSScriptEngine extends InvocationInterceptingScriptEngineWithInvocable<GraalJSScriptEngine> {
extends InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<GraalJSScriptEngine> {
private static final Logger LOGGER = LoggerFactory.getLogger(OpenhabGraalJSScriptEngine.class); private static final Logger LOGGER = LoggerFactory.getLogger(OpenhabGraalJSScriptEngine.class);
private static Source GLOBAL_SOURCE; private static final String GLOBAL_REQUIRE = "require(\"@jsscripting-globals\");";
static {
try {
GLOBAL_SOURCE = Source.newBuilder("js", getFileAsReader("node_modules/@jsscripting-globals.js"),
"@jsscripting-globals.js").cached(true).build();
} catch (IOException e) {
throw new RuntimeException("Failed to load @jsscripting-globals.js", e);
}
}
private static Source OPENHAB_JS_SOURCE;
static {
try {
OPENHAB_JS_SOURCE = Source
.newBuilder("js", getFileAsReader("node_modules/@openhab-globals.js"), "@openhab-globals.js")
.cached(true).build();
} catch (IOException e) {
throw new RuntimeException("Failed to load @openhab-globals.js", e);
}
}
private static final String OPENHAB_JS_INJECTION_CODE = "Object.assign(this, require('openhab'));";
private static final String REQUIRE_WRAPPER_NAME = "__wraprequire__"; private static final String REQUIRE_WRAPPER_NAME = "__wraprequire__";
/** Final CommonJS search path for our library */ // final CommonJS search path for our library
private static final Path NODE_DIR = Paths.get("node_modules"); private static final Path NODE_DIR = Paths.get("node_modules");
/** Shared Polyglot {@link Engine} across all instances of {@link OpenhabGraalJSScriptEngine} */
private static final Engine ENGINE = Engine.newBuilder().allowExperimentalOptions(true)
.option("engine.WarnInterpreterOnly", "false").build();
/** Provides unlimited host access as well as custom translations from JS to Java Objects */
private static final HostAccess HOST_ACCESS = HostAccess.newBuilder(HostAccess.ALL)
// Translate JS-Joda ZonedDateTime to java.time.ZonedDateTime
.targetTypeMapping(Value.class, ZonedDateTime.class, v -> v.hasMember("withFixedOffsetZone"),
v -> ZonedDateTime.parse(v.invokeMember("withFixedOffsetZone").invokeMember("toString").asString()),
HostAccess.TargetMappingPrecedence.LOW)
// Translate JS-Joda Duration to java.time.Duration
.targetTypeMapping(Value.class, Duration.class,
// picking two members to check as Duration has many common function names
v -> v.hasMember("minusDuration") && v.hasMember("toNanos"),
v -> Duration.ofNanos(v.invokeMember("toNanos").asLong()), HostAccess.TargetMappingPrecedence.LOW)
// Translate JS-Joda Instant to java.time.Instant
.targetTypeMapping(Value.class, Instant.class,
// picking two members to check as Instant has many common function names
v -> v.hasMember("toEpochMilli") && v.hasMember("epochSecond"),
v -> Instant.ofEpochMilli(v.invokeMember("toEpochMilli").asLong()),
HostAccess.TargetMappingPrecedence.LOW)
// Translate openhab-js Item to org.openhab.core.items.Item
.targetTypeMapping(Value.class, Item.class, v -> v.hasMember("rawItem"),
v -> v.getMember("rawItem").as(Item.class), HostAccess.TargetMappingPrecedence.LOW)
// Translate openhab-js Quantity to org.openhab.core.library.types.QuantityType
.targetTypeMapping(Value.class, QuantityType.class, v -> v.hasMember("rawQtyType"),
v -> v.getMember("rawQtyType").as(QuantityType.class), HostAccess.TargetMappingPrecedence.LOW)
.build();
/** {@link Lock} synchronization of multi-thread access */
private final Lock lock = new ReentrantLock();
private final JSRuntimeFeatures jsRuntimeFeatures;
// these fields start as null because they are populated on first use // these fields start as null because they are populated on first use
private String engineIdentifier; private @NonNullByDefault({}) String engineIdentifier;
private @Nullable Consumer<String> scriptDependencyListener; private @NonNullByDefault({}) Consumer<String> scriptDependencyListener;
private boolean initialized = false; private boolean initialized = false;
private final boolean injectionEnabled; private String globalScript;
private final boolean injectionCachingEnabled;
/** /**
* Creates an implementation of ScriptEngine {@code (& Invocable)}, wrapping the contained engine, * Creates an implementation of ScriptEngine (& Invocable), wrapping the contained engine, that tracks the script
* that tracks the script lifecycle and provides hooks for scripts to do so too. * lifecycle and provides hooks for scripts to do so too.
*/ */
public OpenhabGraalJSScriptEngine(boolean injectionEnabled, boolean injectionCachingEnabled, public OpenhabGraalJSScriptEngine(@Nullable String injectionCode) {
JSScriptServiceUtil jsScriptServiceUtil, JSDependencyTracker jsDependencyTracker) {
super(null); // delegate depends on fields not yet initialised, so we cannot set it immediately super(null); // delegate depends on fields not yet initialised, so we cannot set it immediately
this.injectionEnabled = injectionEnabled; this.globalScript = GLOBAL_REQUIRE + (injectionCode != null ? injectionCode : "");
this.injectionCachingEnabled = injectionCachingEnabled;
this.jsRuntimeFeatures = jsScriptServiceUtil.getJSRuntimeFeatures(lock);
LOGGER.debug("Initializing GraalJS script engine..."); // Custom translate JS Objects - > Java Objects
HostAccess hostAccess = HostAccess.newBuilder(HostAccess.ALL)
// Translate JS-Joda ZonedDateTime to java.time.ZonedDateTime
.targetTypeMapping(Value.class, ZonedDateTime.class, (v) -> v.hasMember("withFixedOffsetZone"), v -> {
return ZonedDateTime
.parse(v.invokeMember("withFixedOffsetZone").invokeMember("toString").asString());
}, HostAccess.TargetMappingPrecedence.LOW)
delegate = GraalJSScriptEngine.create(ENGINE, // Translate JS-Joda Duration to java.time.Duration
Context.newBuilder("js").allowExperimentalOptions(true).allowAllAccess(true) .targetTypeMapping(Value.class, Duration.class,
.allowHostAccess(HOST_ACCESS) // picking two members to check as Duration has many common function names
.option("js.commonjs-require-cwd", jsDependencyTracker.getLibraryPath().toString()) (v) -> v.hasMember("minusDuration") && v.hasMember("toNanos"), v -> {
.option("js.nashorn-compat", "true") // Enable Nashorn compat mode as openhab-js relies on return Duration.ofNanos(v.invokeMember("toNanos").asLong());
// accessors, see }, HostAccess.TargetMappingPrecedence.LOW)
// https://github.com/oracle/graaljs/blob/master/docs/user/NashornMigrationGuide.md#accessors .build();
.option("js.ecmascript-version", "2022") // If Nashorn compat is enabled, it will enforce ES5
// compatibility, we want ECMA2022 delegate = GraalJSScriptEngine.create(
.option("js.commonjs-require", "true") // Enable CommonJS module support Engine.newBuilder().allowExperimentalOptions(true).option("engine.WarnInterpreterOnly", "false")
.build(),
Context.newBuilder("js").allowExperimentalOptions(true).allowAllAccess(true).allowHostAccess(hostAccess)
.option("js.commonjs-require-cwd", JSDependencyTracker.LIB_PATH)
.option("js.nashorn-compat", "true") // to ease migration
.option("js.ecmascript-version", "2021") // nashorn compat will enforce es5 compatibility, we
// want ecma2021
.option("js.commonjs-require", "true") // enable CommonJS module support
.hostClassLoader(getClass().getClassLoader()) .hostClassLoader(getClass().getClassLoader())
.fileSystem(new DelegatingFileSystem(FileSystems.getDefault().provider()) { .fileSystem(new DelegatingFileSystem(FileSystems.getDefault().provider()) {
@Override @Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options,
FileAttribute<?>... attrs) throws IOException { FileAttribute<?>... attrs) throws IOException {
Consumer<String> localScriptDependencyListener = scriptDependencyListener; if (scriptDependencyListener != null) {
if (localScriptDependencyListener != null) { scriptDependencyListener.accept(path.toString());
localScriptDependencyListener.accept(path.toString());
} }
if (path.toString().endsWith(".js")) { if (path.toString().endsWith(".js")) {
@@ -208,7 +150,7 @@ public class OpenhabGraalJSScriptEngine
public Map<String, Object> readAttributes(Path path, String attributes, public Map<String, Object> readAttributes(Path path, String attributes,
LinkOption... options) throws IOException { LinkOption... options) throws IOException {
if (isRootNodePath(path)) { if (isRootNodePath(path)) {
return Map.of("isRegularFile", true); return Collections.singletonMap("isRegularFile", true);
} }
return super.readAttributes(path, attributes, options); return super.readAttributes(path, attributes, options);
} }
@@ -225,18 +167,11 @@ public class OpenhabGraalJSScriptEngine
@Override @Override
protected void beforeInvocation() { protected void beforeInvocation() {
super.beforeInvocation();
lock.lock();
if (initialized) { if (initialized) {
return; return;
} }
ScriptContext ctx = delegate.getContext(); ScriptContext ctx = delegate.getContext();
if (ctx == null) {
throw new IllegalStateException("Failed to retrieve script context");
}
// these are added post-construction, so we need to fetch them late // these are added post-construction, so we need to fetch them late
this.engineIdentifier = (String) ctx.getAttribute(CONTEXT_KEY_ENGINE_IDENTIFIER); this.engineIdentifier = (String) ctx.getAttribute(CONTEXT_KEY_ENGINE_IDENTIFIER);
@@ -258,63 +193,29 @@ public class OpenhabGraalJSScriptEngine
} }
ScriptExtensionModuleProvider scriptExtensionModuleProvider = new ScriptExtensionModuleProvider( ScriptExtensionModuleProvider scriptExtensionModuleProvider = new ScriptExtensionModuleProvider(
scriptExtensionAccessor, lock); scriptExtensionAccessor);
// Wrap the "require" function to also allow loading modules from the ScriptExtensionModuleProvider
Function<Function<Object[], Object>, Function<String, Object>> wrapRequireFn = originalRequireFn -> moduleName -> scriptExtensionModuleProvider Function<Function<Object[], Object>, Function<String, Object>> wrapRequireFn = originalRequireFn -> moduleName -> scriptExtensionModuleProvider
.locatorFor(delegate.getPolyglotContext(), engineIdentifier).locateModule(moduleName) .locatorFor(delegate.getPolyglotContext(), engineIdentifier).locateModule(moduleName)
.map(m -> (Object) m).orElseGet(() -> originalRequireFn.apply(new Object[] { moduleName })); .map(m -> (Object) m).orElseGet(() -> originalRequireFn.apply(new Object[] { moduleName }));
delegate.getBindings(ScriptContext.ENGINE_SCOPE).put(REQUIRE_WRAPPER_NAME, wrapRequireFn); delegate.getBindings(ScriptContext.ENGINE_SCOPE).put(REQUIRE_WRAPPER_NAME, wrapRequireFn);
delegate.put("require", wrapRequireFn.apply((Function<Object[], Object>) delegate.get("require"))); delegate.put("require", wrapRequireFn.apply((Function<Object[], Object>) delegate.get("require")));
// Injections into the JS runtime
jsRuntimeFeatures.getFeatures().forEach((key, obj) -> {
LOGGER.debug("Injecting {} into the JS runtime...", key);
delegate.put(key, obj);
});
initialized = true; initialized = true;
try { try {
LOGGER.debug("Evaluating cached global script..."); eval(globalScript);
delegate.getPolyglotContext().eval(GLOBAL_SOURCE);
if (this.injectionEnabled) {
if (this.injectionCachingEnabled) {
LOGGER.debug("Evaluating cached openhab-js injection...");
delegate.getPolyglotContext().eval(OPENHAB_JS_SOURCE);
} else {
LOGGER.debug("Evaluating openhab-js injection from the file system...");
eval(OPENHAB_JS_INJECTION_CODE);
}
}
LOGGER.debug("Successfully initialized GraalJS script engine.");
} catch (ScriptException e) { } catch (ScriptException e) {
LOGGER.error("Could not inject global script", e); LOGGER.error("Could not inject global script", e);
} }
} }
@Override
protected Object afterInvocation(Object obj) {
lock.unlock();
return super.afterInvocation(obj);
}
@Override
protected Exception afterThrowsInvocation(Exception e) {
lock.unlock();
return super.afterThrowsInvocation(e);
}
@Override
public void close() {
jsRuntimeFeatures.close();
}
/** /**
* Tests if this is a root node directory, `/node_modules`, `C:\node_modules`, etc... * Tests if this is a root node directory, `/node_modules`, `C:\node_modules`, etc...
* *
* @param path a root path * @param path
* @return whether the given path is a node root directory * @return
*/ */
private boolean isRootNodePath(Path path) { private boolean isRootNodePath(Path path) {
return path.startsWith(path.getRoot().resolve(NODE_DIR)); return path.startsWith(path.getRoot().resolve(NODE_DIR));
@@ -322,26 +223,11 @@ public class OpenhabGraalJSScriptEngine
/** /**
* Converts a root node path to a class resource path for loading local modules * Converts a root node path to a class resource path for loading local modules
* Ex: C:\node_modules\foo.js -> /node_modules/foo.js
* *
* @param path a root path, e.g. C:\node_modules\foo.js * @param path
* @return the class resource path for loading local modules * @return
*/ */
private String nodeFileToResource(Path path) { private String nodeFileToResource(Path path) {
return "/" + path.subpath(0, path.getNameCount()).toString().replace('\\', '/'); return "/" + NODE_DIR + "/" + path.getFileName();
}
/**
* @param fileName filename relative to the resources folder
* @return file as {@link InputStreamReader}
*/
private static Reader getFileAsReader(String fileName) throws IOException {
InputStream ioStream = OpenhabGraalJSScriptEngine.class.getClassLoader().getResourceAsStream(fileName);
if (ioStream == null) {
throw new IOException(fileName + " not found");
}
return new InputStreamReader(ioStream);
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -16,7 +16,6 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
@@ -27,11 +26,9 @@ import org.openhab.core.automation.module.script.ScriptExtensionAccessor;
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager; import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager;
/** /**
* Class providing script extensions via CommonJS modules (with module name `@runtime`). * Class providing script extensions via CommonJS modules.
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
* @author Florian Hotze - Pass in lock object for multi-thread synchronization; Switch to {@link Lock} for multi-thread
* synchronization
*/ */
@NonNullByDefault @NonNullByDefault
@@ -39,13 +36,11 @@ public class ScriptExtensionModuleProvider {
private static final String RUNTIME_MODULE_PREFIX = "@runtime"; private static final String RUNTIME_MODULE_PREFIX = "@runtime";
private static final String DEFAULT_MODULE_NAME = "Defaults"; private static final String DEFAULT_MODULE_NAME = "Defaults";
private final Lock lock;
private final ScriptExtensionAccessor scriptExtensionAccessor; private final ScriptExtensionAccessor scriptExtensionAccessor;
public ScriptExtensionModuleProvider(ScriptExtensionAccessor scriptExtensionAccessor, Lock lock) { public ScriptExtensionModuleProvider(ScriptExtensionAccessor scriptExtensionAccessor) {
this.scriptExtensionAccessor = scriptExtensionAccessor; this.scriptExtensionAccessor = scriptExtensionAccessor;
this.lock = lock;
} }
public ModuleLocator locatorFor(Context ctx, String engineIdentifier) { public ModuleLocator locatorFor(Context ctx, String engineIdentifier) {
@@ -78,15 +73,10 @@ public class ScriptExtensionModuleProvider {
private Value toValue(Context ctx, Map<String, Object> map) { private Value toValue(Context ctx, Map<String, Object> map) {
try { try {
return ctx.eval(Source.newBuilder( // convert to Map to JS Object return ctx.eval(Source.newBuilder( // convert to Map to JS Object
"js", """ "js",
(function (mapOfValues) { "(function (mapOfValues) {\n" + "let rv = {};\n" + "for (var key in mapOfValues) {\n"
let rv = {}; + " rv[key] = mapOfValues.get(key);\n" + "}\n" + "return rv;\n" + "})",
for (var key in mapOfValues) { "<generated>").build()).execute(map);
rv[key] = mapOfValues.get(key);
}
return rv;
})\
""", "<generated>").build()).execute(map);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalArgumentException("Failed to generate exports", e); throw new IllegalArgumentException("Failed to generate exports", e);
} }
@@ -104,7 +94,7 @@ public class ScriptExtensionModuleProvider {
for (Map.Entry<String, Object> entry : rv.entrySet()) { for (Map.Entry<String, Object> entry : rv.entrySet()) {
if (entry.getValue() instanceof ScriptedAutomationManager) { if (entry.getValue() instanceof ScriptedAutomationManager) {
entry.setValue(new ThreadsafeWrappingScriptedAutomationManagerDelegate( entry.setValue(new ThreadsafeWrappingScriptedAutomationManagerDelegate(
(ScriptedAutomationManager) entry.getValue(), lock)); (ScriptedAutomationManager) entry.getValue()));
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -14,48 +14,47 @@ package org.openhab.automation.jsscripting.internal.fs.watch;
import java.io.File; import java.io.File;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.OpenHAB;
import org.openhab.core.automation.module.script.ScriptDependencyTracker; import org.openhab.core.automation.module.script.rulesupport.loader.DependencyTracker;
import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptDependencyTracker;
import org.openhab.core.service.WatchService;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger;
import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.LoggerFactory;
import org.osgi.service.component.annotations.ReferencePolicy;
/** /**
* Tracks JS module dependencies * Tracks JS module dependencies
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/ */
@Component(service = JSDependencyTracker.class) @Component(immediate = true, service = JSDependencyTracker.class)
@NonNullByDefault public class JSDependencyTracker extends DependencyTracker {
public class JSDependencyTracker extends AbstractScriptDependencyTracker {
private static final String LIB_PATH = String.join(File.separator, "automation", "js", "node_modules"); private final Logger logger = LoggerFactory.getLogger(JSDependencyTracker.class);
public static final String LIB_PATH = String.join(File.separator, OpenHAB.getConfigFolder(), "automation", "js",
"node_modules");
public JSDependencyTracker() {
super(LIB_PATH);
}
@Activate @Activate
public JSDependencyTracker(@Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService) { public void activate() {
super(watchService, LIB_PATH); File directory = new File(LIB_PATH);
if (!directory.exists()) {
if (!directory.mkdirs()) {
logger.warn("Failed to create watched directory: {}", LIB_PATH);
}
} else if (directory.isFile()) {
logger.warn("Trying to watch directory {}, however it is a file", LIB_PATH);
}
super.activate();
} }
@Deactivate @Deactivate
@Override
public void deactivate() { public void deactivate() {
super.deactivate(); super.deactivate();
} }
@Override
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeChangeTracker")
public void addChangeTracker(ScriptDependencyTracker.Listener listener) {
super.addChangeTracker(listener);
}
@Override
public void removeChangeTracker(ScriptDependencyTracker.Listener listener) {
super.removeChangeTracker(listener);
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -13,45 +13,53 @@
package org.openhab.automation.jsscripting.internal.fs.watch; package org.openhab.automation.jsscripting.internal.fs.watch;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.util.Optional; import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.automation.module.script.ScriptEngineManager; import org.openhab.core.automation.module.script.ScriptEngineManager;
import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher; import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileReference;
import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher; import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher;
import org.openhab.core.service.ReadyService; import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.service.WatchService;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
/** /**
* Monitors {@code <openHAB-conf>/automation/js} for Javascript files, but not libraries * Monitors <openHAB-conf>/automation/js for Javascript files
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
* @author Jan N. Klug - Refactored to new WatchService
*/ */
@Component(immediate = true, service = { ScriptFileWatcher.class, ScriptDependencyTracker.Listener.class }) @Component(immediate = true)
@NonNullByDefault public class JSScriptFileWatcher extends ScriptFileWatcher {
public class JSScriptFileWatcher extends AbstractScriptFileWatcher {
private static final String FILE_DIRECTORY = "automation" + File.separator + "js"; private static final String FILE_DIRECTORY = "automation" + File.separator + "js";
@Activate @Activate
public JSScriptFileWatcher(final @Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService, public JSScriptFileWatcher(final @Reference ScriptEngineManager manager, final @Reference ReadyService readyService,
final @Reference ScriptEngineManager manager, final @Reference ReadyService readyService, final @Reference JSDependencyTracker jsDependencyTracker) {
final @Reference StartLevelService startLevelService) { super(manager, jsDependencyTracker, readyService, FILE_DIRECTORY);
super(watchService, manager, readyService, startLevelService, FILE_DIRECTORY, true); }
@Activate
@Override
public void activate() {
super.activate();
}
@Deactivate
@Override
public void deactivate() {
super.deactivate();
} }
@Override @Override
protected Optional<String> getScriptType(Path scriptFilePath) { protected boolean createAndLoad(ScriptFileReference ref) {
String scriptType = super.getScriptType(scriptFilePath).orElse(null); return super.createAndLoad(new ScriptFileReference(ref.getScriptFileURL()) {
if (!scriptFilePath.startsWith(getWatchPath().resolve("node_modules")) && ("js".equals(scriptType))) { @Override
return Optional.of(scriptType); public Optional<String> getScriptType() {
} assert super.getScriptType().get().equalsIgnoreCase("js");
return Optional.empty(); return Optional.of(GraalJSScriptEngineFactory.MIME_TYPE);
}
});
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -17,12 +17,9 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptExtensionProvider; import org.openhab.core.automation.module.script.ScriptExtensionProvider;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
@@ -33,7 +30,7 @@ import org.osgi.service.component.annotations.Activate;
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
*/ */
public abstract class AbstractScriptExtensionProvider implements ScriptExtensionProvider { public abstract class AbstractScriptExtensionProvider implements ScriptExtensionProvider {
private Map<String, Function<String, Object>> types = new HashMap<>(); private Map<String, Function<String, Object>> types;
private Map<String, Map<String, Object>> idToTypes = new ConcurrentHashMap<>(); private Map<String, Map<String, Object>> idToTypes = new ConcurrentHashMap<>();
protected abstract String getPresetName(); protected abstract String getPresetName();
@@ -46,7 +43,7 @@ public abstract class AbstractScriptExtensionProvider implements ScriptExtension
@Activate @Activate
public void activate(final BundleContext context) { public void activate(final BundleContext context) {
types.clear(); types = new HashMap<>();
initializeTypes(context); initializeTypes(context);
} }
@@ -57,7 +54,7 @@ public abstract class AbstractScriptExtensionProvider implements ScriptExtension
@Override @Override
public Collection<String> getPresets() { public Collection<String> getPresets() {
return Set.of(getPresetName()); return Collections.singleton(getPresetName());
} }
@Override @Override
@@ -66,10 +63,10 @@ public abstract class AbstractScriptExtensionProvider implements ScriptExtension
} }
@Override @Override
public @Nullable Object get(String scriptIdentifier, String type) throws IllegalArgumentException { public Object get(String scriptIdentifier, String type) throws IllegalArgumentException {
Map<String, Object> forScript = idToTypes.computeIfAbsent(scriptIdentifier, k -> new HashMap<>()); Map<String, Object> forScript = idToTypes.computeIfAbsent(scriptIdentifier, k -> new HashMap<>());
return forScript.computeIfAbsent(type, return forScript.computeIfAbsent(type, k -> types.get(k).apply(scriptIdentifier));
k -> Objects.nonNull(types.get(k)) ? types.get(k).apply(scriptIdentifier) : null);
} }
@Override @Override

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scope;
//import com.oracle.truffle.js.runtime.java.adapter.JavaAdapterFactory;
/**
* Class utility to allow creation of 'extendable' classes with a classloader of the current bundle, rather than the
* classloader of the file being extended.
*
* @author Jonathan Gilbert - Initial contribution
*/
public class ClassExtender {
private ClassLoader classLoader = getClass().getClassLoader();
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scope;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Allows scripts to register for lifecycle events
*
* @author Jonathan Gilbert - Initial contribution
*/
public class Lifecycle implements ScriptDisposalAware {
private static final Logger logger = LoggerFactory.getLogger(Lifecycle.class);
public static final int DEFAULT_PRIORITY = 50;
private List<Hook> listeners = new ArrayList<>();
public void addDisposeHook(Consumer<Object> listener, int priority) {
addListener(listener, priority);
}
public void addDisposeHook(Consumer<Object> listener) {
addDisposeHook(listener, DEFAULT_PRIORITY);
}
private void addListener(Consumer<Object> listener, int priority) {
listeners.add(new Hook(priority, listener));
}
@Override
public void unload(String scriptIdentifier) {
try {
listeners.stream().sorted(Comparator.comparingInt(h -> h.priority))
.forEach(h -> h.fn.accept(scriptIdentifier));
} catch (RuntimeException ex) {
logger.warn("Script unloading halted due to exception in disposal: {}: {}", ex.getClass(), ex.getMessage());
}
}
private static class Hook {
public Hook(int priority, Consumer<Object> fn) {
this.priority = priority;
this.fn = fn;
}
int priority;
Consumer<Object> fn;
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -31,6 +31,10 @@ public class OSGiScriptExtensionProvider extends ScriptDisposalAwareScriptExtens
@Override @Override
protected void initializeTypes(final BundleContext context) { protected void initializeTypes(final BundleContext context) {
ClassExtender classExtender = new ClassExtender();
addType("bundleContext", k -> context); addType("bundleContext", k -> context);
addType("lifecycle", k -> new Lifecycle());
addType("classutil", k -> classExtender);
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -17,12 +17,9 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptExtensionProvider; import org.openhab.core.automation.module.script.ScriptExtensionProvider;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
@@ -58,7 +55,7 @@ public abstract class ScriptDisposalAwareScriptExtensionProvider
@Override @Override
public Collection<String> getPresets() { public Collection<String> getPresets() {
return Set.of(getPresetName()); return Collections.singleton(getPresetName());
} }
@Override @Override
@@ -67,10 +64,10 @@ public abstract class ScriptDisposalAwareScriptExtensionProvider
} }
@Override @Override
public @Nullable Object get(String scriptIdentifier, String type) throws IllegalArgumentException { public Object get(String scriptIdentifier, String type) throws IllegalArgumentException {
Map<String, Object> forScript = idToTypes.computeIfAbsent(scriptIdentifier, k -> new HashMap<>()); Map<String, Object> forScript = idToTypes.computeIfAbsent(scriptIdentifier, k -> new HashMap<>());
return forScript.computeIfAbsent(type, return forScript.computeIfAbsent(type, k -> types.get(k).apply(scriptIdentifier));
k -> Objects.nonNull(types.get(k)) ? types.get(k).apply(scriptIdentifier) : null);
} }
@Override @Override
@@ -92,8 +89,8 @@ public abstract class ScriptDisposalAwareScriptExtensionProvider
if (forScript != null) { if (forScript != null) {
for (Object o : forScript.values()) { for (Object o : forScript.values()) {
if (o instanceof ScriptDisposalAware script) { if (o instanceof ScriptDisposalAware) {
script.unload(scriptIdentifier); ((ScriptDisposalAware) o).unload(scriptIdentifier);
} }
} }
} }

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scope;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptExtensionProvider;
import org.osgi.service.component.annotations.Component;
/**
* Shared Cache implementation for JS scripting.
*
* @author Jonathan Gilbert - Initial contribution
*/
@Component(immediate = true)
@NonNullByDefault
public class SharedCache implements ScriptExtensionProvider {
private static final String PRESET_NAME = "cache";
private static final String OBJECT_NAME = "sharedcache";
private JSCache cache = new JSCache();
@Override
public Collection<String> getDefaultPresets() {
return Set.of(PRESET_NAME);
}
@Override
public Collection<String> getPresets() {
return Set.of(PRESET_NAME);
}
@Override
public Collection<String> getTypes() {
return Set.of(OBJECT_NAME);
}
@Override
public @Nullable Object get(String scriptIdentifier, String type) throws IllegalArgumentException {
if (OBJECT_NAME.equals(type)) {
return cache;
}
return null;
}
@Override
public Map<String, Object> importPreset(String scriptIdentifier, String preset) {
if (PRESET_NAME.equals(preset)) {
final Object requestedType = get(scriptIdentifier, OBJECT_NAME);
if (requestedType != null) {
return Map.of(OBJECT_NAME, requestedType);
}
}
return Collections.emptyMap();
}
@Override
public void unload(String scriptIdentifier) {
// ignore for now
}
public static class JSCache {
private Map<String, Object> backingMap = new HashMap<>();
public void put(String k, Object v) {
backingMap.put(k, v);
}
public @Nullable Object remove(String k) {
return backingMap.remove(k);
}
public @Nullable Object get(String k) {
return backingMap.get(k);
}
public @Nullable Object get(String k, Supplier<Object> supplier) {
return backingMap.computeIfAbsent(k, (unused_key) -> supplier.get());
}
}
}

View File

@@ -0,0 +1,128 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scriptengine;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
/**
* {@link ScriptEngine} implementation that delegates to a supplied ScriptEngine instance. Allows overriding specific
* methods.
*
* @author Jonathan Gilbert - Initial contribution
*/
public abstract class DelegatingScriptEngineWithInvocable<T extends ScriptEngine & Invocable>
implements ScriptEngine, Invocable {
protected T delegate;
public DelegatingScriptEngineWithInvocable(T delegate) {
this.delegate = delegate;
}
@Override
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
return delegate.eval(s, scriptContext);
}
@Override
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
return delegate.eval(reader, scriptContext);
}
@Override
public Object eval(String s) throws ScriptException {
return delegate.eval(s);
}
@Override
public Object eval(Reader reader) throws ScriptException {
return delegate.eval(reader);
}
@Override
public Object eval(String s, Bindings bindings) throws ScriptException {
return delegate.eval(s, bindings);
}
@Override
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
return delegate.eval(reader, bindings);
}
@Override
public void put(String s, Object o) {
delegate.put(s, o);
}
@Override
public Object get(String s) {
return delegate.get(s);
}
@Override
public Bindings getBindings(int i) {
return delegate.getBindings(i);
}
@Override
public void setBindings(Bindings bindings, int i) {
delegate.setBindings(bindings, i);
}
@Override
public Bindings createBindings() {
return delegate.createBindings();
}
@Override
public ScriptContext getContext() {
return delegate.getContext();
}
@Override
public void setContext(ScriptContext scriptContext) {
delegate.setContext(scriptContext);
}
@Override
public ScriptEngineFactory getFactory() {
return delegate.getFactory();
}
@Override
public Object invokeMethod(Object o, String s, Object... objects) throws ScriptException, NoSuchMethodException {
return delegate.invokeMethod(o, s, objects);
}
@Override
public Object invokeFunction(String s, Object... objects) throws ScriptException, NoSuchMethodException {
return delegate.invokeFunction(s, objects);
}
@Override
public <T> T getInterface(Class<T> aClass) {
return delegate.getInterface(aClass);
}
@Override
public <T> T getInterface(Object o, Class<T> aClass) {
return delegate.getInterface(o, aClass);
}
}

View File

@@ -1,136 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scriptengine;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNull;
/**
* {@link ScriptEngine} implementation that delegates to a supplied ScriptEngine instance. Allows overriding specific
* methods.
*
* @author Jonathan Gilbert - Initial contribution
*/
public abstract class DelegatingScriptEngineWithInvocableAndAutocloseable<T extends ScriptEngine & Invocable & AutoCloseable>
implements ScriptEngine, Invocable, AutoCloseable {
protected @NonNull T delegate;
public DelegatingScriptEngineWithInvocableAndAutocloseable(@NonNull T delegate) {
this.delegate = delegate;
}
@Override
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
return delegate.eval(s, scriptContext);
}
@Override
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
return delegate.eval(reader, scriptContext);
}
@Override
public Object eval(String s) throws ScriptException {
return delegate.eval(s);
}
@Override
public Object eval(Reader reader) throws ScriptException {
return delegate.eval(reader);
}
@Override
public Object eval(String s, Bindings bindings) throws ScriptException {
return delegate.eval(s, bindings);
}
@Override
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
return delegate.eval(reader, bindings);
}
@Override
public void put(String s, Object o) {
delegate.put(s, o);
}
@Override
public Object get(String s) {
return delegate.get(s);
}
@Override
public Bindings getBindings(int i) {
return delegate.getBindings(i);
}
@Override
public void setBindings(Bindings bindings, int i) {
delegate.setBindings(bindings, i);
}
@Override
public Bindings createBindings() {
return delegate.createBindings();
}
@Override
public ScriptContext getContext() {
return delegate.getContext();
}
@Override
public void setContext(ScriptContext scriptContext) {
delegate.setContext(scriptContext);
}
@Override
public ScriptEngineFactory getFactory() {
return delegate.getFactory();
}
@Override
public Object invokeMethod(Object o, String s, Object... objects)
throws ScriptException, NoSuchMethodException, IllegalArgumentException {
return delegate.invokeMethod(o, s, objects);
}
@Override
public Object invokeFunction(String s, Object... objects) throws ScriptException, NoSuchMethodException {
return delegate.invokeFunction(s, objects);
}
@Override
public <T> T getInterface(Class<T> aClass) {
return delegate.getInterface(aClass);
}
@Override
public <T> T getInterface(Object o, Class<T> aClass) {
return delegate.getInterface(o, aClass);
}
@Override
public void close() throws Exception {
delegate.close();
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scriptengine;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
/**
* Delegate allowing AOP-style interception of calls, either before Invocation, or upon a {@link ScriptException}.
* being thrown.
*
* @param <T> The delegate class
* @author Jonathan Gilbert - Initial contribution
*/
public abstract class InvocationInterceptingScriptEngineWithInvocable<T extends ScriptEngine & Invocable>
extends DelegatingScriptEngineWithInvocable<T> {
public InvocationInterceptingScriptEngineWithInvocable(T delegate) {
super(delegate);
}
protected void beforeInvocation() {
}
protected ScriptException afterThrowsInvocation(ScriptException se) {
return se;
}
@Override
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
try {
beforeInvocation();
return super.eval(s, scriptContext);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
try {
beforeInvocation();
return super.eval(reader, scriptContext);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object eval(String s) throws ScriptException {
try {
beforeInvocation();
return super.eval(s);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object eval(Reader reader) throws ScriptException {
try {
beforeInvocation();
return super.eval(reader);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object eval(String s, Bindings bindings) throws ScriptException {
try {
beforeInvocation();
return super.eval(s, bindings);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
try {
beforeInvocation();
return super.eval(reader, bindings);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object invokeMethod(Object o, String s, Object... objects) throws ScriptException, NoSuchMethodException {
try {
beforeInvocation();
return super.invokeMethod(o, s, objects);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
@Override
public Object invokeFunction(String s, Object... objects) throws ScriptException, NoSuchMethodException {
try {
beforeInvocation();
return super.invokeFunction(s, objects);
} catch (ScriptException se) {
throw afterThrowsInvocation(se);
}
}
}

View File

@@ -1,159 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.scriptengine;
import java.io.Reader;
import java.lang.reflect.UndeclaredThrowableException;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
/**
* Delegate allowing AOP-style interception of calls, either before Invocation, or upon a {@link ScriptException}.
* being thrown.
*
* @param <T> The delegate class
* @author Jonathan Gilbert - Initial contribution
*/
public abstract class InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<T extends ScriptEngine & Invocable & AutoCloseable>
extends DelegatingScriptEngineWithInvocableAndAutocloseable<T> {
public InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable(T delegate) {
super(delegate);
}
protected void beforeInvocation() {
}
protected Object afterInvocation(Object obj) {
return obj;
}
protected Exception afterThrowsInvocation(Exception e) {
return e;
}
@Override
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(s, scriptContext));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(reader, scriptContext));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object eval(String s) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(s));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object eval(Reader reader) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(reader));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object eval(String s, Bindings bindings) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(s, bindings));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
try {
beforeInvocation();
return afterInvocation(super.eval(reader, bindings));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object invokeMethod(Object o, String s, Object... objects)
throws ScriptException, NoSuchMethodException, NullPointerException, IllegalArgumentException {
try {
beforeInvocation();
return afterInvocation(super.invokeMethod(o, s, objects));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeMethod to avoid
// deadlocks
throw (NoSuchMethodException) afterThrowsInvocation(e);
} catch (NullPointerException e) {
throw (NullPointerException) afterThrowsInvocation(e);
} catch (IllegalArgumentException e) {
throw (IllegalArgumentException) afterThrowsInvocation(e);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
@Override
public Object invokeFunction(String s, Object... objects)
throws ScriptException, NoSuchMethodException, NullPointerException {
try {
beforeInvocation();
return afterInvocation(super.invokeFunction(s, objects));
} catch (ScriptException se) {
throw (ScriptException) afterThrowsInvocation(se);
} catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeFunction to avoid
// deadlocks
throw (NoSuchMethodException) afterThrowsInvocation(e);
} catch (NullPointerException e) {
throw (NullPointerException) afterThrowsInvocation(e);
} catch (Exception e) {
throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
}
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -15,7 +15,6 @@ package org.openhab.automation.jsscripting.internal.threading;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@@ -31,7 +30,7 @@ import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.Configuration;
/** /**
* A version of {@link SimpleRule} which controls multithreaded execution access to this specific rule. This is useful * An version of {@link SimpleRule} which controls multithreaded execution access to this specific rule. This is useful
* for rules which wrap GraalJS Contexts, which are not multithreaded. * for rules which wrap GraalJS Contexts, which are not multithreaded.
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
@@ -39,7 +38,7 @@ import org.openhab.core.config.core.Configuration;
@NonNullByDefault @NonNullByDefault
class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler { class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
private final Lock lock; private final Object lock;
private final SimpleRule delegate; private final SimpleRule delegate;
/** /**
@@ -48,7 +47,7 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
* @param lock rule executions will synchronize on this object * @param lock rule executions will synchronize on this object
* @param delegate the delegate to forward invocations to * @param delegate the delegate to forward invocations to
*/ */
ThreadsafeSimpleRuleDelegate(Lock lock, SimpleRule delegate) { ThreadsafeSimpleRuleDelegate(Object lock, SimpleRule delegate) {
this.lock = lock; this.lock = lock;
this.delegate = delegate; this.delegate = delegate;
} }
@@ -56,11 +55,8 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
@Override @Override
@NonNullByDefault({}) @NonNullByDefault({})
public Object execute(Action module, Map<String, ?> inputs) { public Object execute(Action module, Map<String, ?> inputs) {
lock.lock(); synchronized (lock) {
try {
return delegate.execute(module, inputs); return delegate.execute(module, inputs);
} finally { // Make sure that Lock is unlocked regardless of an exception is thrown or not to avoid deadlocks
lock.unlock();
} }
} }

View File

@@ -1,213 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscripting.internal.threading;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.action.ScriptExecution;
import org.openhab.core.automation.module.script.action.Timer;
import org.openhab.core.scheduler.ScheduledCompletableFuture;
import org.openhab.core.scheduler.Scheduler;
import org.openhab.core.scheduler.SchedulerTemporalAdjuster;
/**
* A polyfill implementation of NodeJS timer functionality (<code>setTimeout()</code>, <code>setInterval()</code> and
* the cancel methods) which controls multithreaded execution access to the single-threaded GraalJS contexts.
*
* @author Florian Hotze - Initial contribution; Reimplementation to conform standard JS setTimeout and setInterval;
* Threadsafe reimplementation of the timer creation methods of {@link ScriptExecution}
*/
public class ThreadsafeTimers {
private final Lock lock;
private final Scheduler scheduler;
private final ScriptExecution scriptExecution;
// Mapping of positive, non-zero integer values (used as timeoutID or intervalID) and the Scheduler
private final Map<Long, ScheduledCompletableFuture<Object>> idSchedulerMapping = new ConcurrentHashMap<>();
private AtomicLong lastId = new AtomicLong();
private String identifier = "noIdentifier";
public ThreadsafeTimers(Lock lock, ScriptExecution scriptExecution, Scheduler scheduler) {
this.lock = lock;
this.scheduler = scheduler;
this.scriptExecution = scriptExecution;
}
/**
* Set the identifier base string used for naming scheduled jobs.
*
* @param identifier identifier to use
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* Schedules a block of code for later execution.
*
* @param instant the point in time when the code should be executed
* @param closure the code block to execute
* @return a handle to the created timer, so that it can be canceled or rescheduled
*/
public Timer createTimer(ZonedDateTime instant, Runnable closure) {
return createTimer(identifier, instant, closure);
}
/**
* Schedules a block of code for later execution.
*
* @param identifier an optional identifier
* @param instant the point in time when the code should be executed
* @param closure the code block to execute
* @return a handle to the created timer, so that it can be canceled or rescheduled
*/
public Timer createTimer(@Nullable String identifier, ZonedDateTime instant, Runnable closure) {
return scriptExecution.createTimer(identifier, instant, () -> {
lock.lock();
try {
closure.run();
} finally { // Make sure that Lock is unlocked regardless of an exception is thrown or not to avoid
// deadlocks
lock.unlock();
}
});
}
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/setTimeout"><code>setTimeout()</code></a> polyfill.
* Sets a timer which executes a given function once the timer expires.
*
* @param callback function to run after the given delay
* @param delay time in milliseconds that the timer should wait before the callback is executed
* @return Positive integer value which identifies the timer created; this value can be passed to
* <code>clearTimeout()</code> to cancel the timeout.
*/
public long setTimeout(Runnable callback, Long delay) {
long id = lastId.incrementAndGet();
ScheduledCompletableFuture<Object> future = scheduler.schedule(() -> {
lock.lock();
try {
callback.run();
idSchedulerMapping.remove(id);
} finally { // Make sure that Lock is unlocked regardless of an exception is thrown or not to avoid
// deadlocks
lock.unlock();
}
}, identifier + ".timeout." + id, Instant.now().plusMillis(delay));
idSchedulerMapping.put(id, future);
return id;
}
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout"><code>clearTimeout()</code></a> polyfill.
* Cancels a timeout previously created by <code>setTimeout()</code>.
*
* @param timeoutId The identifier of the timeout you want to cancel. This ID was returned by the corresponding call
* to setTimeout().
*/
public void clearTimeout(long timeoutId) {
ScheduledCompletableFuture<Object> scheduled = idSchedulerMapping.remove(timeoutId);
if (scheduled != null) {
scheduled.cancel(true);
}
}
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/setInterval"><code>setInterval()</code></a> polyfill.
* Repeatedly calls a function with a fixed time delay between each call.
*
* @param callback function to run
* @param delay time in milliseconds that the timer should delay in between executions of the callback
* @return Numeric, non-zero value which identifies the timer created; this value can be passed to
* <code>clearInterval()</code> to cancel the interval.
*/
public long setInterval(Runnable callback, Long delay) {
long id = lastId.incrementAndGet();
ScheduledCompletableFuture<Object> future = scheduler.schedule(() -> {
lock.lock();
try {
callback.run();
} finally { // Make sure that Lock is unlocked regardless of an exception is thrown or not to avoid
// deadlocks
lock.unlock();
}
}, identifier + ".interval." + id, new LoopingAdjuster(Duration.ofMillis(delay)));
idSchedulerMapping.put(id, future);
return id;
}
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/clearInterval"><code>clearInterval()</code></a>
* polyfill.
* Cancels a timed, repeating action which was previously established by a call to <code>setInterval()</code>.
*
* @param intervalID The identifier of the repeated action you want to cancel. This ID was returned by the
* corresponding call to <code>setInterval()</code>.
*/
public void clearInterval(long intervalID) {
clearTimeout(intervalID);
}
/**
* Cancels all timed actions (i.e. timeouts and intervals) that were created with this instance of
* {@link ThreadsafeTimers}.
* Should be called in a de-initialization/unload hook of the script engine to avoid having scheduled jobs that are
* running endless.
*/
public void clearAll() {
idSchedulerMapping.forEach((id, future) -> future.cancel(true));
idSchedulerMapping.clear();
}
/**
* This is a temporal adjuster that takes a single delay.
* This adjuster makes the scheduler run as a fixed rate scheduler from the first time adjustInto was called.
*
* @author Florian Hotze - Initial contribution
*/
private static class LoopingAdjuster implements SchedulerTemporalAdjuster {
private Duration delay;
private @Nullable Temporal timeDone;
LoopingAdjuster(Duration delay) {
this.delay = delay;
}
@Override
public boolean isDone(Temporal temporal) {
// Always return false so that a new job will be scheduled
return false;
}
@Override
public Temporal adjustInto(Temporal temporal) {
Temporal localTimeDone = timeDone;
Temporal nextTime;
if (localTimeDone != null) {
nextTime = localTimeDone.plus(delay);
} else {
nextTime = temporal.plus(delay);
}
timeDone = nextTime;
return nextTime;
}
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -13,8 +13,6 @@
package org.openhab.automation.jsscripting.internal.threading; package org.openhab.automation.jsscripting.internal.threading;
import java.util.concurrent.locks.Lock;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.automation.Rule; import org.openhab.core.automation.Rule;
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager; import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager;
@@ -33,18 +31,15 @@ import org.openhab.core.automation.type.TriggerType;
* instance of this class that they are registered with. * instance of this class that they are registered with.
* *
* @author Jonathan Gilbert - Initial contribution * @author Jonathan Gilbert - Initial contribution
* @author Florian Hotze - Pass in lock object for multi-thread synchronization; Switch to {@link Lock} for multi-thread
* synchronization
*/ */
@NonNullByDefault @NonNullByDefault
public class ThreadsafeWrappingScriptedAutomationManagerDelegate { public class ThreadsafeWrappingScriptedAutomationManagerDelegate {
private ScriptedAutomationManager delegate; private ScriptedAutomationManager delegate;
private final Lock lock; private Object lock = new Object();
public ThreadsafeWrappingScriptedAutomationManagerDelegate(ScriptedAutomationManager delegate, Lock lock) { public ThreadsafeWrappingScriptedAutomationManagerDelegate(ScriptedAutomationManager delegate) {
this.delegate = delegate; this.delegate = delegate;
this.lock = lock;
} }
public void removeModuleType(String UID) { public void removeModuleType(String UID) {
@@ -65,8 +60,8 @@ public class ThreadsafeWrappingScriptedAutomationManagerDelegate {
public Rule addRule(Rule element) { public Rule addRule(Rule element) {
// wrap in a threadsafe version, safe per context // wrap in a threadsafe version, safe per context
if (element instanceof SimpleRule rule) { if (element instanceof SimpleRule) {
element = new ThreadsafeSimpleRuleDelegate(lock, rule); element = new ThreadsafeSimpleRuleDelegate(lock, (SimpleRule) element);
} }
return delegate.addRule(element); return delegate.addRule(element);

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="jsscripting" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>automation</type>
<name>JavaScript Scripting</name>
<description>This adds a JS (ECMAScript-2021) script engine.</description>
<connection>none</connection>
<service-id>org.openhab.jsscripting</service-id>
<config-description-ref uri="automation:jsscripting"/>
</addon:addon>

View File

@@ -7,9 +7,8 @@
<config-description uri="automation:jsscripting"> <config-description uri="automation:jsscripting">
<parameter name="injectionEnabled" type="boolean" required="true"> <parameter name="injectionEnabled" type="boolean" required="true">
<label>Use Built-in Global Variables</label> <label>Use Built-in Global Variables</label>
<description><![CDATA[ <description><![CDATA[ Import all variables from the OH scripting library into all rules for common services like items, things, actions, log, etc... <br>
Import all variables from the openHAB JavaScript library into all rules for common services like items, things, actions, log, etc... <br> If disabled, the OH scripting library can be imported manually using "<i>require('openhab')</i>"
If disabled, the openHAB JavaScript library can be imported manually using "<i>require('openhab')</i>"
]]></description> ]]></description>
<options> <options>
<option value="true">Use Built-in Variables</option> <option value="true">Use Built-in Variables</option>
@@ -17,17 +16,5 @@
</options> </options>
<default>true</default> <default>true</default>
</parameter> </parameter>
<parameter name="injectionCachingEnabled" type="boolean" required="true">
<label>Cache openHAB JavaScript Library Injection</label>
<description><![CDATA[
Cache the openHAB JavaScript library injection for optimal performance.<br>
Disable this option to allow loading the library from the local user configuration directory "automation/js/node_modules". Disabling caching may increase script loading times, especially on less powerful systems.
]]></description>
<options>
<option value="true">Cache Library Injection</option>
<option value="false">Do Not Cache Library Injection</option>
</options>
<default>true</default>
</parameter>
</config-description> </config-description>
</config-description:config-descriptions> </config-description:config-descriptions>

View File

@@ -1,15 +1,8 @@
# add-on
addon.jsscripting.name = JavaScript Scripting
addon.jsscripting.description = This adds a JS (ECMAScript-2021) script engine.
# add-on
automation.config.jsscripting.injectionCachingEnabled.label = Cache openHAB JavaScript Library Injection
automation.config.jsscripting.injectionCachingEnabled.description = Cache the openHAB JavaScript library injection for optimal performance.<br>Disable this option to allow loading the library from the local user configuration directory "automation/js/node_modules". Disabling caching may increase script loading times, especially on less powerful systems.
automation.config.jsscripting.injectionCachingEnabled.option.true = Cache Library Injection
automation.config.jsscripting.injectionCachingEnabled.option.false = Do Not Cache Library Injection
automation.config.jsscripting.injectionEnabled.label = Use Built-in Global Variables automation.config.jsscripting.injectionEnabled.label = Use Built-in Global Variables
automation.config.jsscripting.injectionEnabled.description = Import all variables from the openHAB JavaScript library into all rules for common services like items, things, actions, log, etc... <br> If disabled, the openHAB JavaScript library can be imported manually using "<i>require('openhab')</i>" automation.config.jsscripting.injectionEnabled.description = Import all variables from the OH scripting library into all rules for common services like items, things, actions, log, etc... <br> If disabled, the OH scripting library can be imported manually using "<i>require('openhab')</i>"
automation.config.jsscripting.injectionEnabled.option.true = Use Built-in Variables automation.config.jsscripting.injectionEnabled.option.true = Use Built-in Variables
automation.config.jsscripting.injectionEnabled.option.false = Do Not Use Built-in Variables automation.config.jsscripting.injectionEnabled.option.false = Do Not Use Built-in Variables
# service
service.automation.jsscripting.label = JS Scripting

View File

@@ -1,15 +0,0 @@
# add-on
addon.jsscripting.name = JavaScript Scripting
addon.jsscripting.description = Dette tilføjer en JS (ECMAScript-2021) script-motor.
# add-on
automation.config.jsscripting.injectionCachingEnabled.label = Cache openHAB JavaScript-bibliotek injektion
automation.config.jsscripting.injectionCachingEnabled.description = Brug det medfølgende openHAB JavaScript-bibliotek for optimal ydeevne.<br> Deaktivér denne indstilling for at tillade indlæsning af biblioteket fra den lokale brugerkonfigurerede mappe "automation/js/node_modules". Anvendelse af et bibliotek leveret af brugeren, kan resultere i øgede indlæsningstider, specielt på mindre kraftfulde systemer.
automation.config.jsscripting.injectionCachingEnabled.option.true = Cache injektion af bibliotek
automation.config.jsscripting.injectionCachingEnabled.option.false = Cache ikke injektion af bibliotek
automation.config.jsscripting.injectionEnabled.label = Brug indbyggede globale variabler
automation.config.jsscripting.injectionEnabled.description = Importer alle variabler fra openHAB JavaScript-biblioteket ind i alle regler for fælles tjenester som items, things, actions, log, etc... <br> Hvis deaktiveret, kan openHAB JavaScript-biblioteket importeres manuelt med "<i>require('openhab')</i>"
automation.config.jsscripting.injectionEnabled.option.true = Brug indbyggede variabler
automation.config.jsscripting.injectionEnabled.option.false = Brug ikke indbyggede variabler

View File

@@ -1,12 +0,0 @@
automation.config.jsscripting.injectionEnabled.label = Globale Integrierte Variablen
automation.config.jsscripting.injectionEnabled.description = Importiere alle globalen Variablen der openHAB Scripting Bibliothek in allen Regeln für häufig genutzte Dienste wie z. B. Items, Things, Actions, usw... <br> Wenn diese Option deaktiviert ist, dann kann die openHAB Scripting Bibliothek manuell mit "<i>require('openhab')</i>" importiert werden
automation.config.jsscripting.injectionEnabled.option.true = Integrierte Variablen verwenden
automation.config.jsscripting.injectionEnabled.option.false = Integrierten Variablen nicht verwenden
automation.config.jsscripting.useIncludedLibrary.label = Integrierte openHAB Scripting Bibliothek
automation.config.jsscripting.useIncludedLibrary.description = Verwende die mitgelieferte openHAB JavaScript-Bibliothek für optimale Performance.<br>Deaktiviere diese Option, um das Laden der Bibliothek aus dem lokalen Benutzerkonfigurationsverzeichnis "automation/js/node_modules" zu ermöglichen. Die Verwendung einer vom Benutzer zur Verfügung gestellten Version der Bibliothek kann die Ladezeiten des Skripts erhöhen, insbesondere auf weniger leistungsfähigen Systemen.
automation.config.jsscripting.useIncludedLibrary.option.true = Integrierte Bibliothek verwenden
automation.config.jsscripting.useIncludedLibrary.option.false = Integrierte Bibliothek nicht verwenden
# service
service.automation.jsscripting.label = JavaScript Scripting

View File

@@ -1,8 +0,0 @@
automation.config.jsscripting.injectionEnabled.label = Beépített teljes körű változók használata
automation.config.jsscripting.injectionEnabled.description = Az összes változó betöltése az OH szkript könyvtárból az összes közös szolgáltatási szabály számára, pl.\: elemek, dolgok, akciók, naplók, stb... <br> Ha kikapcsolja, az OH szkript könyvtárt betöltheti a "<i>require('openhab')</i>" paranccsal
automation.config.jsscripting.injectionEnabled.option.true = Beépített változók használata
automation.config.jsscripting.injectionEnabled.option.false = Ne használja a beépített változókat
# service
service.automation.jsscripting.label = JS szkriptek

View File

@@ -1,15 +0,0 @@
# add-on
addon.jsscripting.name = Script JavaScript
addon.jsscripting.description = Questo aggiunge un motore di script JS (ECMAScript-2021).
# add-on
automation.config.jsscripting.injectionCachingEnabled.label = Salva Libreria per Iniezione openHAB JavaScript
automation.config.jsscripting.injectionCachingEnabled.description = Utilizza la libreria JavaScript openHAB salvata per prestazioni ottimali.<br> Disabilita questa opzione per consentire il caricamento della libreria dalla directory di configurazione utente locale "automation/js/node_modules". L'utilizzo di una versione fornita dall'utente della libreria può aumentare i tempi di caricamento degli script, soprattutto su sistemi meno potenti.
automation.config.jsscripting.injectionCachingEnabled.option.true = Inietta Libreria Salvata
automation.config.jsscripting.injectionCachingEnabled.option.false = Inietta Libreria Non Salvata
automation.config.jsscripting.injectionEnabled.label = Usa le variabili globali Integrate
automation.config.jsscripting.injectionEnabled.description = Importa tutte le variabili dalla libreria JavaScript openHAB in tutte le Rules per servizi comuni come Item, Thing, Action, Log, ecc... <br> Se disabilitata, la libreria JavaScript openHAB può essere importata manualmente usando "<i>require('openhab')</i>"
automation.config.jsscripting.injectionEnabled.option.true = Utilizza variabili integrate
automation.config.jsscripting.injectionEnabled.option.false = Non utilizzare variabili integrate

View File

@@ -1,178 +1,204 @@
// ThreadsafeTimers is injected into the JS runtime
(function (global) { (function (global) {
'use strict'; 'use strict';
// Append the script file name OR rule UID depending on which is available const System = Java.type('java.lang.System');
const defaultIdentifier = 'org.openhab.automation.script' + (globalThis['javax.script.filename'] ? '.file.' + globalThis['javax.script.filename'].replace(/^.*[\\\/]/, '') : globalThis.ruleUID ? '.ui.' + globalThis.ruleUID : ''); const log = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.automation.script");
const System = Java.type('java.lang.System'); const ScriptExecution = Java.type('org.openhab.core.model.script.actions.ScriptExecution');
const formatRegExp = /%[sdj%]/g; const ZonedDateTime = Java.type('java.time.ZonedDateTime');
// Pass the defaultIdentifier to ThreadsafeTimers to enable naming of scheduled jobs
ThreadsafeTimers.setIdentifier(defaultIdentifier);
function createLogger (name = defaultIdentifier) { const formatRegExp = /%[sdj%]/g;
return Java.type('org.slf4j.LoggerFactory').getLogger(name);
}
// User configurable function stringify(value) {
let log = createLogger(); try {
if (Java.isJavaObject(value)) {
function stringify (value) { return value.toString();
try { } else {
if (typeof value === 'string') return value; // special cases
// special cases if (value === undefined) {
if (value === undefined) { return "undefined"
return 'undefined'; }
} if (typeof value === 'function') {
if (value === null) { return "[Function]"
return 'null'; }
} if (value instanceof RegExp) {
// JSON.stringify all objects that do not polyfill toString() return value.toString();
const str = value.toString(); }
if (typeof value === 'object' && (str === '[object Object]' || str === '[object Java]')) { // fallback to JSON
return JSON.stringify(value, null, 2); return JSON.stringify(value, null, 2);
} }
return str; } catch (e) {
} catch (e) { return '[Circular: ' + e + ']';
return 'Error: failed to format log message: ' + e; }
} }
}
function format (f) { function format(f) {
try { if (typeof f !== 'string') {
const args = arguments; var objects = [];
for (var index = 0; index < arguments.length; index++) {
objects.push(stringify(arguments[index]));
}
return objects.join(' ');
}
// If there is only one argument, stringify and return it if (arguments.length === 1) return f;
if (args.length === 1) return stringify(f);
// Else if the first arg is string, do regex string formatting var i = 1;
// the number of args after the formatted string must match the number of % placeholder var args = arguments;
let str; var len = args.length;
let i = 1; var str = String(f).replace(formatRegExp, function (x) {
if (typeof f === 'string') { if (x === '%%') return '%';
str = String(f).replace(formatRegExp, function (x) { if (i >= len) return x;
if (x === '%%') return '%'; switch (x) {
if (i >= args.length) return x; case '%s': return String(args[i++]);
switch (x) { case '%d': return Number(args[i++]);
case '%s': return String(args[i++]); case '%j':
case '%d': return Number(args[i++]); try {
case '%j': return stringify(args[i++]);
try { } catch (_) {
return stringify(args[i++]); return '[Circular]';
} catch (e) { }
return '[Circular]'; // falls through
} default:
// falls through return x;
default: }
return x;
}
}); });
} for (var x = args[i]; i < len; x = args[++i]) {
// Else stringify and join all args if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) {
for (let x = args[i]; i < args.length; x = args[++i]) { str += ' ' + x;
str += ' ' + stringify(x); } else {
} str += ' ' + stringify(x);
return str; }
} catch (e) {
return 'Error: failed to format log message: ' + e;
}
}
const counters = {};
const timers = {};
// Polyfills for common NodeJS functions
const console = {
assert: function (expression, message) {
if (!expression) {
log.error(message);
}
},
count: function (label) {
let counter;
if (label) {
if (counters.hasOwnProperty(label)) {
counter = counters[label];
} else {
counter = 0;
} }
return str;
// update
counters[label] = ++counter;
log.debug(format.apply(null, [label + ':', counter]));
}
},
debug: function () {
log.debug(format.apply(null, arguments));
},
info: function () {
log.info(format.apply(null, arguments));
},
log: function () {
log.info(format.apply(null, arguments));
},
warn: function () {
log.warn(format.apply(null, arguments));
},
error: function () {
log.error(format.apply(null, arguments));
},
trace: function () {
log.trace(new Error(format.apply(null, arguments)).stack.replace(/^Error: /, ''));
},
time: function (label) {
if (label) {
timers[label] = System.currentTimeMillis();
}
},
timeEnd: function (label) {
if (label) {
const now = System.currentTimeMillis();
if (timers.hasOwnProperty(label)) {
log.info(format.apply(null, [label + ':', (now - timers[label]) + 'ms']));
delete timers[label];
} else {
log.info(format.apply(null, [label + ':', '<no timer>']));
}
}
},
// Allow user customizable logging names
// Be aware that a log4j2 required a logger defined for the logger name, otherwise messages won't be logged!
set loggerName (name) {
log = createLogger(name);
this._loggerName = name;
ThreadsafeTimers.setIdentifier(name);
},
get loggerName () {
return this._loggerName || defaultIdentifier;
} }
};
// Polyfill common NodeJS functions onto the global object const counters = {};
globalThis.console = console; const timers = {};
globalThis.setTimeout = function (functionRef, delay, ...args) {
return ThreadsafeTimers.setTimeout(() => functionRef(...args), delay); const console = {
}; 'assert': function (expression, message) {
globalThis.clearTimeout = ThreadsafeTimers.clearTimeout; if (!expression) {
globalThis.setInterval = function (functionRef, delay, ...args) { log.error(message);
return ThreadsafeTimers.setInterval(() => functionRef(...args), delay); }
}; },
globalThis.clearInterval = ThreadsafeTimers.clearInterval;
count: function (label) {
let counter;
if (label) {
if (counters.hasOwnProperty(label)) {
counter = counters[label];
} else {
counter = 0;
}
// update
counters[label] = ++counter;
log.debug(format.apply(null, [label + ':', counter]));
}
},
debug: function () {
log.debug(format.apply(null, arguments));
},
info: function () {
log.info(format.apply(null, arguments));
},
log: function () {
log.info(format.apply(null, arguments));
},
warn: function () {
log.warn(format.apply(null, arguments));
},
error: function () {
log.error(format.apply(null, arguments));
},
trace: function (e) {
if (Java.isJavaObject(e)) {
log.trace(e.getLocalizedMessage(), e);
} else {
if (e.stack) {
log.trace(e.stack);
} else {
if (e.message) {
log.trace(format.apply(null, [(e.name || 'Error') + ':', e.message]));
} else {
log.trace((e.name || 'Error'));
}
}
}
},
time: function (label) {
if (label) {
timers[label] = System.currentTimeMillis();
}
},
timeEnd: function (label) {
if (label) {
const now = System.currentTimeMillis();
if (timers.hasOwnProperty(label)) {
log.info(format.apply(null, [label + ':', (now - timers[label]) + 'ms']));
delete timers[label];
} else {
log.info(format.apply(null, [label + ':', '<no timer>']));
}
}
}
};
function setTimeout(cb, delay) {
const args = Array.prototype.slice.call(arguments, 2);
return ScriptExecution.createTimerWithArgument(
ZonedDateTime.now().plusNanos(delay * 1000000),
args,
function (args) {
cb.apply(global, args);
}
);
}
function clearTimeout(timer) {
if (timer !== undefined && timer.isActive()) {
timer.cancel();
}
}
function setInterval(cb, delay) {
const args = Array.prototype.slice.call(arguments, 2);
const delayNanos = delay * 1000000
let timer = ScriptExecution.createTimerWithArgument(
ZonedDateTime.now().plusNanos(delayNanos),
args,
function (args) {
cb.apply(global, args);
if (!timer.isCancelled()) {
timer.reschedule(ZonedDateTime.now().plusNanos(delayNanos));
}
}
);
return timer;
}
function clearInterval(timer) {
clearTimeout(timer);
}
//Polyfil common functions onto the global object
globalThis.console = console;
globalThis.setTimeout = setTimeout;
globalThis.clearTimeout = clearTimeout;
globalThis.setInterval = setInterval;
globalThis.clearInterval = clearInterval;
//Support legacy NodeJS libraries
globalThis.global = globalThis;
globalThis.process = { env: { NODE_ENV: '' } };
// Support legacy NodeJS libraries
globalThis.global = globalThis;
globalThis.process = { env: { NODE_ENV: '' } };
})(this); })(this);

View File

@@ -1,2 +0,0 @@
# Please check here how to add suppressions https://maven.apache.org/plugins/maven-pmd-plugin/examples/violation-exclusions.html
org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable=AvoidThrowingNullPointerException,AvoidCatchingNPE

View File

@@ -1,49 +0,0 @@
# JavaScript Scripting (Nashorn)
This add-on allows you to use your older ECMAScript 5.1 code on newer Java versions until the code is migrated to ECMAScript 2021+.
It should only be installed for providing backwards compatibility.
When writing new code it is preferred to do this using ECMAScript 2021+ for which support is provided by installing the [JavaScript Scripting](https://www.openhab.org/addons/automation/jsscripting/) add-on.
This add-on uses a standalone [Nashorn Engine](https://github.com/openjdk/nashorn) to run ECMAScript 5.1 code.
The Nashorn Engine was pre-installed in openHAB 2 and openHAB 3 because it was part of Java.
Since Java 15 the Nashorn Engine has been removed from Java.
## Creating JavaScript Scripts
When this add-on is installed, JavaScript script actions will be run by this add-on and allow ECMAScript 5.1 features.
Alternatively, you can create scripts in the `automation/jsr223` configuration directory.
If you create an empty file called `test.nashornjs`, you will see a log line with information similar to:
```text
... [INFO ] [.a.m.s.r.i.l.ScriptFileWatcher:150 ] - Loading script 'test.nashornjs'
```
To enable debug logging, use the [console logging]({{base}}/administration/logging.html) commands to enable debug logging for the automation functionality:
```text
log:set DEBUG org.openhab.core.automation
```
For more information on the available APIs in scripts see the [JSR223 Scripting]({{base}}/configuration/jsr223.html) documentation.
## Script Examples
JavaScript scripts provide access to almost all the functionality in an openHAB runtime environment.
As a simple example, the following script logs "Hello, World!".
Note that `console.log` will usually not work since the output has no terminal to display the text.
The openHAB server uses the [SLF4J](https://www.slf4j.org/) library for logging.
```js
var LoggerFactory = Java.type('org.slf4j.LoggerFactory');
LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello, World!");
```
Depending on the openHAB logging configuration, you may need to prefix logger names with `org.openhab.core.automation` for them to show up in the log file (or you modify the logging configuration).
The script uses the [LoggerFactory](https://www.slf4j.org/apidocs/org/slf4j/Logger.html) to obtain a named logger and then logs a message like:
```text
... [INFO ] [org.openhab.core.automation.examples ] - Hello, World!
```

View File

@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version>
</parent>
<artifactId>org.openhab.automation.jsscriptingnashorn</artifactId>
<name>openHAB Add-ons :: Bundles :: Automation :: JavaScript Scripting (Nashorn)</name>
<properties>
<bnd.importpackage>jdk.dynalink.*;resolution:=optional</bnd.importpackage>
<asm.version>7.3.1</asm.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>${asm.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.automation.jsscriptingnashorn-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-automation-jsscriptingnashorn" description="JavaScript Scripting (Nashorn)" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jsscriptingnashorn/${project.version}</bundle>
</feature>
</features>

View File

@@ -1,84 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscriptingnashorn.internal;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.AbstractScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.osgi.service.component.annotations.Component;
/**
* This is an implementation of a {@link ScriptEngineFactory} for Nashorn.
*
* @author Wouter Born - Initial contribution
*/
@Component(service = ScriptEngineFactory.class)
@NonNullByDefault
public class NashornScriptEngineFactory extends AbstractScriptEngineFactory {
private final org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory factory = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory();
private final List<String> scriptTypes = createScriptTypes();
private List<String> createScriptTypes() {
List<String> extensions = List.of("nashornjs");
String mimeTypeVersion = ";version=ECMAScript-5.1";
List<String> mimeTypes = factory.getMimeTypes().stream().map(mimeType -> mimeType + mimeTypeVersion)
.collect(Collectors.toUnmodifiableList());
return Stream.of(extensions, mimeTypes).flatMap(List::stream).collect(Collectors.toUnmodifiableList());
}
@Override
public List<String> getScriptTypes() {
return scriptTypes;
}
@Override
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
Set<String> expressions = new HashSet<>();
for (Entry<String, Object> entry : scopeValues.entrySet()) {
scriptEngine.put(entry.getKey(), entry.getValue());
if (entry.getValue() instanceof Class) {
expressions.add(String.format("%s = %<s.static;", entry.getKey()));
}
}
String scriptToEval = String.join("\n", expressions);
try {
scriptEngine.eval(scriptToEval);
} catch (ScriptException ex) {
logger.error("ScriptException while importing scope: {}", ex.getMessage());
}
}
@Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) {
return scriptTypes.contains(scriptType)
? factory.getScriptEngine(NashornScriptEngineFactory.class.getClassLoader())
: null;
}
}

View File

@@ -1,20 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
@org.osgi.annotation.bundle.Header(name = org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE, value = "*")
package org.openhab.automation.jsscriptingnashorn.internal;
/**
* Additional information for the JavaScript Scripting Nashorn package
*
* @author Wouter Born - Initial contribution
*/

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="jsscriptingnashorn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>automation</type>
<name>JavaScript Scripting (Nashorn)</name>
<description>This adds a JS (Nashorn) script engine.</description>
<connection>none</connection>
</addon:addon>

View File

@@ -1,43 +0,0 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.automation.jsscriptingnashorn;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.automation.jsscriptingnashorn.internal.NashornScriptEngineFactory;
/**
* Tests {@link NashornScriptEngineFactory}.
*
* @author Wouter Born - Initial contribution
*/
@NonNullByDefault
public class NashornScriptEngineFactoryTest {
@Test
public void scriptTypesAreNashornSpecific() {
List<String> scriptTypes = new NashornScriptEngineFactory().getScriptTypes();
assertThat(scriptTypes,
contains("nashornjs", "application/javascript;version=ECMAScript-5.1",
"application/ecmascript;version=ECMAScript-5.1", "text/javascript;version=ECMAScript-5.1",
"text/ecmascript;version=ECMAScript-5.1"));
assertThat(scriptTypes.size(), is(5));
}
}

View File

@@ -1,11 +1,4 @@
# Jython Scripting (DEPRECATED) # Jython Scripting
::: tip Note:
Currently, the development of Jython stopped at version 2.7 with no definite timeline to support Python 3.x.
The 3rd party openHAB helper library for Jython is also no longer maintained.
We would not recommend using Jython scripting at this point in time.
For alternatives, check out the list of other supported [automation add-ons](https://www.openhab.org/addons/#automation).
:::
This add-on provides [Jython](https://www.jython.org/) 2.7.2 that can be used as a scripting language within automation rules and which eliminates the need to download Jython and create `EXTRA_JAVA_OPTS` entries for `bootclasspath`, `python.home` and `python.path`. This add-on provides [Jython](https://www.jython.org/) 2.7.2 that can be used as a scripting language within automation rules and which eliminates the need to download Jython and create `EXTRA_JAVA_OPTS` entries for `bootclasspath`, `python.home` and `python.path`.
@@ -41,7 +34,7 @@ The openHAB server uses the [SLF4J](https://www.slf4j.org/) library for logging.
```python ```python
from org.slf4j import LoggerFactory from org.slf4j import LoggerFactory
LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello, World!") LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello world!")
``` ```
Jython can [import Java classes](https://jython.readthedocs.io/en/latest/ModulesPackages/). Jython can [import Java classes](https://jython.readthedocs.io/en/latest/ModulesPackages/).
@@ -59,5 +52,5 @@ The script uses the [LoggerFactory](https://www.slf4j.org/apidocs/org/slf4j/Logg
to obtain a named logger and then logs a message like: to obtain a named logger and then logs a message like:
```text ```text
... [INFO ] [.openhab.core.automation.examples:-2 ] - Hello, World! ... [INFO ] [.openhab.core.automation.examples:-2 ] - Hello world!
``` ```

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.automation.jythonscripting</artifactId> <artifactId>org.openhab.automation.jythonscripting</artifactId>

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="pidcontroller" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>automation</type>
<name>Jython Scripting (DEPRECATED)</name>
<description>This adds a Jython script engine.</description>
<connection>none</connection>
</addon:addon>

View File

@@ -24,7 +24,7 @@ Select the Item you like to control in the "Item Action" and leave the command e
### Trigger ### Trigger
This module triggers whenever the `input` or the `setpoint` changes or the `loopTime` expires. This module triggers whenever the `input` or the `setpoint` changes or the `loopTime` expires.
Every trigger calculates the P-, the I- and the D-part and sums them up to form the `output` value. Every trigger calculates the P, the I and the D part and sums them up to form the `output` value.
This is then transferred to the action module. This is then transferred to the action module.
| Name | Type | Description | Required | | Name | Type | Description | Required |
@@ -35,36 +35,26 @@ This is then transferred to the action module.
| `ki` | Decimal | I: [Integral Gain](#integral-i-gain-parameter) Parameter | Y | | `ki` | Decimal | I: [Integral Gain](#integral-i-gain-parameter) Parameter | Y |
| `kd` | Decimal | D: [Derivative Gain](#derivative-d-gain-parameter) Parameter | Y | | `kd` | Decimal | D: [Derivative Gain](#derivative-d-gain-parameter) Parameter | Y |
| `kdTimeConstant` | Decimal | D-T1: [Derivative Gain Time Constant](#derivative-time-constant-d-t1-parameter) in sec. | Y | | `kdTimeConstant` | Decimal | D-T1: [Derivative Gain Time Constant](#derivative-time-constant-d-t1-parameter) in sec. | Y |
| `commandItem` | String | Send a String "RESET" to this item to reset the I- and the D-part to 0. | N | | `commandItem` | String | Send a String "RESET" to this item to reset the I and the D part to 0. | N |
| `loopTime` | Decimal | The interval the output value will be updated in milliseconds. Note: the output will also be updated when the input value or the setpoint changes. | Y | | `loopTime` | Decimal | The interval the output value will be updated in milliseconds. Note: the output will also be updated when the input value or the setpoint changes. | Y |
| `integralMinValue` | Decimal | The I-part will be limited (min) to this value. | N | | `pInspector` | Item | Name of the debug Item for the current P part | N |
| `integralMaxValue` | Decimal | The I-part will be limited (max) to this value. | N | | `iInspector` | Item | Name of the debug Item for the current I part | N |
| `pInspector` | Item | Name of the inspector Item for the current P-part | N | | `dInspector` | Item | Name of the debug Item for the current D part | N |
| `iInspector` | Item | Name of the inspector Item for the current I-part | N | | `eInspector` | Item | Name of the debug Item for the current regulation difference (error) | N |
| `dInspector` | Item | Name of the inspector Item for the current D-part | N |
| `eInspector` | Item | Name of the inspector Item for the current regulation difference (error) | N |
The `loopTime` should be max a tenth of the system response. The `loopTime` should be max a tenth of the system response.
E.g. the heating needs 10 min to heat up the room, the loop time should be max 1 min. E.g. the heating needs 10 min to heat up the room, the loop time should be max 1 min.
Lower values won't harm, but need more calculation resources. Lower values won't harm, but need more calculation resources.
The I-part can be limited via `integralMinValue`/`integralMaxValue`. You can view the internal P, I and D parts of the controller with the inspector Items.
This is useful if the regulation cannot meet its setpoint from time to time.
E.g. a heating controller in the summer, which can not cool (min limit) or when the heating valve is already at 100% and the room is only slowly heating up (max limit).
When controlling a heating valve, reasonable values are 0% (min limit) and 100% (max limit).
You can view the internal P-, I- and D-parts of the controller with the inspector Items.
These values are useful when tuning the controller. These values are useful when tuning the controller.
They are updated every time the output is updated. They are updated every time the output is updated.
Inspector items are also used to recover the controller's previous state during startup. This feature allows the PID
controller parameters to be updated and openHAB to be restarted without losing the current controller state.
## Proportional (P) Gain Parameter ## Proportional (P) Gain Parameter
Parameter: `kp` Parameter: `kp`
A value of 0 disables the P-part. A value of 0 disables the P part.
A value of 1 sets the output to the current setpoint deviation (error). A value of 1 sets the output to the current setpoint deviation (error).
E.g. the setpoint is 25°C and the measured value is 20°C, the output will be set to 5. E.g. the setpoint is 25°C and the measured value is 20°C, the output will be set to 5.
@@ -77,7 +67,7 @@ Parameter: `ki`
The purpose of this parameter is to let the output drift towards the setpoint. The purpose of this parameter is to let the output drift towards the setpoint.
The bigger this parameter, the faster the drifting. The bigger this parameter, the faster the drifting.
A value of 0 disables the I-part. A value of 0 disables the I part.
A value of 1 adds the current setpoint deviation (error) to the output each `loopTime` (in milliseconds). A value of 1 adds the current setpoint deviation (error) to the output each `loopTime` (in milliseconds).
E.g. (`loopTimeMs=1000`) the setpoint is 25°C and the measured value is 20°C, the output will be set to 5 after 1 sec. E.g. (`loopTimeMs=1000`) the setpoint is 25°C and the measured value is 20°C, the output will be set to 5 after 1 sec.
@@ -91,7 +81,7 @@ Parameter: `kd`
The purpose of this parameter is to react to sudden changes (e.g. an opened window) and also to damp the regulation. The purpose of this parameter is to react to sudden changes (e.g. an opened window) and also to damp the regulation.
This makes the regulation more resilient against oscillations, i.e. bigger `kp` and `ki` values can be set. This makes the regulation more resilient against oscillations, i.e. bigger `kp` and `ki` values can be set.
A value of 0 disables the D-part. A value of 0 disables the D part.
A value of 1 sets the output to the difference between the last setpoint deviation (error) and the current. A value of 1 sets the output to the difference between the last setpoint deviation (error) and the current.
E.g. the setpoint is 25°C and the measured value is 20°C (error=5°C). E.g. the setpoint is 25°C and the measured value is 20°C (error=5°C).
@@ -101,13 +91,13 @@ When the temperature drops to 10°C due to an opened window (error=15°C), the o
Parameter: `kdTimeConstant` Parameter: `kdTimeConstant`
The purpose of this parameter is to slow down the impact of the D-part. The purpose of this parameter is to slow down the impact of the D part.
This parameter behaves like a [low-pass](https://en.wikipedia.org/wiki/Low-pass_filter) filter. This parameter behaves like a [low-pass](https://en.wikipedia.org/wiki/Low-pass_filter) filter.
The D-part will become 63% of its actual value after `kdTimeConstant` seconds and 99% after 5 times `kdTimeConstant`. E.g. `kdTimeConstant` is set to 10s, the D-part will become 99% after 50s. The D part will become 63% of its actual value after `kdTimeConstant` seconds and 99% after 5 times `kdTimeConstant`. E.g. `kdTimeConstant` is set to 10s, the D part will become 99% after 50s.
Higher values lead to a longer lasting impact of the D-part (stretching) after a change in the setpoint deviation (error). Higher values lead to a longer lasting impact of the D part (stretching) after a change in the setpoint deviation (error).
The "stretching" also results in a lower amplitude, i.e. if you increase this value, you might want to also increase `kd` to keep the height of the D-part at the same level. The "stretching" also results in a lower amplitude, i.e. if you increase this value, you might want to also increase `kd` to keep the height of the D part at the same level.
## Tuning ## Tuning
@@ -118,7 +108,7 @@ This results in quite reasonable working systems in most cases.
So, this will be described in the following. So, this will be described in the following.
To be able to proceed with this method, you need to visualize the input and the output value of the PID controller over time. To be able to proceed with this method, you need to visualize the input and the output value of the PID controller over time.
It's also good to visualize the individual P-, I- and D-parts (these are forming the output value) via the inspector items. It's also good to visualize the individual P, I and D parts (these are forming the output value) via the inspector items.
The visualization could be done by adding a persistence and use Grafana for example. The visualization could be done by adding a persistence and use Grafana for example.
After you added a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) with above trigger and action module and configured those, proceed with the following steps: After you added a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) with above trigger and action module and configured those, proceed with the following steps:
@@ -131,18 +121,10 @@ E.g. the time it takes from opening the heater valve and seeing an effect of the
3. Decrease `kp` a bit, that the system doesn't oscillate anymore 3. Decrease `kp` a bit, that the system doesn't oscillate anymore
4. Repeat the two steps for the `ki` parameter (keep `kp` set) 4. Repeat the two steps for the `ki` parameter (keep `kp` set)
5. Repeat the two steps for the `kd` parameter (keep `kp` and `ki` set) 5. Repeat the two steps for the `kd` parameter (keep `kp` and `ki` set)
6. As the D-part acts as a damper, you should now be able to increase `kp` and `ki` further without resulting in oscillations 6. As the D part acts as a damper, you should now be able to increase `kp` and `ki` further without resulting in oscillations
After each modification of above parameters, test the system response by introducing a setpoint deviation (error). After each modification of above parameters, test the system response by introducing a setpoint deviation (error).
This can be done either by changing the setpoint (e.g. 20°C -> 25°C) or by forcing the measured value to change (e.g. by opening a window). This can be done either by changing the setpoint (e.g. 20°C -> 25°C) or by forcing the measured value to change (e.g. by opening a window).
This process can take some time with slow responding control loops like heating systems. This process can take some time with slow responding control loops like heating systems.
You will get faster results with constant lighting or PV zero export applications. You will get faster results with constant lighting or PV zero export applications.
## Persisting controller state across restarts
Persisting controller state requires inspector items `iInspector`, `dInspector`, `eInspector` to be configured.
The PID controller uses these Items to expose internal state in order to restore it during startup or reload.
In addition, you need to have persistence set up for these items in openHAB. Please see openHAB documentation regarding
[Persistence](https://www.openhab.org/docs/configuration/persistence.html) for more details and instructions.

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0</version> <version>3.2.0</version>
</parent> </parent>
<artifactId>org.openhab.automation.pidcontroller</artifactId> <artifactId>org.openhab.automation.pidcontroller</artifactId>

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2010-2023 Contributors to the openHAB project * Copyright (c) 2010-2021 Contributors to the openHAB project
* *
* See the NOTICE file(s) distributed with this work for additional * See the NOTICE file(s) distributed with this work for additional
* information. * information.
@@ -15,7 +15,7 @@ package org.openhab.automation.pidcontroller.internal;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Realizes a first-order FIR low pass filter. To keep code complexity low, it is implemented as moving average (all * Realizes an first-order FIR low pass filter. To keep code complexity low, it is implemented as moving average (all
* FIR coefficients are set to normalized ones). * FIR coefficients are set to normalized ones).
* *
* The exponential decaying function is used for the calculation (see https://en.wikipedia.org/wiki/Time_constant). That * The exponential decaying function is used for the calculation (see https://en.wikipedia.org/wiki/Time_constant). That

Some files were not shown because too many files have changed in this diff Show More