diff --git a/CODEOWNERS b/CODEOWNERS index 300354bcf8e56..884d35675c67c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -20,6 +20,7 @@ /bundles/org.openhab.binding.amazondashbutton/ @OLibutzki /bundles/org.openhab.binding.amazonechocontrol/ @mgeramb /bundles/org.openhab.binding.ambientweather/ @mhilbush +/bundles/org.openhab.binding.amplipi/ @kaikreuzer /bundles/org.openhab.binding.androiddebugbridge/ @GiviMAD /bundles/org.openhab.binding.astro/ @gerrieg /bundles/org.openhab.binding.atlona/ @tmrobert8 diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 4c2c4ac9928c3..0d5b98e0924c6 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -91,6 +91,11 @@ org.openhab.binding.ambientweather ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.amplipi + ${project.version} + org.openhab.addons.bundles org.openhab.binding.androiddebugbridge diff --git a/bundles/org.openhab.binding.amplipi/NOTICE b/bundles/org.openhab.binding.amplipi/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.amplipi/README.md b/bundles/org.openhab.binding.amplipi/README.md new file mode 100644 index 0000000000000..ec341b008bdc0 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/README.md @@ -0,0 +1,91 @@ +# AmpliPi Binding + +This binding supports the multi room audio system [AmpliPi](http://www.amplipi.com/) from [MicroNova](http://www.micro-nova.com/). + + +## Supported Things + +The AmpliPi itself is modeled as a Bridge of type `controller`. +Every available zone as well as group is managed as an individual Thing of type `zone` resp. `group`. + +## Discovery + +Once the AmpliPi announces itself through mDNS (still a pending feature), it will be automatically discovered on the network. + +As soon as the AmpliPi is online, its zones and groups are automatically retrieved and added as Things to the Inbox. + +## Thing Configuration + +The `controller` Bridge has two configuration parameters: + +| Parameter | Required | Description | +|-----------------|----------|----------------------------------------------------------------------------------------------------| +| hostname | yes | The hostname or IP address of the AmpliPi on the network | +| refreshInterval | no | The time to wait between two polling requests for receiving state updates. Defaults to 10 seconds. | + +Both the `zone` and `group` Things only require a single configuration parameter `id`, which corresponds to their id on the AmpliPi. + +## Channels + +These are the channels of the `controller` Bridge: + +| Channel | Type | Description | +|----------|--------|------------------------------------------------------------------------------------------------------| +| preset | Number | Allows setting a pre-configured preset. The available options are dynamically read from the AmpliPi. | +| input1 | String | The selected input of source 1 | +| input2 | String | The selected input of source 2 | +| input3 | String | The selected input of source 3 | +| input4 | String | The selected input of source 4 | + +The `zone` and `group` Things have the following channels: + +| Channel | Type | Description | +|----------|--------|----------------------------------------------------| +| volume | Dimmer | The volume of the zone/group | +| mute | Switch | Mutes the zone/group | +| source | Number | The source (1-4) that this zone/group is playing | + + +## Full Example + +amplipi.things: + +``` +Bridge amplipi:controller:1 "My AmpliPi" [ hostname="amplipi.local" ] { + zone zone2 "Living Room" [ id=1 ] +} +``` + +amplipi.items: + +``` +Number Preset "Preset" { channel="amplipi:controller:1:preset" } +String Input1 "Input 1" { channel="amplipi:controller:1:input1" } +String Input2 "Input 2" { channel="amplipi:controller:1:input2" } +String Input3 "Input 3" { channel="amplipi:controller:1:input3" } +String Input4 "Input 4" { channel="amplipi:controller:1:input4" } + +Dimmer VolumeZ2 "Volume Zone2" { channel="amplipi:zone:1:zone2:volume" } +Switch MuteZ2 "Mute Zone2" { channel="amplipi:zone:1:zone2::mute" } +Number SourceZ2 "Source Zone2" { channel="amplipi:zone:1:zone2::source" } +``` + +amplipi.sitemap: + +``` +sitemap amplipi label="Main Menu" +{ + Frame label="AmpliPi" { + Selection item=Preset + Selection item=Input1 + Selection item=Input2 + Selection item=Input3 + Selection item=Input4 + } + Frame label="Living Room Zone" { + Slider item=VolumeZ2 label="Volume Zone 1 [%.1f %%]" + Switch item=MuteZ2 + Selection item=SourceZ2 + } +} +``` diff --git a/bundles/org.openhab.binding.amplipi/amplipi-api.yml b/bundles/org.openhab.binding.amplipi/amplipi-api.yml new file mode 100644 index 0000000000000..fe1731dfac0ba --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/amplipi-api.yml @@ -0,0 +1,4439 @@ +openapi: 3.0.2 +info: + title: AmpliPi + description: | # The links in the description below are tested to work with redoc and may not be portable + This is the AmpliPi home audio system's control server. + + # Configuration + + This web interface allows you to control and configure your AmpliPi device. + At the moment the API is the only way to configure the AmpliPi. + + ## Try it out! + + __Using this web interface to test API commands:__ + + 1. Go to an API request + 1. Pick one of the examples + 2. Edit it + 3. Press try button, it will send an API command/request to the AmpliPi + + __Try using the get status:__ + + 1. Go to [Status -> Get Status](#get-/api/) + 2. Click the Try button, you will see a response below with the full status/config of the AmpliPi controller + + __Try creating a new group:__ + + 1. Go to [Group -> Create Group](#post-/api/group) + 2. Click Example + 3. Edit the zones and group name + 4. Click the try button, you will see a response with the newly created group + + __Here are some other things that you might want to change:__ + + - [Stream -> Create new stream](#post-/api/stream) + - [Zone -> Update Zone](#patch-/api/zones/-zid-) (to change the zone name) + - [Preset -> Create preset](#post-/api/preset) (Have a look at the model to see what can be added here) + + # More Info + + Check out all of the different things you can do with this API: + + - [Status](#tag--status) + - [Source](#tag--source) + - [Zone](#tag--zone) + - [Group](#tag--group) + - [Stream](#tag--stream) + - [Preset](#tag--preset) + + # OpenAPI + + This API is documented using the OpenAPI specification + + version: '1.0' + contact: + email: info@micro-nova.com + name: Micronova + url: http://micro-nova.com + license: + name: GPL + url: /license +servers: +- url: '' + description: AmpliPi Controller +paths: + /api: + get: + tags: + - status + summary: Get Status + description: 'Get the system status and configuration ' + operationId: get_status_api_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + /api/: + get: + tags: + - status + summary: Get Status + description: 'Get the system status and configuration ' + operationId: get_status_api__get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + /api/sources: + get: + tags: + - source + summary: Get Sources + description: 'Get all sources ' + operationId: get_sources_api_sources_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: Response Get Sources Api Sources Get + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Source' + /api/sources/{sid}: + get: + tags: + - source + summary: Get Source + description: 'Get Source with id=**sid** ' + operationId: get_source_api_sources__sid__get + parameters: + - description: Source ID + required: true + schema: + title: Sid + maximum: 3.0 + minimum: 0.0 + type: integer + description: Source ID + name: sid + in: path + examples: + '1': + value: 0 + summary: '1' + '2': + value: 1 + summary: '2' + '3': + value: 2 + summary: '3' + '4': + value: 3 + summary: '4' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Source' + examples: + stream connected: + value: + id: 1 + name: '1' + input: stream=1009 + nothing connected: + value: + id: 2 + name: '2' + input: '' + rca connected: + value: + id: 3 + name: '3' + input: local + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - source + summary: Set Source + description: 'Update a source''s configuration (source=**sid**) ' + operationId: set_source_api_sources__sid__patch + parameters: + - description: Source ID + required: true + schema: + title: Sid + maximum: 3.0 + minimum: 0.0 + type: integer + description: Source ID + name: sid + in: path + examples: + '1': + value: 0 + summary: '1' + '2': + value: 1 + summary: '2' + '3': + value: 2 + summary: '3' + '4': + value: 3 + summary: '4' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SourceUpdate' + examples: + Update Input to RCA input: + value: + input: local + Update name: + value: + name: J2 + Update Input to Matt and Kim Radio: + value: + input: stream=10001 + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/zones: + get: + tags: + - zone + summary: Get Zones + description: 'Get all zones ' + operationId: get_zones_api_zones_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: Response Get Zones Api Zones Get + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Zone' + /api/zones/{zid}: + get: + tags: + - zone + summary: Get Zone + description: 'Get Zone with id=**zid** ' + operationId: get_zone_api_zones__zid__get + parameters: + - description: Zone ID + required: true + schema: + title: Zid + maximum: 35.0 + minimum: 0.0 + type: integer + description: Zone ID + name: zid + in: path + examples: + Local: + value: 0 + summary: Local + Office: + value: 1 + summary: Office + Laundry Room: + value: 2 + summary: Laundry Room + Dining Room: + value: 3 + summary: Dining Room + BROKEN: + value: 4 + summary: BROKEN + Guest Bedroom: + value: 5 + summary: Guest Bedroom + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Zone' + examples: + Living Room: + value: + name: Living Room + source_id: 1 + mute: false + vol: -25 + disabled: false + Dining Room: + value: + name: Dining Room + source_id: 2 + mute: true + vol: -65 + disabled: false + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - zone + summary: Set Zone + description: 'Update a zone''s configuration (zone=**zid**) ' + operationId: set_zone_api_zones__zid__patch + parameters: + - description: Zone ID + required: true + schema: + title: Zid + maximum: 35.0 + minimum: 0.0 + type: integer + description: Zone ID + name: zid + in: path + examples: + Local: + value: 0 + summary: Local + Office: + value: 1 + summary: Office + Laundry Room: + value: 2 + summary: Laundry Room + Dining Room: + value: 3 + summary: Dining Room + BROKEN: + value: 4 + summary: BROKEN + Guest Bedroom: + value: 5 + summary: Guest Bedroom + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ZoneUpdate' + examples: + Change Name: + value: + name: Bedroom + Change audio source: + value: + source-id: 3 + Increase Volume: + value: + vol: -45 + Mute: + value: + mute: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/group: + post: + tags: + - group + summary: Create Group + description: 'Create a new grouping of zones ' + operationId: create_group_api_group_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + examples: + Upstairs Group: + value: + name: Upstairs + zones: + - 1 + - 2 + - 3 + - 4 + - 5 + Downstairs Group: + value: + name: Downstairs + zones: + - 6 + - 7 + - 8 + - 9 + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + examples: + Upstairs Group: + value: + id: 101 + name: Upstairs + zones: + - 1 + - 2 + - 3 + - 4 + - 5 + vol_delta: -65 + Downstairs Group: + value: + id: 102 + name: Downstairs + zones: + - 6 + - 7 + - 8 + - 9 + vol_delta: -30 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/groups: + get: + tags: + - group + summary: Get Groups + description: 'Get all groups ' + operationId: get_groups_api_groups_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: Response Get Groups Api Groups Get + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Group' + /api/groups/{gid}: + get: + tags: + - group + summary: Get Group + description: 'Get Group with id=**gid** ' + operationId: get_group_api_groups__gid__get + parameters: + - description: Stream ID + required: true + schema: + title: Gid + minimum: 0.0 + type: integer + description: Stream ID + name: gid + in: path + examples: + Whole House: + value: 0 + summary: Whole House + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + examples: + Upstairs Group: + value: + id: 101 + name: Upstairs + zones: + - 1 + - 2 + - 3 + - 4 + - 5 + vol_delta: -65 + Downstairs Group: + value: + id: 102 + name: Downstairs + zones: + - 6 + - 7 + - 8 + - 9 + vol_delta: -30 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - group + summary: Delete Group + description: 'Delete a group (group=**gid**) ' + operationId: delete_group_api_groups__gid__delete + parameters: + - description: Stream ID + required: true + schema: + title: Gid + minimum: 0.0 + type: integer + description: Stream ID + name: gid + in: path + examples: + Whole House: + value: 0 + summary: Whole House + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - group + summary: Set Group + description: 'Update a groups''s configuration (group=**gid**) ' + operationId: set_group_api_groups__gid__patch + parameters: + - description: Stream ID + required: true + schema: + title: Gid + minimum: 0.0 + type: integer + description: Stream ID + name: gid + in: path + examples: + Whole House: + value: 0 + summary: Whole House + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GroupUpdate' + examples: + Change Name: + value: + name: Upstairs + Change audio source: + value: + source-id: 3 + Increase Volume: + value: + vol_delta: -45 + Mute: + value: + mute: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/stream: + post: + tags: + - stream + summary: Create Stream + description: 'Create a new audio stream ' + operationId: create_stream_api_stream_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Stream' + examples: + Add Beatles Internet Radio Station: + value: + logo: http://www.beatlesradio.com/content/images/thumbs/0000587.gif + name: Beatles Radio + type: internetradio + url: http://www.beatlesradio.com:8000/stream/1/ + Add Classical KING Internet Radio Station: + value: + logo: https://i.iheart.com/v3/re/assets/images/7bcfd87a-de3e-47d0-b896-be0ed38c9d74.png + name: Classical KING FM 98.1 + type: internetradio + url: http://classicalking.streamguys1.com/king-fm-aac-iheart + Add Generic DLNA: + value: + name: Replace this text with a name you like! + type: dlna + Add Groove Salad Internet Radio Station: + value: + logo: https://somafm.com/img3/groovesalad-200.jpg + name: Groove Salad + type: internetradio + url: http://ice2.somafm.com/groovesalad-16-aac + Add KEXP Internet Radio Station: + value: + logo: https://i.iheart.com/v3/re/new_assets/cc4e0a17-5233-4e4b-9b6b-7799904f78ea + name: KEXP 90.3 + type: internetradio + url: http://live-aacplus-64.kexp.org/kexp64.aac + Add Matt and Kim Pandora Station: + value: + name: Matt and Kim Radio + password: s79sDDkjf + station: '4473713754798410236' + type: pandora + user: test@micro-nova.com + Add MicroNova Spotify: + value: + name: MicroNova Spotify + type: spotify + Add Micronova Airplay: + value: + name: Micronova AP + type: shairport + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Stream' + examples: + Regina Spektor Radio (playing): + value: + id: 90890 + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + Matt and Kim Radio (disconnected): + value: + id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + Shairport (connected): + value: + id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + Shairport (disconnected): + value: + id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/streams: + get: + tags: + - stream + summary: Get Streams + description: 'Get all streams ' + operationId: get_streams_api_streams_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: Response Get Streams Api Streams Get + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Stream' + /api/streams/{sid}: + get: + tags: + - stream + summary: Get Stream + description: 'Get Stream with id=**sid** ' + operationId: get_stream_api_streams__sid__get + parameters: + - description: Stream ID + required: true + schema: + title: Sid + minimum: 0.0 + type: integer + description: Stream ID + name: sid + in: path + examples: + Regina Spektor Radio: + value: 90890 + summary: Regina Spektor Radio + Matt and Kim Radio: + value: 90891 + summary: Matt and Kim Radio + Pink Radio: + value: 90892 + summary: Pink Radio + Jason's iPhone: + value: 44590 + summary: Jason's iPhone + Marc's iPhone: + value: 4893 + summary: Marc's iPhone + Rnay: + value: 4894 + summary: Rnay + Jeremy's Spotify: + value: 4895 + summary: Jeremy's Spotify + Lincoln's Spotify: + value: 4896 + summary: Lincoln's Spotify + Indie Pop Rocks: + value: 90893 + summary: Indie Pop Rocks + Groove Salad: + value: 90894 + summary: Groove Salad + SP_TEST: + value: 90895 + summary: SP_TEST + Trial_DLNA: + value: 90896 + summary: Trial_DLNA + T2: + value: 90897 + summary: T2 + T3: + value: 90898 + summary: T3 + T2.5: + value: 90899 + summary: T2.5 + Jeremy's DLNA: + value: 90900 + summary: Jeremy's DLNA + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Stream' + examples: + Regina Spektor Radio (playing): + value: + id: 90890 + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + Matt and Kim Radio (disconnected): + value: + id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + Shairport (connected): + value: + id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + Shairport (disconnected): + value: + id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - stream + summary: Delete Stream + description: 'Delete a stream ' + operationId: delete_stream_api_streams__sid__delete + parameters: + - description: Stream ID + required: true + schema: + title: Sid + minimum: 0.0 + type: integer + description: Stream ID + name: sid + in: path + examples: + Regina Spektor Radio: + value: 90890 + summary: Regina Spektor Radio + Matt and Kim Radio: + value: 90891 + summary: Matt and Kim Radio + Pink Radio: + value: 90892 + summary: Pink Radio + Jason's iPhone: + value: 44590 + summary: Jason's iPhone + Marc's iPhone: + value: 4893 + summary: Marc's iPhone + Rnay: + value: 4894 + summary: Rnay + Jeremy's Spotify: + value: 4895 + summary: Jeremy's Spotify + Lincoln's Spotify: + value: 4896 + summary: Lincoln's Spotify + Indie Pop Rocks: + value: 90893 + summary: Indie Pop Rocks + Groove Salad: + value: 90894 + summary: Groove Salad + SP_TEST: + value: 90895 + summary: SP_TEST + Trial_DLNA: + value: 90896 + summary: Trial_DLNA + T2: + value: 90897 + summary: T2 + T3: + value: 90898 + summary: T3 + T2.5: + value: 90899 + summary: T2.5 + Jeremy's DLNA: + value: 90900 + summary: Jeremy's DLNA + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - stream + summary: Set Stream + description: 'Update a stream''s configuration (stream=**sid**) ' + operationId: set_stream_api_streams__sid__patch + parameters: + - description: Stream ID + required: true + schema: + title: Sid + minimum: 0.0 + type: integer + description: Stream ID + name: sid + in: path + examples: + Regina Spektor Radio: + value: 90890 + summary: Regina Spektor Radio + Matt and Kim Radio: + value: 90891 + summary: Matt and Kim Radio + Pink Radio: + value: 90892 + summary: Pink Radio + Jason's iPhone: + value: 44590 + summary: Jason's iPhone + Marc's iPhone: + value: 4893 + summary: Marc's iPhone + Rnay: + value: 4894 + summary: Rnay + Jeremy's Spotify: + value: 4895 + summary: Jeremy's Spotify + Lincoln's Spotify: + value: 4896 + summary: Lincoln's Spotify + Indie Pop Rocks: + value: 90893 + summary: Indie Pop Rocks + Groove Salad: + value: 90894 + summary: Groove Salad + SP_TEST: + value: 90895 + summary: SP_TEST + Trial_DLNA: + value: 90896 + summary: Trial_DLNA + T2: + value: 90897 + summary: T2 + T3: + value: 90898 + summary: T3 + T2.5: + value: 90899 + summary: T2.5 + Jeremy's DLNA: + value: 90900 + summary: Jeremy's DLNA + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StreamUpdate' + examples: + Change account info: + value: + password: sd9sk3k30 + user: test@micro-nova.com + Change name: + value: + name: Matt and Kim Radio + Change pandora radio station: + value: + station: 0982034049300 + Upgrade groove salad stream quality: + value: + url: http://ice2.somafm.com/groovesalad-64-aac + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/streams/{sid}/station={station}: + post: + tags: + - stream + summary: Change Station + description: 'Change station on a pandora stream (stream=**sid**) ' + operationId: change_station_api_streams__sid__station__station__post + parameters: + - description: Stream ID + required: true + schema: + title: Sid + minimum: 0.0 + type: integer + description: Stream ID + name: sid + in: path + - description: Number found on the end of a pandora url while playing the station, + ie 4610303469018478727 in https://www.pandora.com/station/play/4610303469018478727 + required: true + schema: + title: Pandora Station ID + minimum: 0.0 + type: integer + description: Number found on the end of a pandora url while playing the + station, ie 4610303469018478727 in https://www.pandora.com/station/play/4610303469018478727 + name: station + in: path + examples: + Regina Spektor Radio: + value: 90890 + summary: Regina Spektor Radio + Matt and Kim Radio: + value: 90891 + summary: Matt and Kim Radio + Pink Radio: + value: 90892 + summary: Pink Radio + Jason's iPhone: + value: 44590 + summary: Jason's iPhone + Marc's iPhone: + value: 4893 + summary: Marc's iPhone + Rnay: + value: 4894 + summary: Rnay + Jeremy's Spotify: + value: 4895 + summary: Jeremy's Spotify + Lincoln's Spotify: + value: 4896 + summary: Lincoln's Spotify + Indie Pop Rocks: + value: 90893 + summary: Indie Pop Rocks + Groove Salad: + value: 90894 + summary: Groove Salad + SP_TEST: + value: 90895 + summary: SP_TEST + Trial_DLNA: + value: 90896 + summary: Trial_DLNA + T2: + value: 90897 + summary: T2 + T3: + value: 90898 + summary: T3 + T2.5: + value: 90899 + summary: T2.5 + Jeremy's DLNA: + value: 90900 + summary: Jeremy's DLNA + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/streams/{sid}/{cmd}: + post: + tags: + - stream + summary: Exec Command + description: "Execute a comamnds on a stream (stream=**sid**).\n\n Command\ + \ options:\n * Play Stream: **play**\n * Pause Stream: **pause**\n * Skip\ + \ to next song: **next**\n * Stop Stream: **stop**\n * Like/Love Current\ + \ Song: **love**\n * Ban Current Song (pandora only): **ban**\n * Shelve\ + \ Current Song (pandora only): **shelve**\n\nCurrently only available with\ + \ Pandora streams" + operationId: exec_command_api_streams__sid___cmd__post + parameters: + - description: Stream ID + required: true + schema: + title: Sid + minimum: 0.0 + type: integer + description: Stream ID + name: sid + in: path + - required: true + schema: + $ref: '#/components/schemas/StreamCommand' + name: cmd + in: path + examples: + Regina Spektor Radio: + value: 90890 + summary: Regina Spektor Radio + Matt and Kim Radio: + value: 90891 + summary: Matt and Kim Radio + Pink Radio: + value: 90892 + summary: Pink Radio + Jason's iPhone: + value: 44590 + summary: Jason's iPhone + Marc's iPhone: + value: 4893 + summary: Marc's iPhone + Rnay: + value: 4894 + summary: Rnay + Jeremy's Spotify: + value: 4895 + summary: Jeremy's Spotify + Lincoln's Spotify: + value: 4896 + summary: Lincoln's Spotify + Indie Pop Rocks: + value: 90893 + summary: Indie Pop Rocks + Groove Salad: + value: 90894 + summary: Groove Salad + SP_TEST: + value: 90895 + summary: SP_TEST + Trial_DLNA: + value: 90896 + summary: Trial_DLNA + T2: + value: 90897 + summary: T2 + T3: + value: 90898 + summary: T3 + T2.5: + value: 90899 + summary: T2.5 + Jeremy's DLNA: + value: 90900 + summary: Jeremy's DLNA + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/preset: + post: + tags: + - preset + summary: Create Preset + description: 'Create a new preset configuration ' + operationId: create_preset_api_preset_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Preset' + examples: + Add Mute All: + value: + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Preset' + examples: + Mute All: + value: + id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/presets: + get: + tags: + - preset + summary: Get Presets + description: 'Get all presets ' + operationId: get_presets_api_presets_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: Response Get Presets Api Presets Get + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Preset' + /api/presets/{pid}: + get: + tags: + - preset + summary: Get Preset + description: 'Get Preset with id=**pid** ' + operationId: get_preset_api_presets__pid__get + parameters: + - description: Preset ID + required: true + schema: + title: Pid + minimum: 0.0 + type: integer + description: Preset ID + name: pid + in: path + examples: + Mute All: + value: 10000 + summary: Mute All + Restore last config: + value: 9999 + summary: Restore last config + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Preset' + examples: + Mute All: + value: + id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - preset + summary: Delete Preset + description: 'Delete a preset ' + operationId: delete_preset_api_presets__pid__delete + parameters: + - description: Preset ID + required: true + schema: + title: Pid + minimum: 0.0 + type: integer + description: Preset ID + name: pid + in: path + examples: + Mute All: + value: 10000 + summary: Mute All + Restore last config: + value: 9999 + summary: Restore last config + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - preset + summary: Set Preset + description: 'Update a preset''s configuration (preset=**pid**) ' + operationId: set_preset_api_presets__pid__patch + parameters: + - description: Preset ID + required: true + schema: + title: Pid + minimum: 0.0 + type: integer + description: Preset ID + name: pid + in: path + examples: + Mute All: + value: 10000 + summary: Mute All + Restore last config: + value: 9999 + summary: Restore last config + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PresetUpdate' + examples: + Only mute some: + value: + name: Mute Some + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 5 + mute: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/presets/{pid}/load: + post: + tags: + - preset + summary: Load Preset + description: 'Load a preset configuration ' + operationId: load_preset_api_presets__pid__load_post + parameters: + - description: Preset ID + required: true + schema: + title: Pid + minimum: 0.0 + type: integer + description: Preset ID + name: pid + in: path + examples: + Mute All: + value: 10000 + summary: Mute All + Restore last config: + value: 9999 + summary: Restore last config + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + examples: + Status of Jason's AmpliPi: + value: + groups: + - id: 0 + mute: false + name: Whole House + source_id: null + vol_delta: -44 + zones: + - 0 + - 1 + - 2 + - 3 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - id: 1 + mute: true + name: KitchLivDining + source_id: 0 + vol_delta: -49 + zones: + - 3 + - 9 + - 10 + - 11 + presets: + - id: 10000 + name: Mute All + state: + zones: + - id: 0 + mute: true + - id: 1 + mute: true + - id: 2 + mute: true + - id: 3 + mute: true + - id: 4 + mute: true + - id: 5 + mute: true + sources: + - id: 0 + input: stream=90890 + name: J1 + - id: 1 + input: stream=44590 + name: J2 + - id: 2 + input: local + name: Marc + - id: 3 + input: local + name: Source 4 + streams: + - id: 90890 + info: + album: Far (Deluxe Version) + artist: Regina Spektor + img_url: http://mediaserver-cont-dc6-1-v4v6.pandora.com/images/public/int/2/1/5/4/093624974512_500W_500H.jpg + station: Regina Spektor Radio + track: Eet + name: Regina Spektor Radio + password: '' + station: '4473713754798410236' + status: playing + type: pandora + user: example1@micro-nova.com + - id: 90891 + info: + details: No info available + name: Matt and Kim Radio + password: '' + station: '4610303469018478727' + status: disconnected + type: pandora + user: example2@micro-nova.com + - id: 90892 + info: + details: No info available + name: Pink Radio + password: '' + station: '4326539910057675260' + status: disconnected + type: pandora + user: example3@micro-nova.com + - id: 44590 + info: + details: No info available + name: Jason's iPhone + status: connected + type: shairport + - id: 4894 + info: + details: No info available + name: Rnay + status: disconnected + type: shairport + info: + version: 0.0.1 + zones: + - disabled: false + id: 0 + mute: false + name: Local + source_id: 1 + vol: -35 + - disabled: false + id: 1 + mute: false + name: Office + source_id: 0 + vol: -41 + - disabled: false + id: 2 + mute: true + name: Laundry Room + source_id: 0 + vol: -48 + - disabled: false + id: 3 + mute: true + name: Dining Room + source_id: 0 + vol: -44 + - disabled: true + id: 4 + mute: true + name: BROKEN + source_id: 0 + vol: -50 + - disabled: false + id: 5 + mute: true + name: Guest Bedroom + source_id: 0 + vol: -48 + - disabled: false + id: 6 + mute: true + name: Main Bedroom + source_id: 0 + vol: -40 + - disabled: false + id: 7 + mute: true + name: Main Bathroom + source_id: 0 + vol: -44 + - disabled: false + id: 8 + mute: true + name: Master Bathroom + source_id: 0 + vol: -41 + - disabled: false + id: 9 + mute: true + name: Kitchen High + source_id: 0 + vol: -53 + - disabled: false + id: 10 + mute: true + name: kitchen Low + source_id: 0 + vol: -52 + - disabled: false + id: 11 + mute: true + name: Living Room + source_id: 0 + vol: -46 + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' +components: + schemas: + Command: + title: Command + required: + - stream_id + - cmd + type: object + properties: + stream_id: + title: Stream Id + type: integer + description: Stream to execute the command on + cmd: + title: Cmd + type: string + description: Command to execute + description: 'A command to execute on a stream ' + Group: + title: Group + required: + - name + - zones + type: object + properties: + id: + title: Id + type: integer + description: Unique identifier + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + zones: + title: Zones + uniqueItems: true + type: array + items: + type: integer + description: Set of zones belonging to a group + mute: + title: Mute + type: boolean + description: Set to true if output is all zones muted + default: true + vol_delta: + title: Vol Delta + maximum: 0.0 + minimum: -79.0 + type: integer + description: Average utput volume in dB + default: -79 + description: 'A group of zones that can share the same audio input and be controlled + as a group ie. Updstairs. + + + Volume, mute, and source_id fields are aggregates of the member zones.' + GroupUpdate: + title: GroupUpdate + type: object + properties: + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + zones: + title: Zones + type: array + items: + type: integer + description: Set of zones belonging to a group + mute: + title: Mute + type: boolean + description: Set to true if output is all zones muted + default: true + vol_delta: + title: Vol Delta + maximum: 0.0 + minimum: -79.0 + type: integer + description: Average utput volume in dB + default: -79 + description: 'Reconfiguration of a Group ' + GroupUpdate2: + title: GroupUpdate2 + required: + - id + type: object + properties: + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + zones: + title: Zones + type: array + items: + type: integer + description: Set of zones belonging to a group + mute: + title: Mute + type: boolean + description: Set to true if output is all zones muted + default: true + vol_delta: + title: Vol Delta + maximum: 0.0 + minimum: -79.0 + type: integer + description: Average utput volume in dB + default: -79 + id: + title: Id + type: integer + description: 'Reconfiguration of a specific Group ' + HTTPValidationError: + title: HTTPValidationError + type: object + properties: + detail: + title: Detail + type: array + items: + $ref: '#/components/schemas/ValidationError' + Info: + title: Info + type: object + properties: + config_file: + title: Config File + type: string + default: Uknown + version: + title: Version + type: string + default: Unknown + mock_ctrl: + title: Mock Ctrl + type: boolean + default: false + mock_streams: + title: Mock Streams + type: boolean + default: false + description: 'Information about the settings used by the controller ' + Preset: + title: Preset + required: + - name + - state + type: object + properties: + id: + title: Id + type: integer + description: Unique identifier + name: + title: Name + type: string + description: Friendly name + state: + $ref: '#/components/schemas/PresetState' + commands: + title: Commands + type: array + items: + $ref: '#/components/schemas/Command' + default: [] + last_used: + title: Last Used + type: integer + description: 'A partial controller configuration the can be loaded on demand. + + In addition to most of the configuration found in Status, this can contain + commands as well that configure the state of different streaming services.' + PresetState: + title: PresetState + type: object + properties: + sources: + title: Sources + type: array + items: + $ref: '#/components/schemas/SourceUpdate2' + zones: + title: Zones + type: array + items: + $ref: '#/components/schemas/ZoneUpdate2' + groups: + title: Groups + type: array + items: + $ref: '#/components/schemas/GroupUpdate2' + description: 'A set of partial configuration changes to make to sources, zones, + and groups ' + PresetUpdate: + title: PresetUpdate + type: object + properties: + name: + title: Name + type: string + description: Friendly name + state: + $ref: '#/components/schemas/PresetState' + commands: + title: Commands + type: array + items: + $ref: '#/components/schemas/Command' + description: 'Changes to a current preset + + + The contents of state and commands will be completely replaced if populated. + + Merging old and new updates seems too complicated and error prone.' + Source: + title: Source + required: + - name + type: object + properties: + id: + title: Id + type: integer + description: Unique identifier + name: + title: Name + type: string + description: Friendly name + input: + title: Input + type: string + description: "Connected audio source\n\n * Digital Stream ('stream=SID')\ + \ where SID is the ID of the connected stream\n * Analog RCA Input ('local')\ + \ connects to the RCA inputs associated\n * Nothing ('') behind the scenes\ + \ this is muxed to a digital output\n " + default: '' + description: 'An audio source ' + SourceUpdate: + title: SourceUpdate + type: object + properties: + name: + title: Name + type: string + description: Friendly name + input: + title: Input + type: string + description: 'Partial reconfiguration of an audio Source ' + SourceUpdate2: + title: SourceUpdate2 + required: + - id + type: object + properties: + name: + title: Name + type: string + description: Friendly name + input: + title: Input + type: string + id: + title: Id + maximum: 4.0 + minimum: 0.0 + type: integer + description: 'Partial reconfiguration of a specific audio Source ' + Status: + title: Status + type: object + properties: + sources: + title: Sources + type: array + items: + $ref: '#/components/schemas/Source' + default: + - id: 0 + name: '0' + input: '' + - id: 1 + name: '1' + input: '' + - id: 2 + name: '2' + input: '' + - id: 3 + name: '3' + input: '' + zones: + title: Zones + type: array + items: + $ref: '#/components/schemas/Zone' + default: + - id: 0 + name: Zone 0 + source_id: 0 + mute: true + vol: -79 + disabled: false + - id: 1 + name: Zone 1 + source_id: 0 + mute: true + vol: -79 + disabled: false + - id: 2 + name: Zone 2 + source_id: 0 + mute: true + vol: -79 + disabled: false + - id: 3 + name: Zone 3 + source_id: 0 + mute: true + vol: -79 + disabled: false + - id: 4 + name: Zone 4 + source_id: 0 + mute: true + vol: -79 + disabled: false + - id: 5 + name: Zone 5 + source_id: 0 + mute: true + vol: -79 + disabled: false + groups: + title: Groups + type: array + items: + $ref: '#/components/schemas/Group' + default: [] + streams: + title: Streams + type: array + items: + $ref: '#/components/schemas/Stream' + default: [] + presets: + title: Presets + type: array + items: + $ref: '#/components/schemas/Preset' + default: [] + info: + $ref: '#/components/schemas/Info' + description: 'Full Controller Configuration and Status ' + Stream: + title: Stream + required: + - name + - type + type: object + properties: + id: + title: Id + type: integer + description: Unique identifier + name: + title: Name + type: string + description: Friendly name + type: + title: Type + type: string + description: "stream type\n\n * pandora\n * shairport\n * dlna\n * internetradio\n\ + \ * spotify\n " + user: + title: User + type: string + description: User login + password: + title: Password + type: string + description: Password + station: + title: Station + type: string + description: Radio station identifier + url: + title: Url + type: string + description: Stream url, used for internetradio + logo: + title: Logo + type: string + description: Icon/Logo url, used for internetradio + info: + title: Info + type: object + description: Additional info about the current audio playing from the stream + (generated during playback + status: + title: Status + type: string + description: State of the stream + description: 'Digital stream such as Pandora, Airplay or Spotify ' + StreamCommand: + title: StreamCommand + enum: + - play + - pause + - next + - stop + - like + - ban + - shelve + type: string + description: An enumeration. + StreamUpdate: + title: StreamUpdate + type: object + properties: + name: + title: Name + type: string + description: Friendly name + user: + title: User + type: string + password: + title: Password + type: string + station: + title: Station + type: string + url: + title: Url + type: string + logo: + title: Logo + type: string + description: 'Reconfiguration of a Stream ' + ValidationError: + title: ValidationError + required: + - loc + - msg + - type + type: object + properties: + loc: + title: Location + type: array + items: + type: string + msg: + title: Message + type: string + type: + title: Error Type + type: string + Zone: + title: Zone + required: + - name + type: object + properties: + id: + title: Id + type: integer + description: Unique identifier + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + mute: + title: Mute + type: boolean + description: Set to true if output is muted + default: true + vol: + title: Vol + maximum: 0.0 + minimum: -79.0 + type: integer + description: Output volume in dB + default: -79 + disabled: + title: Disabled + type: boolean + description: Set to true if not connected to a speaker + default: false + description: 'Audio output to a stereo pair of speakers, typically belonging + to a room ' + ZoneUpdate: + title: ZoneUpdate + type: object + properties: + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + mute: + title: Mute + type: boolean + description: Set to true if output is muted + default: true + vol: + title: Vol + maximum: 0.0 + minimum: -79.0 + type: integer + description: Output volume in dB + default: -79 + disabled: + title: Disabled + type: boolean + description: Set to true if not connected to a speaker + default: false + description: 'Reconfiguration of a Zone ' + ZoneUpdate2: + title: ZoneUpdate2 + required: + - id + type: object + properties: + name: + title: Name + type: string + description: Friendly name + source_id: + title: Source Id + maximum: 3.0 + minimum: 0.0 + type: integer + description: id of the connected source + default: 0 + mute: + title: Mute + type: boolean + description: Set to true if output is muted + default: true + vol: + title: Vol + maximum: 0.0 + minimum: -79.0 + type: integer + description: Output volume in dB + default: -79 + disabled: + title: Disabled + type: boolean + description: Set to true if not connected to a speaker + default: false + id: + title: Id + maximum: 35.0 + minimum: 0.0 + type: integer + description: 'Reconfiguration of a specific Zone ' +tags: +- name: status + description: The status and configuration of the entire system, including source, + zones, groups, and streams. +- name: source + description: Audio source. Can accept sudio input from a local (RCA) connection + or any stream. Sources can be connected to one or multiple zones, or connected + to nothing at all. +- name: zone + description: Stereo output to a set of speakers, typically a room. Individually + controllable with its own volume control. Can be connected to one of the 4 audio + sources. +- name: group + description: Group of zones. Grouping allows a set of zones to be controlled together. + A zone can belong to multiple groups, allowing for different levels of abstraction, + ie. Guest Bedroom can belong to both the 'Upstairs' and 'Whole House' groups., +- name: stream + description: Digital stream that can be connected to a source, ie. Pandora, Airplay, + Spotify, Internet Radio, DLNA. +- name: preset + description: A partial system configuration. Used to load specific configurations, + such as "Home Theater" mode where the living room speakers are connected to the + TV's audio output. diff --git a/bundles/org.openhab.binding.amplipi/pom.xml b/bundles/org.openhab.binding.amplipi/pom.xml new file mode 100644 index 0000000000000..55ec0c7caa2ee --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.2.0-SNAPSHOT + + + org.openhab.binding.amplipi + + openHAB Add-ons :: Bundles :: AmpliPi Binding + + + 3.4.3 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + org.openapitools + openapi-generator-maven-plugin + 5.1.0 + + + + generate + + + ${project.basedir}/amplipi-api.yml + jaxrs-cxf-client + ${project.basedir} + org.openhab.binding.amplipi.internal.model + true + false + false + false + false + false + true + + + + + + + + diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Command.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Command.java new file mode 100644 index 0000000000000..1dd1464f48b1c --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Command.java @@ -0,0 +1,95 @@ +/** + * 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.binding.amplipi.internal.model; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * A command to execute on a stream + **/ +@Schema(description = "A command to execute on a stream ") +public class Command { + + @Schema(required = true) + @SerializedName("stream_id") + /** + * Stream to execute the command on + **/ + private Integer streamId; + + @Schema(required = true) + /** + * Command to execute + **/ + private String cmd; + + /** + * Stream to execute the command on + * + * @return streamId + **/ + public Integer getStreamId() { + return streamId; + } + + public void setStreamId(Integer streamId) { + this.streamId = streamId; + } + + public Command streamId(Integer streamId) { + this.streamId = streamId; + return this; + } + + /** + * Command to execute + * + * @return cmd + **/ + public String getCmd() { + return cmd; + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + + public Command cmd(String cmd) { + this.cmd = cmd; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Command {\n"); + + sb.append(" streamId: ").append(toIndentedString(streamId)).append("\n"); + sb.append(" cmd: ").append(toIndentedString(cmd)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Group.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Group.java new file mode 100644 index 0000000000000..7d2b4bd364837 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Group.java @@ -0,0 +1,209 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.LinkedHashSet; +import java.util.Set; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * A group of zones that can share the same audio input and be controlled as a group ie. Updstairs. Volume, mute, and + * source_id fields are aggregates of the member zones. + **/ +@Schema(description = "A group of zones that can share the same audio input and be controlled as a group ie. Updstairs. Volume, mute, and source_id fields are aggregates of the member zones.") +public class Group { + + @Schema + /** + * Unique identifier + **/ + private Integer id; + + @Schema(required = true) + /** + * Friendly name + **/ + private String name; + + @Schema + @SerializedName("source_id") + /** + * id of the connected source + **/ + private Integer sourceId = 0; + + @Schema(required = true) + /** + * Set of zones belonging to a group + **/ + private Set zones = new LinkedHashSet(); + + @Schema + /** + * Set to true if output is all zones muted + **/ + private Boolean mute = true; + + @Schema + @SerializedName("vol_delta") + /** + * Average utput volume in dB + **/ + private Integer volDelta = -79; + + /** + * Unique identifier + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Group id(Integer id) { + this.id = id; + return this; + } + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Group name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public Group sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set of zones belonging to a group + * + * @return zones + **/ + public Set getZones() { + return zones; + } + + public void setZones(Set zones) { + this.zones = zones; + } + + public Group zones(Set zones) { + this.zones = zones; + return this; + } + + public Group addZonesItem(Integer zonesItem) { + this.zones.add(zonesItem); + return this; + } + + /** + * Set to true if output is all zones muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public Group mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Average utput volume in dB + * minimum: -79 + * maximum: 0 + * + * @return volDelta + **/ + public Integer getVolDelta() { + return volDelta; + } + + public void setVolDelta(Integer volDelta) { + this.volDelta = volDelta; + } + + public Group volDelta(Integer volDelta) { + this.volDelta = volDelta; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Group {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" volDelta: ").append(toIndentedString(volDelta)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate.java new file mode 100644 index 0000000000000..ceaee3148d00d --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate.java @@ -0,0 +1,182 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Reconfiguration of a Group + **/ +@Schema(description = "Reconfiguration of a Group ") +public class GroupUpdate { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + @SerializedName("source_id") + /** + * id of the connected source + **/ + private Integer sourceId; + + @Schema + /** + * Set of zones belonging to a group + **/ + private List zones; + + @Schema + /** + * Set to true if output is all zones muted + **/ + private Boolean mute; + + @Schema + @SerializedName("vol_delta") + /** + * Average utput volume in dB + **/ + private Integer volDelta; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GroupUpdate name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public GroupUpdate sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set of zones belonging to a group + * + * @return zones + **/ + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } + + public GroupUpdate zones(List zones) { + this.zones = zones; + return this; + } + + public GroupUpdate addZonesItem(Integer zonesItem) { + this.zones.add(zonesItem); + return this; + } + + /** + * Set to true if output is all zones muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public GroupUpdate mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Average utput volume in dB + * minimum: -79 + * maximum: 0 + * + * @return volDelta + **/ + public Integer getVolDelta() { + return volDelta; + } + + public void setVolDelta(Integer volDelta) { + this.volDelta = volDelta; + } + + public GroupUpdate volDelta(Integer volDelta) { + this.volDelta = volDelta; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class GroupUpdate {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" volDelta: ").append(toIndentedString(volDelta)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate2.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate2.java new file mode 100644 index 0000000000000..cfb099fa6ea34 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/GroupUpdate2.java @@ -0,0 +1,204 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Reconfiguration of a specific Group + **/ +@Schema(description = "Reconfiguration of a specific Group ") +public class GroupUpdate2 { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + /** + * id of the connected source + **/ + @SerializedName("source_id") + private Integer sourceId = 0; + + @Schema + /** + * Set of zones belonging to a group + **/ + private List zones = null; + + @Schema + /** + * Set to true if output is all zones muted + **/ + private Boolean mute = true; + + @Schema + @SerializedName("vol_delta") + /** + * Average output volume in dB + **/ + private Integer volDelta = -79; + + @Schema(required = true) + private Integer id; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GroupUpdate2 name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public GroupUpdate2 sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set of zones belonging to a group + * + * @return zones + **/ + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } + + public GroupUpdate2 zones(List zones) { + this.zones = zones; + return this; + } + + public GroupUpdate2 addZonesItem(Integer zonesItem) { + this.zones.add(zonesItem); + return this; + } + + /** + * Set to true if output is all zones muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public GroupUpdate2 mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Average output volume in dB + * minimum: -79 + * maximum: 0 + * + * @return volDelta + **/ + public Integer getVolDelta() { + return volDelta; + } + + public void setVolDelta(Integer volDelta) { + this.volDelta = volDelta; + } + + public GroupUpdate2 volDelta(Integer volDelta) { + this.volDelta = volDelta; + return this; + } + + /** + * Get id + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public GroupUpdate2 id(Integer id) { + this.id = id; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class GroupUpdate2 {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" volDelta: ").append(toIndentedString(volDelta)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/HTTPValidationError.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/HTTPValidationError.java new file mode 100644 index 0000000000000..af500c9d0ae35 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/HTTPValidationError.java @@ -0,0 +1,67 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +public class HTTPValidationError { + + @Schema + private List detail = null; + + /** + * Get detail + * + * @return detail + **/ + public List getDetail() { + return detail; + } + + public void setDetail(List detail) { + this.detail = detail; + } + + public HTTPValidationError detail(List detail) { + this.detail = detail; + return this; + } + + public HTTPValidationError addDetailItem(ValidationError detailItem) { + this.detail.add(detailItem); + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class HTTPValidationError {\n"); + + sb.append(" detail: ").append(toIndentedString(detail)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Info.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Info.java new file mode 100644 index 0000000000000..33da56fb9825b --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Info.java @@ -0,0 +1,135 @@ +/** + * 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.binding.amplipi.internal.model; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Information about the settings used by the controller + **/ +@Schema(description = "Information about the settings used by the controller ") +public class Info { + + @Schema + @SerializedName("config_file") + private String configFile = "Unknown"; + + @Schema + private String version = "Unknown"; + + @Schema + @SerializedName("mock_ctrl") + private Boolean mockCtrl = false; + + @Schema + @SerializedName("mock_streams") + private Boolean mockStreams = false; + + /** + * Get configFile + * + * @return configFile + **/ + public String getConfigFile() { + return configFile; + } + + public void setConfigFile(String configFile) { + this.configFile = configFile; + } + + public Info configFile(String configFile) { + this.configFile = configFile; + return this; + } + + /** + * Get version + * + * @return version + **/ + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Info version(String version) { + this.version = version; + return this; + } + + /** + * Get mockCtrl + * + * @return mockCtrl + **/ + public Boolean getMockCtrl() { + return mockCtrl; + } + + public void setMockCtrl(Boolean mockCtrl) { + this.mockCtrl = mockCtrl; + } + + public Info mockCtrl(Boolean mockCtrl) { + this.mockCtrl = mockCtrl; + return this; + } + + /** + * Get mockStreams + * + * @return mockStreams + **/ + public Boolean getMockStreams() { + return mockStreams; + } + + public void setMockStreams(Boolean mockStreams) { + this.mockStreams = mockStreams; + } + + public Info mockStreams(Boolean mockStreams) { + this.mockStreams = mockStreams; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Info {\n"); + + sb.append(" configFile: ").append(toIndentedString(configFile)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" mockCtrl: ").append(toIndentedString(mockCtrl)).append("\n"); + sb.append(" mockStreams: ").append(toIndentedString(mockStreams)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Preset.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Preset.java new file mode 100644 index 0000000000000..cd124ba632e40 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Preset.java @@ -0,0 +1,169 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * A partial controller configuration the can be loaded on demand. In addition to most of the configuration found in + * Status, this can contain commands as well that configure the state of different streaming services. + **/ +@Schema(description = "A partial controller configuration the can be loaded on demand. In addition to most of the configuration found in Status, this can contain commands as well that configure the state of different streaming services.") +public class Preset { + + @Schema + /** + * Unique identifier + **/ + private Integer id; + + @Schema(required = true) + /** + * Friendly name + **/ + private String name; + + @Schema(required = true) + private PresetState state; + + @Schema + private List commands = null; + + @Schema + @SerializedName("last_used") + private Integer lastUsed; + + /** + * Unique identifier + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Preset id(Integer id) { + this.id = id; + return this; + } + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Preset name(String name) { + this.name = name; + return this; + } + + /** + * Get state + * + * @return state + **/ + public PresetState getState() { + return state; + } + + public void setState(PresetState state) { + this.state = state; + } + + public Preset state(PresetState state) { + this.state = state; + return this; + } + + /** + * Get commands + * + * @return commands + **/ + public List getCommands() { + return commands; + } + + public void setCommands(List commands) { + this.commands = commands; + } + + public Preset commands(List commands) { + this.commands = commands; + return this; + } + + public Preset addCommandsItem(Command commandsItem) { + this.commands.add(commandsItem); + return this; + } + + /** + * Get lastUsed + * + * @return lastUsed + **/ + public Integer getLastUsed() { + return lastUsed; + } + + public void setLastUsed(Integer lastUsed) { + this.lastUsed = lastUsed; + } + + public Preset lastUsed(Integer lastUsed) { + this.lastUsed = lastUsed; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Preset {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" commands: ").append(toIndentedString(commands)).append("\n"); + sb.append(" lastUsed: ").append(toIndentedString(lastUsed)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetState.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetState.java new file mode 100644 index 0000000000000..3c95f3f926d76 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetState.java @@ -0,0 +1,125 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * A set of partial configuration changes to make to sources, zones, and groups + **/ +@Schema(description = "A set of partial configuration changes to make to sources, zones, and groups ") +public class PresetState { + + @Schema + private List sources = null; + + @Schema + private List zones = null; + + @Schema + private List groups = null; + + /** + * Get sources + * + * @return sources + **/ + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } + + public PresetState sources(List sources) { + this.sources = sources; + return this; + } + + public PresetState addSourcesItem(SourceUpdate2 sourcesItem) { + this.sources.add(sourcesItem); + return this; + } + + /** + * Get zones + * + * @return zones + **/ + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } + + public PresetState zones(List zones) { + this.zones = zones; + return this; + } + + public PresetState addZonesItem(ZoneUpdate2 zonesItem) { + this.zones.add(zonesItem); + return this; + } + + /** + * Get groups + * + * @return groups + **/ + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public PresetState groups(List groups) { + this.groups = groups; + return this; + } + + public PresetState addGroupsItem(GroupUpdate2 groupsItem) { + this.groups.add(groupsItem); + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PresetState {\n"); + + sb.append(" sources: ").append(toIndentedString(sources)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" groups: ").append(toIndentedString(groups)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetUpdate.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetUpdate.java new file mode 100644 index 0000000000000..f83270c2d6560 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/PresetUpdate.java @@ -0,0 +1,119 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Changes to a current preset The contents of state and commands will be completely replaced if populated. Merging old + * and new updates seems too complicated and error prone. + **/ +@Schema(description = "Changes to a current preset The contents of state and commands will be completely replaced if populated. Merging old and new updates seems too complicated and error prone.") +public class PresetUpdate { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + private PresetState state; + + @Schema + private List commands = null; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PresetUpdate name(String name) { + this.name = name; + return this; + } + + /** + * Get state + * + * @return state + **/ + public PresetState getState() { + return state; + } + + public void setState(PresetState state) { + this.state = state; + } + + public PresetUpdate state(PresetState state) { + this.state = state; + return this; + } + + /** + * Get commands + * + * @return commands + **/ + public List getCommands() { + return commands; + } + + public void setCommands(List commands) { + this.commands = commands; + } + + public PresetUpdate commands(List commands) { + this.commands = commands; + return this; + } + + public PresetUpdate addCommandsItem(Command commandsItem) { + this.commands.add(commandsItem); + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PresetUpdate {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" commands: ").append(toIndentedString(commands)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Source.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Source.java new file mode 100644 index 0000000000000..6b009c734c002 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Source.java @@ -0,0 +1,121 @@ +/** + * 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.binding.amplipi.internal.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * An audio source + **/ +@Schema(description = "An audio source ") +public class Source { + + @Schema + /** + * Unique identifier + **/ + private Integer id; + + @Schema(required = true) + /** + * Friendly name + **/ + private String name; + + @Schema + /** + * Connected audio source * Digital Stream ('stream=SID') where SID is the ID of the connected stream * Analog RCA + * Input ('local') connects to the RCA inputs associated * Nothing ('') behind the scenes this is muxed to a digital + * output + **/ + private String input = ""; + + /** + * Unique identifier + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Source id(Integer id) { + this.id = id; + return this; + } + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Source name(String name) { + this.name = name; + return this; + } + + /** + * Connected audio source * Digital Stream ('stream=SID') where SID is the ID of the connected stream * + * Analog RCA Input ('local') connects to the RCA inputs associated * Nothing ('') behind the scenes + * this is muxed to a digital output + * + * @return input + **/ + public String getInput() { + return input; + } + + public void setInput(String input) { + this.input = input; + } + + public Source input(String input) { + this.input = input; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Source {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" input: ").append(toIndentedString(input)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate.java new file mode 100644 index 0000000000000..06857cd97acb2 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate.java @@ -0,0 +1,89 @@ +/** + * 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.binding.amplipi.internal.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Partial reconfiguration of an audio Source + **/ +@Schema(description = "Partial reconfiguration of an audio Source ") +public class SourceUpdate { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + private String input; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SourceUpdate name(String name) { + this.name = name; + return this; + } + + /** + * Get input + * + * @return input + **/ + public String getInput() { + return input; + } + + public void setInput(String input) { + this.input = input; + } + + public SourceUpdate input(String input) { + this.input = input; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SourceUpdate {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" input: ").append(toIndentedString(input)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate2.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate2.java new file mode 100644 index 0000000000000..ef7d99f64001c --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/SourceUpdate2.java @@ -0,0 +1,113 @@ +/** + * 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.binding.amplipi.internal.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Partial reconfiguration of a specific audio Source + **/ +@Schema(description = "Partial reconfiguration of a specific audio Source ") +public class SourceUpdate2 { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + private String input; + + @Schema(required = true) + private Integer id; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SourceUpdate2 name(String name) { + this.name = name; + return this; + } + + /** + * Get input + * + * @return input + **/ + public String getInput() { + return input; + } + + public void setInput(String input) { + this.input = input; + } + + public SourceUpdate2 input(String input) { + this.input = input; + return this; + } + + /** + * Get id + * minimum: 0 + * maximum: 4 + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public SourceUpdate2 id(Integer id) { + this.id = id; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SourceUpdate2 {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" input: ").append(toIndentedString(input)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Status.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Status.java new file mode 100644 index 0000000000000..d4e8c416e2efe --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Status.java @@ -0,0 +1,201 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Full Controller Configuration and Status + **/ +@Schema(description = "Full Controller Configuration and Status ") +public class Status { + + @Schema + private List sources = null; + + @Schema + private List zones = null; + + @Schema + private List groups = null; + + @Schema + private List streams = null; + + @Schema + private List presets = null; + + @Schema + private Info info; + + /** + * Get sources + * + * @return sources + **/ + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } + + public Status sources(List sources) { + this.sources = sources; + return this; + } + + public Status addSourcesItem(Source sourcesItem) { + this.sources.add(sourcesItem); + return this; + } + + /** + * Get zones + * + * @return zones + **/ + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } + + public Status zones(List zones) { + this.zones = zones; + return this; + } + + public Status addZonesItem(Zone zonesItem) { + this.zones.add(zonesItem); + return this; + } + + /** + * Get groups + * + * @return groups + **/ + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public Status groups(List groups) { + this.groups = groups; + return this; + } + + public Status addGroupsItem(Group groupsItem) { + this.groups.add(groupsItem); + return this; + } + + /** + * Get streams + * + * @return streams + **/ + public List getStreams() { + return streams; + } + + public void setStreams(List streams) { + this.streams = streams; + } + + public Status streams(List streams) { + this.streams = streams; + return this; + } + + public Status addStreamsItem(Stream streamsItem) { + this.streams.add(streamsItem); + return this; + } + + /** + * Get presets + * + * @return presets + **/ + public List getPresets() { + return presets; + } + + public void setPresets(List presets) { + this.presets = presets; + } + + public Status presets(List presets) { + this.presets = presets; + return this; + } + + public Status addPresetsItem(Preset presetsItem) { + this.presets.add(presetsItem); + return this; + } + + /** + * Get info + * + * @return info + **/ + public Info getInfo() { + return info; + } + + public void setInfo(Info info) { + this.info = info; + } + + public Status info(Info info) { + this.info = info; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Status {\n"); + + sb.append(" sources: ").append(toIndentedString(sources)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" groups: ").append(toIndentedString(groups)).append("\n"); + sb.append(" streams: ").append(toIndentedString(streams)).append("\n"); + sb.append(" presets: ").append(toIndentedString(presets)).append("\n"); + sb.append(" info: ").append(toIndentedString(info)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Stream.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Stream.java new file mode 100644 index 0000000000000..16485a0b76f7d --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Stream.java @@ -0,0 +1,292 @@ +/** + * 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.binding.amplipi.internal.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Digital stream such as Pandora, Airplay or Spotify + **/ +@Schema(description = "Digital stream such as Pandora, Airplay or Spotify ") +public class Stream { + + @Schema + /** + * Unique identifier + **/ + private Integer id; + + @Schema(required = true) + /** + * Friendly name + **/ + private String name; + + @Schema(required = true) + /** + * stream type * pandora * shairport * dlna * internetradio * spotify + **/ + private String type; + + @Schema + /** + * User login + **/ + private String user; + + @Schema + /** + * Password + **/ + private String password; + + @Schema + /** + * Radio station identifier + **/ + private String station; + + @Schema + /** + * Stream url, used for internetradio + **/ + private String url; + + @Schema + /** + * Icon/Logo url, used for internetradio + **/ + private String logo; + + @Schema + /** + * Additional info about the current audio playing from the stream (generated during playback + **/ + private Object info; + + @Schema + /** + * State of the stream + **/ + private String status; + + /** + * Unique identifier + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Stream id(Integer id) { + this.id = id; + return this; + } + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Stream name(String name) { + this.name = name; + return this; + } + + /** + * stream type * pandora * shairport * dlna * internetradio * spotify + * + * @return type + **/ + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Stream type(String type) { + this.type = type; + return this; + } + + /** + * User login + * + * @return user + **/ + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public Stream user(String user) { + this.user = user; + return this; + } + + /** + * Password + * + * @return password + **/ + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Stream password(String password) { + this.password = password; + return this; + } + + /** + * Radio station identifier + * + * @return station + **/ + public String getStation() { + return station; + } + + public void setStation(String station) { + this.station = station; + } + + public Stream station(String station) { + this.station = station; + return this; + } + + /** + * Stream url, used for internetradio + * + * @return url + **/ + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Stream url(String url) { + this.url = url; + return this; + } + + /** + * Icon/Logo url, used for internetradio + * + * @return logo + **/ + public String getLogo() { + return logo; + } + + public void setLogo(String logo) { + this.logo = logo; + } + + public Stream logo(String logo) { + this.logo = logo; + return this; + } + + /** + * Additional info about the current audio playing from the stream (generated during playback + * + * @return info + **/ + public Object getInfo() { + return info; + } + + public void setInfo(Object info) { + this.info = info; + } + + public Stream info(Object info) { + this.info = info; + return this; + } + + /** + * State of the stream + * + * @return status + **/ + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Stream status(String status) { + this.status = status; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Stream {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" user: ").append(toIndentedString(user)).append("\n"); + sb.append(" password: ").append(toIndentedString(password)).append("\n"); + sb.append(" station: ").append(toIndentedString(station)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" logo: ").append(toIndentedString(logo)).append("\n"); + sb.append(" info: ").append(toIndentedString(info)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamCommand.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamCommand.java new file mode 100644 index 0000000000000..2cd01b01d7325 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamCommand.java @@ -0,0 +1,55 @@ +/** + * 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.binding.amplipi.internal.model; + +/** + * An enumeration. + */ +public enum StreamCommand { + + PLAY("play"), + + PAUSE("pause"), + + NEXT("next"), + + STOP("stop"), + + LIKE("like"), + + BAN("ban"), + + SHELVE("shelve"); + + private String value; + + StreamCommand(String value) { + this.value = value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static StreamCommand fromValue(String value) { + for (StreamCommand b : StreamCommand.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + +} + diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamUpdate.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamUpdate.java new file mode 100644 index 0000000000000..ce017148674c0 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/StreamUpdate.java @@ -0,0 +1,177 @@ +/** + * 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.binding.amplipi.internal.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Reconfiguration of a Stream + **/ +@Schema(description = "Reconfiguration of a Stream ") +public class StreamUpdate { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + private String user; + + @Schema + private String password; + + @Schema + private String station; + + @Schema + private String url; + + @Schema + private String logo; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public StreamUpdate name(String name) { + this.name = name; + return this; + } + + /** + * Get user + * + * @return user + **/ + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public StreamUpdate user(String user) { + this.user = user; + return this; + } + + /** + * Get password + * + * @return password + **/ + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public StreamUpdate password(String password) { + this.password = password; + return this; + } + + /** + * Get station + * + * @return station + **/ + public String getStation() { + return station; + } + + public void setStation(String station) { + this.station = station; + } + + public StreamUpdate station(String station) { + this.station = station; + return this; + } + + /** + * Get url + * + * @return url + **/ + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public StreamUpdate url(String url) { + this.url = url; + return this; + } + + /** + * Get logo + * + * @return logo + **/ + public String getLogo() { + return logo; + } + + public void setLogo(String logo) { + this.logo = logo; + } + + public StreamUpdate logo(String logo) { + this.logo = logo; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class StreamUpdate {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" user: ").append(toIndentedString(user)).append("\n"); + sb.append(" password: ").append(toIndentedString(password)).append("\n"); + sb.append(" station: ").append(toIndentedString(station)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" logo: ").append(toIndentedString(logo)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ValidationError.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ValidationError.java new file mode 100644 index 0000000000000..76913112edaa7 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ValidationError.java @@ -0,0 +1,112 @@ +/** + * 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.binding.amplipi.internal.model; + +import java.util.ArrayList; +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +public class ValidationError { + + @Schema(required = true) + private List loc = new ArrayList(); + + @Schema(required = true) + private String msg; + + @Schema(required = true) + private String type; + + /** + * Get loc + * + * @return loc + **/ + public List getLoc() { + return loc; + } + + public void setLoc(List loc) { + this.loc = loc; + } + + public ValidationError loc(List loc) { + this.loc = loc; + return this; + } + + public ValidationError addLocItem(String locItem) { + this.loc.add(locItem); + return this; + } + + /** + * Get msg + * + * @return msg + **/ + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public ValidationError msg(String msg) { + this.msg = msg; + return this; + } + + /** + * Get type + * + * @return type + **/ + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public ValidationError type(String type) { + this.type = type; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ValidationError {\n"); + + sb.append(" loc: ").append(toIndentedString(loc)).append("\n"); + sb.append(" msg: ").append(toIndentedString(msg)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Zone.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Zone.java new file mode 100644 index 0000000000000..61af4286532a7 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/Zone.java @@ -0,0 +1,199 @@ +/** + * 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.binding.amplipi.internal.model; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Audio output to a stereo pair of speakers, typically belonging to a room + **/ +@Schema(description = "Audio output to a stereo pair of speakers, typically belonging to a room ") +public class Zone { + + @Schema + /** + * Unique identifier + **/ + private Integer id; + + @Schema(required = true) + /** + * Friendly name + **/ + private String name; + + @Schema + @SerializedName("source_id") + /** + * id of the connected source + **/ + private Integer sourceId = 0; + + @Schema + /** + * Set to true if output is muted + **/ + private Boolean mute = true; + + @Schema + /** + * Output volume in dB + **/ + private Integer vol = -79; + + @Schema + /** + * Set to true if not connected to a speaker + **/ + private Boolean disabled = false; + + /** + * Unique identifier + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Zone id(Integer id) { + this.id = id; + return this; + } + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Zone name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public Zone sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set to true if output is muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public Zone mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Output volume in dB + * minimum: -79 + * maximum: 0 + * + * @return vol + **/ + public Integer getVol() { + return vol; + } + + public void setVol(Integer vol) { + this.vol = vol; + } + + public Zone vol(Integer vol) { + this.vol = vol; + return this; + } + + /** + * Set to true if not connected to a speaker + * + * @return disabled + **/ + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(Boolean disabled) { + this.disabled = disabled; + } + + public Zone disabled(Boolean disabled) { + this.disabled = disabled; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Zone {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" vol: ").append(toIndentedString(vol)).append("\n"); + sb.append(" disabled: ").append(toIndentedString(disabled)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate.java new file mode 100644 index 0000000000000..6c14fa5596bad --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate.java @@ -0,0 +1,174 @@ +/** + * 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.binding.amplipi.internal.model; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Reconfiguration of a Zone + **/ +@Schema(description = "Reconfiguration of a Zone ") +public class ZoneUpdate { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + @SerializedName("source_id") + /** + * id of the connected source + **/ + private Integer sourceId; + + @Schema + /** + * Set to true if output is muted + **/ + private Boolean mute; + + @Schema + /** + * Output volume in dB + **/ + private Integer vol; + + @Schema + /** + * Set to true if not connected to a speaker + **/ + private Boolean disabled; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ZoneUpdate name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public ZoneUpdate sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set to true if output is muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public ZoneUpdate mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Output volume in dB + * minimum: -79 + * maximum: 0 + * + * @return vol + **/ + public Integer getVol() { + return vol; + } + + public void setVol(Integer vol) { + this.vol = vol; + } + + public ZoneUpdate vol(Integer vol) { + this.vol = vol; + return this; + } + + /** + * Set to true if not connected to a speaker + * + * @return disabled + **/ + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(Boolean disabled) { + this.disabled = disabled; + } + + public ZoneUpdate disabled(Boolean disabled) { + this.disabled = disabled; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ZoneUpdate {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" vol: ").append(toIndentedString(vol)).append("\n"); + sb.append(" disabled: ").append(toIndentedString(disabled)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate2.java b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate2.java new file mode 100644 index 0000000000000..bc236a1bd15fb --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/gen/java/org/openhab/binding/amplipi/internal/model/ZoneUpdate2.java @@ -0,0 +1,198 @@ +/** + * 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.binding.amplipi.internal.model; + +import com.google.gson.annotations.SerializedName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Reconfiguration of a specific Zone + **/ +@Schema(description = "Reconfiguration of a specific Zone ") +public class ZoneUpdate2 { + + @Schema + /** + * Friendly name + **/ + private String name; + + @Schema + @SerializedName("source_id") + /** + * id of the connected source + **/ + private Integer sourceId = 0; + + @Schema + /** + * Set to true if output is muted + **/ + private Boolean mute = true; + + @Schema + /** + * Output volume in dB + **/ + private Integer vol = -79; + + @Schema + /** + * Set to true if not connected to a speaker + **/ + private Boolean disabled = false; + + @Schema(required = true) + private Integer id; + + /** + * Friendly name + * + * @return name + **/ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ZoneUpdate2 name(String name) { + this.name = name; + return this; + } + + /** + * id of the connected source + * minimum: 0 + * maximum: 3 + * + * @return sourceId + **/ + public Integer getSourceId() { + return sourceId; + } + + public void setSourceId(Integer sourceId) { + this.sourceId = sourceId; + } + + public ZoneUpdate2 sourceId(Integer sourceId) { + this.sourceId = sourceId; + return this; + } + + /** + * Set to true if output is muted + * + * @return mute + **/ + public Boolean getMute() { + return mute; + } + + public void setMute(Boolean mute) { + this.mute = mute; + } + + public ZoneUpdate2 mute(Boolean mute) { + this.mute = mute; + return this; + } + + /** + * Output volume in dB + * minimum: -79 + * maximum: 0 + * + * @return vol + **/ + public Integer getVol() { + return vol; + } + + public void setVol(Integer vol) { + this.vol = vol; + } + + public ZoneUpdate2 vol(Integer vol) { + this.vol = vol; + return this; + } + + /** + * Set to true if not connected to a speaker + * + * @return disabled + **/ + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(Boolean disabled) { + this.disabled = disabled; + } + + public ZoneUpdate2 disabled(Boolean disabled) { + this.disabled = disabled; + return this; + } + + /** + * Get id + * minimum: 0 + * maximum: 35 + * + * @return id + **/ + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public ZoneUpdate2 id(Integer id) { + this.id = id; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ZoneUpdate2 {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" sourceId: ").append(toIndentedString(sourceId)).append("\n"); + sb.append(" mute: ").append(toIndentedString(mute)).append("\n"); + sb.append(" vol: ").append(toIndentedString(vol)).append("\n"); + sb.append(" disabled: ").append(toIndentedString(disabled)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/feature/feature.xml b/bundles/org.openhab.binding.amplipi/src/main/feature/feature.xml new file mode 100644 index 0000000000000..2c7bfcfce1631 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.amplipi/${project.version} + + diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiBindingConstants.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiBindingConstants.java new file mode 100644 index 0000000000000..05e4781c5dbca --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiBindingConstants.java @@ -0,0 +1,44 @@ +/** + * 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.binding.amplipi.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link AmpliPiBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +public class AmpliPiBindingConstants { + + private static final String BINDING_ID = "amplipi"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller"); + public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone"); + public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group"); + + // List of all Channel ids + public static final String CHANNEL_PRESET = "preset"; + public static final String CHANNEL_INPUT = "input"; + public static final String CHANNEL_VOLUME = "volume"; + public static final String CHANNEL_MUTE = "mute"; + public static final String CHANNEL_SOURCE = "source"; + + // list of configuration parameters + public static final String CFG_PARAM_HOSTNAME = "hostname"; + public static final String CFG_PARAM_ID = "id"; +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiConfiguration.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiConfiguration.java new file mode 100644 index 0000000000000..a207161e73499 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiConfiguration.java @@ -0,0 +1,27 @@ +/** + * 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.binding.amplipi.internal; + +/** + * The {@link AmpliPiConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Kai Kreuzer - Initial contribution + */ +public class AmpliPiConfiguration { + + /** + * Sample configuration parameters. Replace with your own. + */ + public String hostname; + public int refreshInterval; +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiGroupHandler.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiGroupHandler.java new file mode 100644 index 0000000000000..9962b484f88fb --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiGroupHandler.java @@ -0,0 +1,149 @@ +/** + * 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.binding.amplipi.internal; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.amplipi.internal.model.Group; +import org.openhab.binding.amplipi.internal.model.GroupUpdate; +import org.openhab.binding.amplipi.internal.model.Status; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * The {@link AmpliPiGroupHandler} is responsible for handling commands, which are + * sent to one of the AmpliPi Groups. + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +public class AmpliPiGroupHandler extends BaseThingHandler implements AmpliPiStatusChangeListener { + + private final Logger logger = LoggerFactory.getLogger(AmpliPiGroupHandler.class); + + private final HttpClient httpClient; + private final Gson gson; + + private @Nullable AmpliPiHandler bridgeHandler; + + public AmpliPiGroupHandler(Thing thing, HttpClient httpClient) { + super(thing); + this.httpClient = httpClient; + this.gson = new Gson(); + } + + private int getId(Thing thing) { + return Integer.valueOf(thing.getConfiguration().get(AmpliPiBindingConstants.CFG_PARAM_ID).toString()); + } + + @Override + public void initialize() { + Bridge bridge = getBridge(); + if (bridge != null) { + bridgeHandler = (AmpliPiHandler) bridge.getHandler(); + if (bridgeHandler != null) { + bridgeHandler.addStatusChangeListener(this); + } else { + throw new IllegalStateException("Bridge handler must not be null here!"); + } + if (bridge.getStatus() == ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } else { + throw new IllegalStateException("Bridge must not be null here!"); + } + } + + @Override + public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) { + if (command == RefreshType.REFRESH) { + // do nothing - we just wait for the next automatic refresh + return; + } + GroupUpdate update = new GroupUpdate(); + switch (channelUID.getId()) { + case AmpliPiBindingConstants.CHANNEL_MUTE: + if (command instanceof OnOffType) { + update.setMute(command == OnOffType.ON); + } + break; + case AmpliPiBindingConstants.CHANNEL_VOLUME: + if (command instanceof PercentType) { + update.setVolDelta(AmpliPiUtils.percentTypeToVolume((PercentType) command)); + } + break; + case AmpliPiBindingConstants.CHANNEL_SOURCE: + if (command instanceof DecimalType) { + update.setSourceId(((DecimalType) command).intValue()); + } + break; + } + if (bridgeHandler != null) { + String url = bridgeHandler.getUrl() + "/api/groups/" + getId(thing); + ; + StringContentProvider contentProvider = new StringContentProvider(gson.toJson(update)); + try { + ContentResponse response = httpClient.newRequest(url).method(HttpMethod.PATCH) + .content(contentProvider, "application/json").send(); + if (response.getStatus() != HttpStatus.OK_200) { + logger.error("AmpliPi API returned HTTP status {}.", response.getStatus()); + logger.debug("Content: {}", response.getContentAsString()); + } else { + updateStatus(ThingStatus.ONLINE); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi request failed: " + e.getMessage()); + } + } + } + + @Override + public void receive(@NonNull Status status) { + int id = getId(thing); + Optional group = status.getGroups().stream().filter(z -> z.getId().equals(id)).findFirst(); + if (group.isPresent()) { + Boolean mute = group.get().getMute(); + Integer volume = group.get().getVolDelta(); + Integer source = group.get().getSourceId(); + updateState(AmpliPiBindingConstants.CHANNEL_MUTE, mute ? OnOffType.ON : OnOffType.OFF); + updateState(AmpliPiBindingConstants.CHANNEL_VOLUME, AmpliPiUtils.volumeToPercentType(volume)); + updateState(AmpliPiBindingConstants.CHANNEL_SOURCE, new DecimalType(source)); + } + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandler.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandler.java new file mode 100644 index 0000000000000..41523c6ec7269 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandler.java @@ -0,0 +1,215 @@ +/** + * 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.binding.amplipi.internal; + +import static org.openhab.binding.amplipi.internal.AmpliPiBindingConstants.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.amplipi.internal.discovery.AmpliPiZoneAndGroupDiscoveryService; +import org.openhab.binding.amplipi.internal.model.Preset; +import org.openhab.binding.amplipi.internal.model.SourceUpdate; +import org.openhab.binding.amplipi.internal.model.Status; +import org.openhab.binding.amplipi.internal.model.Stream; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * The {@link AmpliPiHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +public class AmpliPiHandler extends BaseBridgeHandler { + + private static final int REQUEST_TIMEOUT = 5000; + + private final Logger logger = LoggerFactory.getLogger(AmpliPiHandler.class); + + private final HttpClient httpClient; + private final Gson gson; + + private String url = "http://amplipi"; + private List presets = List.of(); + private List streams = List.of(); + private List changeListeners = new ArrayList<>(); + + private @Nullable ScheduledFuture refreshJob; + + public AmpliPiHandler(Thing thing, HttpClient httpClient) { + super((Bridge) thing); + this.httpClient = httpClient; + this.gson = new Gson(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + AmpliPiConfiguration config = getConfigAs(AmpliPiConfiguration.class); + url = "http://" + config.hostname; + + if (CHANNEL_PRESET.equals(channelUID.getId())) { + if (command instanceof RefreshType) { + updateState(channelUID, UnDefType.NULL); + } else if (command instanceof DecimalType) { + DecimalType preset = (DecimalType) command; + try { + ContentResponse response = this.httpClient + .newRequest(url + "/api/presets/" + preset.intValue() + "/load").method(HttpMethod.POST) + .timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send(); + if (response.getStatus() != HttpStatus.OK_200) { + logger.error("AmpliPi API returned HTTP status {}.", response.getStatus()); + logger.debug("Content: {}", response.getContentAsString()); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi request failed: " + e.getMessage()); + } + } + } else if (channelUID.getId().startsWith(CHANNEL_INPUT)) { + if (command instanceof StringType) { + StringType input = (StringType) command; + int source = Integer.valueOf(channelUID.getId().substring(CHANNEL_INPUT.length())) - 1; + SourceUpdate update = new SourceUpdate(); + update.setInput(input.toString()); + try { + StringContentProvider contentProvider = new StringContentProvider(gson.toJson(update)); + ContentResponse response = this.httpClient.newRequest(url + "/api/sources/" + source) + .method(HttpMethod.PATCH).content(contentProvider, "application/json") + .timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send(); + if (response.getStatus() != HttpStatus.OK_200) { + logger.error("AmpliPi API returned HTTP status {}.", response.getStatus()); + logger.debug("Content: {}", response.getContentAsString()); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi request failed: " + e.getMessage()); + } + } + } + } + + @Override + public void initialize() { + AmpliPiConfiguration config = getConfigAs(AmpliPiConfiguration.class); + url = "http://" + config.hostname; + + updateStatus(ThingStatus.UNKNOWN); + + refreshJob = scheduler.scheduleWithFixedDelay(() -> { + try { + ContentResponse response = this.httpClient.newRequest(url + "/api") + .timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send(); + if (response.getStatus() == HttpStatus.OK_200) { + Status currentStatus = this.gson.fromJson(response.getContentAsString(), Status.class); + if (currentStatus != null) { + updateStatus(ThingStatus.ONLINE); + setProperties(currentStatus); + setInputs(currentStatus); + presets = currentStatus.getPresets(); + streams = currentStatus.getStreams(); + changeListeners.forEach(l -> l.receive(currentStatus)); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "No valid response from AmpliPi API."); + logger.debug("Received response: {}", response.getContentAsString()); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi API returned HTTP status " + response.getStatus() + "."); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi request failed: " + e.getMessage()); + } catch (Exception e) { + logger.error("Unexpected error occurred: {}", e.getMessage()); + } + }, 0, config.refreshInterval, TimeUnit.SECONDS); + } + + private void setProperties(Status currentStatus) { + String version = currentStatus.getInfo().getVersion(); + Map props = editProperties(); + props.put(Thing.PROPERTY_FIRMWARE_VERSION, version); + updateProperties(props); + } + + private void setInputs(Status currentStatus) { + currentStatus.getSources().forEach(source -> { + updateState(CHANNEL_INPUT + (source.getId() + 1), new StringType(source.getInput())); + }); + } + + @Override + public void dispose() { + if (refreshJob != null) { + refreshJob.cancel(true); + refreshJob = null; + } + } + + @Override + public Collection> getServices() { + return Set.of(PresetCommandOptionProvider.class, InputStateOptionProvider.class, + AmpliPiZoneAndGroupDiscoveryService.class); + } + + public List getPresets() { + return presets; + } + + public List getStreams() { + return streams; + } + + public String getUrl() { + return url; + } + + public void addStatusChangeListener(AmpliPiStatusChangeListener listener) { + changeListeners.add(listener); + } + + public void removeStatusChangeListener(AmpliPiStatusChangeListener listener) { + changeListeners.remove(listener); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandlerFactory.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandlerFactory.java new file mode 100644 index 0000000000000..e548682a27f1c --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiHandlerFactory.java @@ -0,0 +1,73 @@ +/** + * 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.binding.amplipi.internal; + +import static org.openhab.binding.amplipi.internal.AmpliPiBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link AmpliPiHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.amplipi", service = ThingHandlerFactory.class) +public class AmpliPiHandlerFactory extends BaseThingHandlerFactory { + + private HttpClient httpClient; + + @Activate + public AmpliPiHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + } + + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_CONTROLLER, THING_TYPE_ZONE, + THING_TYPE_GROUP); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_CONTROLLER.equals(thingTypeUID)) { + return new AmpliPiHandler(thing, httpClient); + } + if (THING_TYPE_ZONE.equals(thingTypeUID)) { + return new AmpliPiZoneHandler(thing, httpClient); + } + if (THING_TYPE_GROUP.equals(thingTypeUID)) { + return new AmpliPiGroupHandler(thing, httpClient); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiStatusChangeListener.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiStatusChangeListener.java new file mode 100644 index 0000000000000..315f576390c4b --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiStatusChangeListener.java @@ -0,0 +1,33 @@ +/** + * 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.binding.amplipi.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.amplipi.internal.model.Status; + +/** + * This interface is implemented by classes that want to register for any updates from the AmpliPi system. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public interface AmpliPiStatusChangeListener { + + /** + * This method is called whenever a status update occurs. + * + * @param status The current status of the AmpliPi + */ + public void receive(Status status); +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiUtils.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiUtils.java new file mode 100644 index 0000000000000..27324f1984f94 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiUtils.java @@ -0,0 +1,48 @@ +/** + * 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.binding.amplipi.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.PercentType; + +/** + * This class has some commonly used static helper functions. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public class AmpliPiUtils { + + /** + * Converts a volume from AmpliPi to an openHAB PercentType + * + * @param volume volume from AmpliPi + * @return according PercentType for openHAB + */ + public static PercentType volumeToPercentType(Integer volume) { + // AmpliPi volumes range from -79..0 + return new PercentType((volume + 79) * 100 / 79); + } + + /** + * Converts a volume as PercentType from openHAB to an integer for AmpliPi + * + * @param volume volume as PercentType + * @return according volume as int for AmpliPi + */ + public static int percentTypeToVolume(PercentType volume) { + // AmpliPi volumes range from -79..0 + return (volume.intValue() * 79 / 100) - 79; + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiZoneHandler.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiZoneHandler.java new file mode 100644 index 0000000000000..ff3ad20e3377e --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/AmpliPiZoneHandler.java @@ -0,0 +1,148 @@ +/** + * 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.binding.amplipi.internal; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.amplipi.internal.model.Status; +import org.openhab.binding.amplipi.internal.model.Zone; +import org.openhab.binding.amplipi.internal.model.ZoneUpdate; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * The {@link AmpliPiGroupHandler} is responsible for handling commands, which are + * sent to one of the AmpliPi Groups. + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +public class AmpliPiZoneHandler extends BaseThingHandler implements AmpliPiStatusChangeListener { + + private final Logger logger = LoggerFactory.getLogger(AmpliPiZoneHandler.class); + + private final HttpClient httpClient; + private final Gson gson; + + private @Nullable AmpliPiHandler bridgeHandler; + + public AmpliPiZoneHandler(Thing thing, HttpClient httpClient) { + super(thing); + this.httpClient = httpClient; + this.gson = new Gson(); + } + + @Override + public void initialize() { + Bridge bridge = getBridge(); + if (bridge != null) { + bridgeHandler = (AmpliPiHandler) bridge.getHandler(); + if (bridgeHandler != null) { + bridgeHandler.addStatusChangeListener(this); + } else { + throw new IllegalStateException("Bridge handler must not be null here!"); + } + if (bridge.getStatus() == ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } else { + throw new IllegalStateException("Bridge must not be null here!"); + } + } + + private int getId(Thing thing) { + return Integer.valueOf(thing.getConfiguration().get(AmpliPiBindingConstants.CFG_PARAM_ID).toString()); + } + + @Override + public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) { + if (command == RefreshType.REFRESH) { + // do nothing - we just wait for the next automatic refresh + return; + } + ZoneUpdate update = new ZoneUpdate(); + switch (channelUID.getId()) { + case AmpliPiBindingConstants.CHANNEL_MUTE: + if (command instanceof OnOffType) { + update.setMute(command == OnOffType.ON); + } + break; + case AmpliPiBindingConstants.CHANNEL_VOLUME: + if (command instanceof PercentType) { + update.setVol(AmpliPiUtils.percentTypeToVolume((PercentType) command)); + } + break; + case AmpliPiBindingConstants.CHANNEL_SOURCE: + if (command instanceof DecimalType) { + update.setSourceId(((DecimalType) command).intValue()); + } + break; + } + if (bridgeHandler != null) { + String url = bridgeHandler.getUrl() + "/api/zones/" + getId(thing); + StringContentProvider contentProvider = new StringContentProvider(gson.toJson(update)); + try { + ContentResponse response = httpClient.newRequest(url).method(HttpMethod.PATCH) + .content(contentProvider, "application/json").send(); + if (response.getStatus() != HttpStatus.OK_200) { + logger.error("AmpliPi API returned HTTP status {}.", response.getStatus()); + logger.debug("Content: {}", response.getContentAsString()); + } else { + updateStatus(ThingStatus.ONLINE); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "AmpliPi request failed: " + e.getMessage()); + } + } + } + + @Override + public void receive(@NonNull Status status) { + int id = getId(thing); + Optional zone = status.getZones().stream().filter(z -> z.getId().equals(id)).findFirst(); + if (zone.isPresent()) { + Boolean mute = zone.get().getMute(); + Integer volume = zone.get().getVol(); + Integer source = zone.get().getSourceId(); + updateState(AmpliPiBindingConstants.CHANNEL_MUTE, mute ? OnOffType.ON : OnOffType.OFF); + updateState(AmpliPiBindingConstants.CHANNEL_VOLUME, AmpliPiUtils.volumeToPercentType(volume)); + updateState(AmpliPiBindingConstants.CHANNEL_SOURCE, new DecimalType(source)); + } + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/InputStateOptionProvider.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/InputStateOptionProvider.java new file mode 100644 index 0000000000000..e212e34fac28e --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/InputStateOptionProvider.java @@ -0,0 +1,74 @@ +/** + * 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.binding.amplipi.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.amplipi.internal.model.Stream; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateOption; + +/** + * This class provides the list of valid inputs for the input channel of a source. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public class InputStateOptionProvider extends BaseDynamicStateDescriptionProvider implements ThingHandlerService { + + private @Nullable AmpliPiHandler handler; + + @Override + public void setThingHandler(ThingHandler handler) { + this.handler = (AmpliPiHandler) handler; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @Override + public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original, + @Nullable Locale locale) { + ChannelTypeUID typeUID = channel.getChannelTypeUID(); + List options = new ArrayList<>(); + options.add(new StateOption("local", "RCA")); + if (typeUID != null && AmpliPiBindingConstants.CHANNEL_INPUT.equals(typeUID.getId()) && handler != null) { + List streams = handler.getStreams(); + for (Stream stream : streams) { + options.add(new StateOption("stream=" + stream.getId(), getLabel(stream))); + } + setStateOptions(channel.getUID(), options); + } + return super.getStateDescription(channel, original, locale); + } + + private @Nullable String getLabel(Stream stream) { + if (stream.getType().equals("internetradio")) { + return stream.getName(); + } else { + return stream.getType().substring(0, 1).toUpperCase() + stream.getType().substring(1); + } + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/PresetCommandOptionProvider.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/PresetCommandOptionProvider.java new file mode 100644 index 0000000000000..233e18d755178 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/PresetCommandOptionProvider.java @@ -0,0 +1,65 @@ +/** + * 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.binding.amplipi.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.amplipi.internal.model.Preset; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.CommandDescription; +import org.openhab.core.types.CommandOption; + +/** + * This class provides the list of valid commands for the preset channel. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public class PresetCommandOptionProvider extends BaseDynamicCommandDescriptionProvider implements ThingHandlerService { + + private @Nullable AmpliPiHandler handler; + + @Override + public void setThingHandler(ThingHandler handler) { + this.handler = (AmpliPiHandler) handler; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @Override + public @Nullable CommandDescription getCommandDescription(Channel channel, + @Nullable CommandDescription originalCommandDescription, @Nullable Locale locale) { + ChannelTypeUID typeUID = channel.getChannelTypeUID(); + List options = new ArrayList<>(); + if (typeUID != null && AmpliPiBindingConstants.CHANNEL_PRESET.equals(typeUID.getId()) && handler != null) { + List presets = handler.getPresets(); + for (Preset preset : presets) { + options.add(new CommandOption(preset.getId().toString(), preset.getName())); + } + setCommandOptions(channel.getUID(), options); + } + return super.getCommandDescription(channel, originalCommandDescription, locale); + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiMDNSDiscoveryParticipant.java new file mode 100644 index 0000000000000..129c1baff9c0a --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiMDNSDiscoveryParticipant.java @@ -0,0 +1,67 @@ +/** + * 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.binding.amplipi.internal.discovery; + +import java.util.Set; + +import javax.jmdns.ServiceInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.amplipi.internal.AmpliPiBindingConstants; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; + +/** + * This is a discovery participant which finds AmpliPis on the local network + * through their mDNS announcements. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public class AmpliPiMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant { + + @Override + public Set getSupportedThingTypeUIDs() { + return Set.of(AmpliPiBindingConstants.THING_TYPE_CONTROLLER); + } + + @Override + public String getServiceType() { + return "_http._tcp"; + } + + @Override + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + ThingUID uid = getThingUID(service); + if (uid != null) { + DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(service.getName()) + .withProperty(AmpliPiBindingConstants.CFG_PARAM_HOSTNAME, + service.getInet4Addresses()[0].getHostAddress()) + .withRepresentationProperty(AmpliPiBindingConstants.CFG_PARAM_HOSTNAME).build(); + return result; + } else { + return null; + } + } + + @Override + public @Nullable ThingUID getThingUID(ServiceInfo service) { + // TODO: Currently, the AmpliPi does not seem to announce any services. + return null; + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiZoneAndGroupDiscoveryService.java b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiZoneAndGroupDiscoveryService.java new file mode 100644 index 0000000000000..5ebf8a3b26ce4 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/java/org/openhab/binding/amplipi/internal/discovery/AmpliPiZoneAndGroupDiscoveryService.java @@ -0,0 +1,115 @@ +/** + * 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.binding.amplipi.internal.discovery; + +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.amplipi.internal.AmpliPiBindingConstants; +import org.openhab.binding.amplipi.internal.AmpliPiHandler; +import org.openhab.binding.amplipi.internal.AmpliPiStatusChangeListener; +import org.openhab.binding.amplipi.internal.model.Group; +import org.openhab.binding.amplipi.internal.model.Status; +import org.openhab.binding.amplipi.internal.model.Zone; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; + +/** + * This class discoveres the available zones and groups of the AmpliPi system. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public class AmpliPiZoneAndGroupDiscoveryService extends AbstractDiscoveryService + implements ThingHandlerService, AmpliPiStatusChangeListener { + + private static final int TIMEOUT = 10; + + private @Nullable AmpliPiHandler handler; + private List zones = List.of(); + private List groups = List.of(); + + public AmpliPiZoneAndGroupDiscoveryService() throws IllegalArgumentException { + super(Set.of(AmpliPiBindingConstants.THING_TYPE_GROUP, AmpliPiBindingConstants.THING_TYPE_ZONE), TIMEOUT, true); + } + + @Override + public void setThingHandler(ThingHandler handler) { + AmpliPiHandler ampliPiHander = (AmpliPiHandler) handler; + ampliPiHander.addStatusChangeListener(this); + this.handler = ampliPiHander; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @Override + protected void startScan() { + for (Zone z : zones) { + if (!z.getDisabled()) { + createZone(z); + } + } + for (Group g : groups) { + createGroup(g); + } + } + + private void createZone(Zone z) { + if (handler != null) { + ThingUID bridgeUID = handler.getThing().getUID(); + ThingUID uid = new ThingUID(AmpliPiBindingConstants.THING_TYPE_ZONE, bridgeUID, z.getId().toString()); + DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(z.getName()) + .withProperty(AmpliPiBindingConstants.CFG_PARAM_ID, z.getId()).withBridge(bridgeUID) + .withRepresentationProperty(AmpliPiBindingConstants.CFG_PARAM_ID).build(); + thingDiscovered(result); + } + } + + private void createGroup(Group g) { + if (handler != null) { + ThingUID bridgeUID = handler.getThing().getUID(); + ThingUID uid = new ThingUID(AmpliPiBindingConstants.THING_TYPE_GROUP, bridgeUID, g.getId().toString()); + DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(g.getName()) + .withProperty(AmpliPiBindingConstants.CFG_PARAM_ID, g.getId()).withBridge(bridgeUID) + .withRepresentationProperty(AmpliPiBindingConstants.CFG_PARAM_ID).build(); + thingDiscovered(result); + } + } + + @Override + public void deactivate() { + if (handler != null) { + handler.removeStatusChangeListener(this); + } + super.deactivate(); + } + + @Override + public void receive(Status status) { + zones = status.getZones(); + groups = status.getGroups(); + if (isBackgroundDiscoveryEnabled()) { + startScan(); + } + } +} diff --git a/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 0000000000000..6c6299d748641 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + AmpliPi Binding + This is the binding for the AmpliPi Home Audio System from MicroNova. + + diff --git a/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..d73b031091590 --- /dev/null +++ b/bundles/org.openhab.binding.amplipi/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,118 @@ + + + + + + An AmpliPi controller + + + + + + This channel selects the input for source 1. + + + + This channel selects the input for source 2. + + + + This channel selects the input for source 3. + + + + This channel selects the input for source 4. + + + + + + network-address + + Hostname or IP address of the AmpliPi + + + + Interval the device is polled in sec. + 10 + + + + + + + + + + + A zone of the AmpliPi system + + + + + + + + + + + The ID of the zone + + + + + + + + + + + A group of the AmpliPi system + + + + + + + + + + + The ID of the group + + + + + + Number + + Choose an existing preset + veto + + + + String + + The selected input for the source + recommend + + + + Number + + The audio source that is played + + + + + + + + + recommend + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index d890661143031..2d2f5f69237c7 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -52,6 +52,7 @@ org.openhab.binding.amazondashbutton org.openhab.binding.amazonechocontrol org.openhab.binding.ambientweather + org.openhab.binding.amplipi org.openhab.binding.androiddebugbridge org.openhab.binding.astro org.openhab.binding.atlona