Giter Club home page Giter Club logo

iobroker.heos's Introduction

Logo

ioBroker.heos

NPM version Downloads Number of Installations (latest) Number of Installations (stable) Dependency Status Known Vulnerabilities

NPM

The adapter lets control HEOS from ioBroker.

Disclaimer

HEOS, DENON and Marantz are trademarks of D&M Holdings Inc. The developers of this module are in no way endorsed by or affiliated with D&M Holdings Inc., or any associated subsidiaries, logos or trademarks.

Reference

The used HEOS API is documented here: https://rn.dmglobal.com/euheos/HEOS_CLI_ProtocolSpecification_2021.pdf

Network Requirements

The protocol SSDP is used for finding the players. UPnP requires multicast access to the 239.255.255.250:1900 along with the appropriate IGMP messages. The source port for receiving SSDP Messages can be configured in the adapter settings (Default setting is 0 means the port is automatically choosen). Further Details: https://support.denon.com/app/answers/detail/a_id/4717/~/network-requirements-for-heos For the API access to the HEOS Players the adapter uses the port 1255.

Configuration

  • AutoPlay: Automatically plays music after the player is connected or on unmute. Can be configured globally in configuration. If it is enabled globally you can disable it for one specific player with the state auto_play.
  • Command scope: Defines to which players the command scope/[cmd] of the command state is send to. It can be send to all players, all leading players or to all PIDs in the comma separated state: heos.0.command_scope_pid
  • Mute Regex: In the configuration you can activate a function to mute the player based on a regex match on the song information. That can be used to mute ads automatically. For example for Spotify you can use the following regex: spotify:ad:|Advertisement.
  • ignore_broadcast_cmd: This player state configures, if the player should ignore commands to all players e.g. player/set_mute&state=on or pressing the play button for presets/playlists

States and their meanings

Command State

The HEOS player can be controlled by the different player states. To control the players in a more advanced way you can use the command state. On the one hand there is one global command state (heos.0.command) to control the whole adapter or multiple players with one command. On the other hand there is a command state per player.

HEOS Command State (heos.0.command)

  • system/connect: Try to Connect to HEOS
  • system/disconnect: Disconnect from HEOS
  • system/reconnect: Disconnect and Connect
  • system/load_sources: Reload sources
  • system/reboot: Reboot connected player
  • system/reboot_all: Reboot all players
  • group/set_group?pid=<pid1>,<pid2>,...: Set group with the list of player ids e.g. group/set_group?pid=12345678,12345679.
  • group/set_group?pid=<pid1>: Delete existing group e.g. "group/set_group?pid=12345678"
  • group/ungroup_all: Delete all groups
  • group/group_all: Group all player in one group
  • player/[cmd]: Send the command to all players. e.g. player/set_mute&state=on
  • leader/[cmd]: Send the command to all leading players. e.g. leader/set_mute&state=on
  • scope/[cmd]: Send the command to the configured scope all players, leading players or comma separated player pids in scope_pids
  • ...: All other commands are tried to send to HEOS (Look in the HEOS API PDF for details)

Player Command State (heos.0.players.123456789.command)

Note: Multiple commands are possible, if they are separated with the pipe e.g. set_volume&level=20|play_preset&preset=1

  • set_volume?level=0|1|..|100: Set the player volume
  • set_play_state?state=play|pause|stop: Set the player state
  • set_play_mode?repeat=on_all|on_one|off&shuffle=on|off: Set Repeat and Shuffle mode
  • set_mute?state=on|off: Mute player
  • volume_down?step=1..10: Lower volume
  • volume_up?step=1..10: Raise volume
  • play_next: Play next
  • play_previous: Play previous
  • play_preset?preset=1|2|..|n: Play preset n
  • play_stream?url=url_path: Play URL-Stream
  • add_to_queue?sid=1025&aid=4&cid=[CID]: Play playlist with [CID] on player (aid: 1 – play now; 2 – play next; 3 – add to end; 4 – replace and play)

Presets & Playlists

Each source e.g. preset/favorite or playlists are located in the sources state folder (heos.0.sources). You can find your presets/favorites in the subfolder with the ID 1028 and the playlists in the subfolder with the ID 1025. Initially the adapter don't create your individual presets and playlists, because you have to trigger an update by setting the following states to true:

  • Presets/Favorites: heos.0.sources.1028.browse
  • Playlists: heos.0.sources.1025.browse After that the adapter creates the states for the presets or playlists so that you easily can play the preset on all players.

Image color extraction

With version 1.7.6 the prominent colors of the song cover are extracted and saved to three new player states:

  • current_image_color_palette: Prominent colors selected by node-vibrant.
  • current_image_color_background: Color with the biggest population in the image. Can be used as background color for player controls in VIS.
  • current_image_color_foreground: Color with the second biggest population in the image and a good read contrast to the background color. Can be used as text color for player controls in VIS.

Seek

The seek functionality is not working on all sources. Spotify and Amazon Music are supporting seeking.

SayIt

SayIt Adapter is supported.

Sayit Sayit Config

Material UI

Material UI Adapter is supported.

Material

VIS

Installation

  • Create following string states:
    • 0_userdata.0.heos.queue_pid
    • 0_userdata.0.heos.queue_html
    • 0_userdata.0.heos.browse_result_html

Player View

  • Open the file: player_view.json
  • Replace 123456789 with the player pid
  • Import view into VIS

Player view

Presets

  • Click button heos.0.sources.1028.browse to load presets
  • Open the file: presets_view.json
  • Import view into VIS

Presets config Presets

Queue

Queue widget

Browse Sources

Browse widget Browse sources Browse tunein

Alternative you can use the script from Uhula: https://forum.iobroker.net/post/498779

Changelog

2.2.1 (2024-01-14)

  • (withstu) add workaround for node 19+ ECONNRESET bug #299

2.2.0 (2024-01-06)

  • (withstu) update dependencies
  • (withstu) add admin 5 UI support
  • (withstu) improve preferred IP handling
  • (withstu) improve undefined station handling #299
  • (withstu) reduce upnp requests

2.1.0 (2023-08-05)

  • (withstu) replace got with axios
  • (withstu) improve upnp handling
  • (withstu) prevent duplicate connect messages

2.0.0 (2023-08-05)

  • (withstu) fix pipelines and remove node 14.x support

1.12.3 (2023-08-05)

  • (withstu) update dependencies

1.12.2 (2023-05-13)

  • (withstu) optimize error handling

1.12.1 (2023-02-26)

  • (withstu) optimize leader election

1.12.0 (2023-02-25)

  • (withstu) optimize scope handling
  • (withstu) switch to HEOS default cmd delimiter
  • (withstu) add configuration to prefer list of IPs for adapter connection
  • (withstu) optimize error handling

1.11.4 (2022-11-04)

  • (withstu) improve play all button in browse feature

1.11.3 (2022-11-04)

  • (withstu) update some dependencies
  • (withstu) improve failure handling
  • (withstu) improve play all button in browse feature

1.11.2 (2022-10-16)

  • (withstu) adopt to new adapter structure

1.11.1 (2022-10-16)

  • (withstu) fix release

1.11.0 (2022-10-16)

  • (withstu) improve player failure detection
  • (withstu) fix bug in regex mute
  • (withstu) fix upnp NaN warning #192

1.10.0 (2022-06-16)

  • (foxriver76) fix default value of sid (closes #174)

1.9.2 (2022-01-22)

  • (withstu) add volume lock

1.9.1 (2021-08-17)

  • (withstu) fix type issues
  • (withstu) fix roles and repeat state

1.9.0 (2021-07-27)

  • (stephanritscher) add option to configure udp source port

1.8.6 (2021-06-13)

  • (withstu) test fixed pipeline

1.8.4 (2021-06-13)

  • (withstu) improve stability

1.8.3 (2021-05-13)

  • (withstu) fix upnp values on failure

1.8.2 (2021-05-12)

  • (withstu) BREAKING: add queue paging
  • (withstu) BREAKING: volume_max -> volume_limit
  • (foxriver76) Fix type issues and some more minor changes

1.8.1 (2021-05-07)

  • (withstu) fix reboot loop

1.8.0 (2021-04-24)

  • (withstu) add reboot on failure configuration

1.7.9 (2021-04-07)

  • (withstu) fix reboot
  • (withstu) add power state

1.7.8 (2021-04-05)

  • (withstu) add reboot

1.7.7 (2021-02-25)

  • (withstu) add creation of missing version state

1.7.6 (2021-02-24)

  • (withstu) add image color extraction

1.7.5 (2021-02-12)

  • (withstu) add bit depth

1.7.4 (2021-02-01)

  • (withstu) fix upnp init bug

1.7.3 (2021-02-01)

  • (withstu) add upnp module and support bitrate, audio format and sample rate

1.7.2 (2021-01-30)

  • (withstu) fix seek in groups

1.7.1 (2021-01-30)

  • (withstu) add seek

1.7.0 (2021-01-29)

  • (withstu) reboot not responding players
  • (withstu) delete old presets and playlists

1.6.2 (2021-01-02)

  • (withstu) fix "user not logged in" handling

1.6.1 (2020-11-25)

  • (withstu) clear timeout and interval on unload; fix roles; remove sleep in tts module

1.6.0 (2020-11-22)

  • (withstu) add regex mute

1.5.6 (2020-11-22)

  • (withstu) add source images & optimize auto play

1.5.5 (2020-11-01)

  • (withstu) update some packages and add sources event

1.5.4 (2020-10-24)

  • (withstu) ignore invalid now playing responses

1.5.3 (2020-10-18)

  • (withstu) minor improvements related to auto play feature

1.5.2 (2020-10-11)

  • (withstu) improve tts stop method

1.5.1 (2020-10-11)

  • (withstu) improve tts and don't update queue during tts

1.5.0 (2020-10-10)

  • (withstu) add tts support and maximum volume

1.4.0 (2020-10-10)

  • (withstu) add more play and queue settings
  • (withstu) bugfixing for invalid heos responses (empty player name)

1.3.4 (2020-10-04)

  • (withstu) remove sorting and available filter and fix browse play

1.3.3 (2020-10-04)

  • (withstu) fix previous page button in browse feature

1.3.2 (2020-10-04)

  • (withstu) fix preset sorting

1.3.1 (2020-10-03)

  • (withstu) add back button to browse feature

1.3.0 (2020-10-03)

  • (withstu) add queue and some browse improvements

1.2.4 (2020-09-29)

  • (withstu) minor bugfix

1.2.3 (2020-09-29)

  • (withstu) improve browse feature (add pictures and sources view)

1.2.2 (2020-09-28)

  • (withstu) rename browse command

1.2.1 (2020-09-28)

  • (withstu) introduce browse_result state

1.2.0 (2020-09-27)

  • (withstu) Breaking change: restructure playlists/presets (you should delete the devices playlists, presets and sources before installation)

1.1.2 (2020-09-26)

  • (withstu) log browse parameters

1.1.1 (2020-09-26)

  • (withstu) add source browse feature (Click the button in the sources. You can find the possible next commands in the log.)

1.1.0 (2020-09-26)

  • (withstu) encrypt password

1.0.1 (2020-09-21)

  • (withstu) remove connected state, because it is included in the info channel

1.0.0 (2020-09-21)

  • (withstu) initial release

License

MIT License

Copyright (c) 2024 withstu [email protected]

derived from https://forum.iobroker.net/topic/10420/vorlage-denon-heos-script by Uwe Uhula TTS derived from https://github.com/ioBroker/ioBroker.sonos

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

iobroker.heos's People

Contributors

dependabot[bot] avatar foxriver76 avatar stephanritscher avatar withstu avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

iobroker.heos's Issues

HEOS responded with invalid data.

Hello,

i upgraded js-controller to 3.3.15 und the admin adapter to 5.1.23. As a result, I get the error message sporadically:

2021-09-13 05:36:52.749 error [startPlayers] HEOS responded with invalid data.

In fact all works fine.
Perhaps there is a possibility to remedy this error.

Wrong object type

Describe the bug
Since js-controller version >4.0 following warning appears in the log:
Object heos..sources.***.sid is invalid: Default value has to be type "number" but received type "string"

To Reproduce
Restart the adapter.

Expected behavior
Value of object needs to be type "number".

Screenshots & Logfiles
grafik

Versions:

  • Adapter version: 1.9.1
  • JS-Controller version: 4.0.21
  • Node version: v14.18.3
  • Operating system: Raspbian GNU/Linux 10 (buster)

Adapter no connection after some days

After some days the adapter cannot control speakers anymore as it says not connected to speaker. After adapter restart it immediately works again

v1.10.0

Release script: Action required

Hi, it looks like you are using @alcalzone/release-script to manage your releases.
When updating to the latest version, you need to remove the following line from your .github/workflows/test-and-release.yml if you want the releases to keep working:

  deploy:
    # Trigger this step only when a commit on master is tagged with a version number
    if: |
      contains(github.event.head_commit.message, '[skip ci]') == false &&
      github.event_name == 'push' &&
-     github.event.base_ref == 'refs/heads/master' &&
      startsWith(github.ref, 'refs/tags/v')

It may also look like this one:

  deploy:
    # Trigger this step only when a commit on master is tagged with a version number
    if: |
      contains(github.event.head_commit.message, '[skip ci]') == false &&
      github.event_name == 'push' &&
-     github.event.base_ref == 'refs/heads/main' &&
      startsWith(github.ref, 'refs/tags/v')

Wrong datatypes or missing conversions with 1.8.0

2021-05-11 13:16:51.106  - warn: heos.0 (2054) Read-only state "heos.0.error" has been written without ack-flag with value "true"
--
2021-05-11 13:16:51.357  - info: heos.0 (2054) connect HEOS player DenonWZoben (-1927616611)
2021-05-11 13:16:51.386  - warn: heos.0 (2054) State "heos.0.players.-1927616611.state" has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.386  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.state_simple" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.396  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_sid" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.397  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_source_name"  has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.397  - warn:  heos.0 (2054) State  "heos.0.players.-1927616611.current_source_image_url" has no existing  object, this might lead to an error in future versions
2021-05-11 13:16:51.397  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_type" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.398  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_station" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.398  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_title" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.398  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_album" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.398  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_album_id" has  no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.399  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_artist" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.399  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_image_url" has  no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.399  - warn:  heos.0 (2054) State  "heos.0.players.-1927616611.current_image_color_palette" has no existing  object, this might lead to an error in future versions
2021-05-11 13:16:51.399  - warn:  heos.0 (2054) State  "heos.0.players.-1927616611.current_image_color_background" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.399  - warn:  heos.0 (2054) State  "heos.0.players.-1927616611.current_image_color_foreground" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.400  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_mid" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.426  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_qid" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.431  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_bitrate" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.431  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_bitdepth" has  no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.431  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_sample_rate"  has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.431  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.current_audio_format"  has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.435  - warn:  heos.0 (2054) Read-only state "heos.0.sources.browse_result" has been  written without ack-flag with value  "{"name":"sources","image_url":"","parameter":{},"payload":[{"name":"Local   Music","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_servers.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1024"}},{"name":"Playlists","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1025"}},{"name":"History","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_history.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1026"}},{"name":"AUX   Input","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_aux.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1027"}},{"name":"Favorites","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_favorites.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1028"}}]}"
2021-05-11 13:16:51.445  - warn:  heos.0 (2054) State  "heos.0.players.-1927616611.current_allowed_actions" has no existing  object, this might lead to an error in future versions
2021-05-11 13:16:51.467  - info: heos.0 (2054) connect HEOS player Speaker Terrasse (543608195)
2021-05-11 13:16:51.470  - warn: heos.0 (2054) State "heos.0.players.-1927616611.queue" has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.476  - info: heos.0 (2054) State value to set for "heos.0.sources.1024.sid" has to be type "number" but received type "string"
2021-05-11 13:16:51.477  - info: heos.0 (2054) State value to set for "heos.0.sources.1025.sid" has to be type "number" but received type "string"
2021-05-11 13:16:51.477  - info: heos.0 (2054) State value to set for "heos.0.sources.1026.sid" has to be type "number" but received type "string"
2021-05-11 13:16:51.477  - info: heos.0 (2054) State value to set for "heos.0.sources.1027.sid" has to be type "number" but received type "string"
2021-05-11 13:16:51.477  - info: heos.0 (2054) State value to set for "heos.0.sources.1028.sid" has to be type "number" but received type "string"
2021-05-11 13:16:51.530  - warn:  heos.0 (2054) Read-only state "heos.0.sources.browse_result" has been  written without ack-flag with value  "{"name":"sources","image_url":"","parameter":{},"payload":[{"name":"Local   Music","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_servers.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1024"}},{"name":"Playlists","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1025"}},{"name":"History","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_history.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1026"}},{"name":"AUX   Input","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_aux.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1027"}},{"name":"Favorites","image_url":"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_favorites.png","type":"media","available":true,"commands":{"browse":"browse/browse?sid=1028"}}]}"
2021-05-11 13:16:51.546  - info: heos.0 (2054) disconnect HEOS player Speaker Terrasse (543608195)
2021-05-11 13:16:51.546  - warn: heos.0 (2054) Announced player not found by HEOS. Try to reboot device 192.168.178.79
2021-05-11 13:16:51.546  - info: heos.0 (2054) reconnecting to HEOS ...
2021-05-11 13:16:51.546  - info: heos.0 (2054) disconnecting from HEOS ...
2021-05-11 13:16:51.547  - info: heos.0 (2054) disconnect HEOS player Speaker Terrasse (543608195)
2021-05-11 13:16:51.547  - info: heos.0 (2054) disconnect HEOS player DenonWZoben (-1927616611)
2021-05-11 13:16:51.548  - info: heos.0 (2054) disconnected from HEOS
2021-05-11 13:16:51.555  - warn: heos.0 (2054) State "heos.0.players.543608195.error" has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.555  - warn: heos.0 (2054) State "heos.0.players.543608195.error" has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.555  - warn: heos.0 (2054) State "heos.0.players.-1927616611.error" has no existing object, this might lead to an error in future versions
2021-05-11 13:16:51.556  - warn: heos.0 (2054) Read-only state "heos.0.error" has been written without ack-flag with value "false"
2021-05-11 13:16:51.556  - warn: heos.0 (2054) Read-only state "heos.0.last_error" has been written without ack-flag with value ""
2021-05-11 13:16:51.556  - warn: heos.0 (2054) Read-only state "heos.0.signed_in" has been written without ack-flag with value "false"
2021-05-11 13:16:51.556  - warn: heos.0 (2054) Read-only state "heos.0.signed_in_user" has been written without ack-flag with value ""
2021-05-11 13:16:51.591  - warn:  heos.0 (2054) State "heos.0.players.543608195.last_error" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.592  - warn:  heos.0 (2054) State "heos.0.players.543608195.last_error" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:51.593  - warn:  heos.0 (2054) State "heos.0.players.-1927616611.last_error" has no  existing object, this might lead to an error in future versions
2021-05-11 13:16:52.072  - info: heos.0 (2054) State value to set for "heos.0.players.-1927616611.pid" has to be type "string" but received type "number"
2021-05-11 13:16:52.073  - info: heos.0 (2054) State value to set for "heos.0.players.-1927616611.pid" has to be type "string" but received type "number"
2021-05-11 13:16:52.131  - info: heos.0 (2054) State value to set for "heos.0.players.543608195.pid" has to be type "string" but received type "number"
2021-05-11 13:16:56.549  - info: heos.0 (2054) connecting to HEOS (192.168.178.79) ...
2021-05-11 13:16:56.554  - warn: heos.0 (2054) rebooting player 192.168.178.79
2021-05-11 13:16:56.699  - warn: heos.0 (2054) Read-only state "heos.0.players.543608195.connected" has been written without ack-flag with value "true"
2021-05-11 13:16:56.700  - info: heos.0 (2054) auto play default music at Speaker Terrasse
2021-05-11 13:16:57.556  - info: heos.0 (2054) reconnecting to HEOS ...
2021-05-11 13:16:57.556  - info: heos.0 (2054) disconnecting from HEOS ...
2021-05-11 13:16:57.557  - info: heos.0 (2054) disconnected from HEOS
2021-05-11 13:16:57.564  - warn: heos.0 (2054) Read-only state "heos.0.error" has been written without ack-flag with value "false"
2021-05-11 13:16:57.564  - warn: heos.0 (2054) Read-only state "heos.0.last_error" has been written without ack-flag with value ""
2021-05-11 13:16:57.564  - warn: heos.0 (2054) Read-only state "heos.0.signed_in" has been written without ack-flag with value "false"
2021-05-11 13:16:57.564  - warn: heos.0 (2054) Read-only state "heos.0.signed_in_user" has been written without ack-flag with value ""
2021-05-11 13:17:02.558  - info: heos.0 (2054) searching for HEOS devices ...
2021-05-11 13:17:02.561  - info: heos.0 (2054) connecting to HEOS (192.168.178.140) ...
2021-05-11 13:17:02.561  - info: heos.0 (2054) connected to HEOS (192.168.178.140)
2021-05-11 13:17:02.564  - info: heos.0 (2054) connect HEOS player DenonWZoben (-1927616611)
2021-05-11 13:17:02.569  - warn: heos.0 (2054) [setLastError] result=fail,text=Invalid credentials,command=system/sign_in
2021-05-11 13:17:02.577  - warn: heos.0 (2054) Read-only state "heos.0.error" has been written without ack-flag with value "true"
2021-05-11 13:17:02.645  - info: heos.0 (2054) connect HEOS player Speaker Terrasse (543608195)
2021-05-11 13:17:02.664  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:02.670  - info: heos.0 (2054) State value to set for "heos.0.players.-1927616611.volume" has to be type "number" but received type "string"
2021-05-11 13:17:02.814  - info: heos.0 (2054) State value to set for "heos.0.players.-1927616611.pid" has to be type "string" but received type "number"
2021-05-11 13:17:02.834  - info: heos.0 (2054) State value to set for "heos.0.players.543608195.pid" has to be type "string" but received type "number"
2021-05-11 13:17:07.646  - warn: heos.0 (2054) Read-only state "heos.0.players.-1927616611.connected" has been written without ack-flag with value "true"
2021-05-11 13:17:07.648  - info: heos.0 (2054) auto play music at DenonWZoben
2021-05-11 13:17:08.870  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:09.422  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:09.434  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:09.939  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:10.354  - info: heos.0 (2054) State value to set for "heos.0.players.-1927616611.volume" has to be type "number" but received type "string"
2021-05-11 13:17:13.062  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:13.793  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:14.759  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:14.984  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"
2021-05-11 13:17:15.490  - info:  heos.0 (2054) State value to set for  "heos.0.players.-1927616611.current_sid" has to be type "string" but  received type "number"

If you have questions or need help, let me know.

type issue

State value to set for "heos.0.players.12345678.group_volume" has to be type "number" but received type "string"

Update stable version in repo from 2.1.0 to 2.2.1

Think about update stable version to 2.2.1

Version: stable=2.1.0 (177 days old) => latest=2.2.1 (15 days old)
Installs: stable=740 (69.09%), latest=67 (6.26%), total=1071

Click to use developer portal
Click to edit

Note: This is an automatically generated message and not personally authored by bluefox!
Do not close this issue manually as a new issue will be created if condition for update still exists
@mcm1957 for evidence

Heos Widget VIS2

Hello, I have tried to get the Heos widget to work in VIS 02. Unfortunately buttons are not displayed correctly and have no function. Will these be adapted? see also the forum. https://forum.iobroker.net/topic/73347/heos-mit-vis-2/2?_=1710258088960

Hallo ich habe versucht das Heos Widget in VIS 02 zum laufen zu bringen. Leider werden Button nicht richtig dargestellt und haben keine Funktion. Werden diese angepasst? siehe auch das Forum. https://forum.iobroker.net/topic/73347/heos-mit-vis-2/2?_=1710258088960](url)

run specific tune in radio-station

Thanks for coding that adapter. I want to use that adapter together with an nspanel. I only have some buttons there that i can use and i want directly start a radio station via button hit. At the moment i can switch the receiver on/off and change the volume and i can play the last tuned station but i cant switch the station as i dont know what id/information the denon needs to do that task. It there any chance to use your adapter for that?
Regards,
DaPeace

Start HEOS Music with a music station

Hi,

is it possible to start the HEOS Adapter with my favorite Music Station?

In the HEOS App I can choose it manually e.g. TuneIn and then he is playing it.

But I want to make a blockly over IOBroker where I give him a default input when my wake up routine is starting

Thank you

Error in Log

Describe the bug
Somtimes I found an error in the logs

To Reproduce
I have nothing found to reproduce.

Expected behavior
Mostly once a day I get this error message in the log. The Heos 5 is started via a blokly script with the command "play" and volume "17". This seems to be where the error occurs. But unfortunately not always. My IoBroker is shuting down at night so that a backup of the VM (Bookworm) can be created. I have recently updated to js-controller 5.12 and since then this error message. The adapter reconnected after the message then works normally until the next day.

Screenshots & Logfiles

2023-10-04 05:33:41.841 - error: heos.0 (798) Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().

2023-10-04 05:33:41.841 - error: heos.0 (798) unhandled promise rejection: undefined is not a valid state value
2023-10-04 05:33:41.842 - error: heos.0 (798) Error: undefined is not a valid state value
at Object.maybeCallbackWithError (/opt/iobroker/node_modules/@iobroker/js-controller-common/build/lib/common/maybeCallback.js:35:17)
at Heos._setState (/opt/iobroker/node_modules/@iobroker/js-controller-adapter/build/lib/adapter/adapter.js:5439:49)
at Heos.setState (/opt/iobroker/node_modules/@iobroker/js-controller-adapter/build/lib/adapter/adapter.js:5400:21)
at HeosPlayer.parseResponse (/opt/iobroker/node_modules/iobroker.heos/lib/heos-player.js:1826:21)
at Heos.parseResponse (/opt/iobroker/node_modules/iobroker.heos/main.js:1814:17)
at Heos.onData (/opt/iobroker/node_modules/iobroker.heos/main.js:749:14)
at Socket. (/opt/iobroker/node_modules/iobroker.heos/main.js:2442:45)
at Socket.emit (node:events:517:28)
at addChunk (node:internal/streams/readable:335:12)
at readableAddChunk (node:internal/streams/readable:308:9)
at Readable.push (node:internal/streams/readable:245:10)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
2023-10-04 05:33:41.843 - error: heos.0 (798) undefined is not a valid state value
2023-10-04 05:33:41.843 - info: heos.0 (798) disconnecting from HEOS ...
2023-10-04 05:33:41.844 - info: heos.0 (798) [Buero - Heos 5] disconnect HEOS player Buero - Heos 5 (141082291)
2023-10-04 05:33:41.981 - info: heos.0 (798) [Denon AVR-X4400H] disconnect HEOS player Denon AVR-X4400H (347439546)
2023-10-04 05:33:42.121 - info: heos.0 (798) [Kueche - Heos 5] disconnect HEOS player Kueche - Heos 5 (1570209935)
2023-10-04 05:33:42.214 - info: heos.0 (798) [Garage - Heos 1] disconnect HEOS player Garage - Heos 1 (-175607758)
2023-10-04 05:33:42.305 - info: heos.0 (798) [Dachboden - Heos 1] disconnect HEOS player Dachboden - Heos 1 (-1801497287)
2023-10-04 05:33:42.343 - info: heos.0 (798) terminating
2023-10-04 05:33:42.343 - warn: heos.0 (798) Terminated (UNCAUGHT_EXCEPTION): Without reason
2023-10-04 05:33:42.489 - info: heos.0 (798) disconnected from HEOS
2023-10-04 05:33:42.859 - error: host.ioBroker-MA instance system.adapter.heos.0 terminated with code 6 (UNCAUGHT_EXCEPTION)
2023-10-04 05:33:42.859 - info: host.ioBroker-MA Restart adapter system.adapter.heos.0 because enabled

Versions:

  • Adapter version: 2.1.0
  • JS-Controller version: 5.12
  • Node version: 9.8.1 / 18.18.0
  • Operating system: Proxmox VM debian (bookworm)

Think about to fix the issues found by adapter checker

I am an automatic service that looks for possible errors in ioBroker and creates an issue for it. The link below leads directly to the test:

https://adapter-check.iobroker.in/?q=https://raw.githubusercontent.com/withstu/ioBroker.heos

  • [E701] No actual year found in LICENSE. Please add "Copyright (c) 2022 withstu [email protected]" at the start of LICENSE

Thanks,
your automatic adapter checker.

P.S.: There is a community in Github, which supports the maintenance and further development of adapters. There you will find many experienced developers who are always ready to assist anyone. New developers are always welcome there. For more informations visit: https://github.com/iobroker-community-adapters/info

Grouping and ungrouping

Bug description
I have two Heos1, one Heos5 and aDenos X1700(surrond with Heos) that I want to group using the Heos Adapter. I can then eg group the (Heos1+Heos1) then (Heos1+Heos1)+Heos5 and finally (Heos1+Heos1+Heos5) + X1700. The grouping is also reflected in the Heos iOS app.

To Reproduce
I can group in the following order: (Heos1+Heos1) then (Heos1+Heos1)+Heos5 and finally (Heos1+Heos1+Heos5) + X1700. The grouping is also reflected in the Heos iOS app. I then want to remove the X1700 from the group, but it stays in the group. I can see on the app that the X1700 actually jumps ouf the group, but the immidiately back.
The ioBroker log says: [setLastError] result=fail,text=Processing previous command,command=group/set_group

Expected behavior
When trying to remove the X1700 I would expect it to get out of the group. I can remove the X1700 from the group using the iOS app.

Versions:

  • Adapter version: 10.1
  • Node version: v16.16
  • Operating system: Win10

Command Befehl funktioniert nicht

Hi,

wenn ich einen Command Befehl eingebe passiert leider nichts am Denon.

z.b.

play_preset&preset=2

Das Logfile im Debug Modus gibt auch nicht viel aus leider:

heos.0 2022-10-30 11:41:04.849 debug [HEARTBEAT] pong
heos.0 2022-10-30 11:41:04.848 debug parseResponse: {"heos": {"command": "system/heart_beat", "result": "success", "message": ""}}
heos.0 2022-10-30 11:41:04.847 debug onData: {"heos": {"command": "system/heart_beat", "result": "success", "message": ""}}
heos.0 2022-10-30 11:41:04.844 debug data sent: heos://system/heart_beat
heos.0 2022-10-30 11:41:04.844 debug [HEARTBEAT] ping

Versions:

  • Adapter version: 1.10.0
  • JS-Controller version: 4.0.23
  • Node version: 16.18
  • Operating system: Synology Docker

Hier noch ein paar Screenshots:

Bildschirm­foto 2022-10-30 um 10 45 57

Bildschirm­foto 2022-10-30 um 10 45 47

Bildschirm­foto 2022-10-30 um 10 01 08

Compatibility check to js-controller 4.0

Dear Adapter developer,

with js-controller 4.0 object definitions are now also checked that min/max in only provided for number/mixed objects and that the type of the default value matches to the object type.

If something is not correct this is logged as 'warning' or 'info' log.

Please also make sure to update to the lastest @iobroker/testing dependency 2.5.4 or to accept the PR from Apollon77 for legacy testing!

Please spent some time to verify your adapter by ideally starting with a fresh instance and do some actions and verify the log. If you see a warn or info log there from these checks please adjust the adapter and fix the relevant cases.

For questions please refer to ioBroker/ioBroker.js-controller#1749

Please close the issue after you checked it.

Thank you very much for your support to get the best experience for the growing numbers of ioBroker users!

HDMI CEC

Hi,

ist es möglich einen speziellen HDMI Eingang anzusteuern per Befehl?

Ich frage deshalb weil dann dank CEC ja auch der TV mit angeht, was eine gute Lösung wäre..

Da ich diesen sonst nicht anders anbekomme z.b. über Alexa oder ähnliches.

Und Sobald ich den Denon anmache geht automatisch der TV eben auch an.

Wäre dankbar für Eure Hilfe.

VG

Question: How-To continue music after ungroup

Hi @withstu,

I have a little question, which is probably not an adapter issue.

I am currently running some kind of dynamic multiroom setup, whenever the main AVR is active and someone enters the bathroom, the audio follows. However, I then also added the step to activate the dynamic MR setup if someone turns on the main AVR, while someone is currently in the bathroom, here the problem comes into the play.

I start grouping, then start changing the input source accordingly, that works well. However, if then the person leaves the bathroom and I ungroup, the audio also stops completely on the main AVR where I streamed too. It seems like when you start streaming to a group, you cannot put the last member out of the group, else it stops playing.

For completeness, the whole script:

/**
 * If someone enters bathroom and AVR is playing, mirror it to bathroom
 */

/**
 * If one of these states is true, presence is assumed
 */
const LIGHT_IDS = [
    'alias.0.bathroom.switches.ceilingSpots.switch',
    'alias.0.bathroom.switches.mirrorCabinet.switch'
];

/** Volume mapping to AVR inputs */
const VOLUME_MAPPING = {
    /** For Netflix, Prime etc. */
    TV: 42,
    /** For Xbox */
    GAME: 24,
    /** For music */
    NET: 12
} as const;

/** The fallback volume for unknown inputs */
const FALLBACK_VOLUME = 28;

const AVR_PID = '-1280216177';
const SPEAKER_BATH_PID = '543608195';
const HEOS_AVR_CMD_ID = `heos.0.players.${AVR_PID}.command`;
const VOLUME_SPEAKER_ID = `heos.0.players.${SPEAKER_BATH_PID}.volume`;
const SPEAKER_IS_PLAYING_ID = `heos.0.players.${SPEAKER_BATH_PID}.state_simple`;
const AVR_IS_PLAYING_ID = `heos.0.players.${AVR_PID}.state_simple`;
const AVR_POWER_ID = 'denon.0.settings.powerSystem';
const AVR_HEOS_POWER_ID = `heos.0.players.${AVR_PID}.power`;
const AVR_INPUT_ID = 'denon.0.zoneMain.selectInput';
const AVR_GROUP_MBRS_ID = `heos.0.players.${AVR_PID}.group_pid`;

const INPUT_TV_CMD = 'inputs/tvaudio';
const INPUT_XBOX_CMD = 'inputs/game';

const INDICATOR_STATE_ID = '0_userdata.0.bathroom.speaker.dynamicMultiroomActive';

/** The input AVR has when playing Heos */
const AVR_HEOS_INPUT = 'NET';

(async () => {
    await createObjects();
})();

on(LIGHT_IDS, async obj => {
    const otherLightId = LIGHT_IDS.find(id => id !== obj.id);
    const otherLightState = await getStateAsync(otherLightId);

    let isPresent = false;
    if (!otherLightState.val && !obj.state.val) {
        isPresent = false;
    } else if (obj.state.val) {
        isPresent = true;
    } else {
        // already present
        return;
    }

    if (!isPresent) {
        if (!(await isDynamicMultiroomActive())) {
            log('Someone left bathroom, but not turned on by script', 'info');
            return;
        }

        await setStateAsync(INDICATOR_STATE_ID, false, true);
        // ungroup if grouped via script
        log('Someone left bathroom, ensure ungrouped', 'info');
        const groupMembers = (await getStateAsync(AVR_GROUP_MBRS_ID))?.val ?? '';
        
        await removeFromGroup(SPEAKER_BATH_PID, groupMembers)
        return;
    }

    const isPlayingState = await getStateAsync(SPEAKER_IS_PLAYING_ID);

    if (isPlayingState.val) {
        log('Someone entered bathroom, but bath speaker already playing', 'info');
        return;
    }
    
    // group if AVR is on
    const avrState = await getStateAsync(AVR_POWER_ID);
    // TODO we test here
    const heosAvrState = await getStateAsync(AVR_HEOS_POWER_ID);

    if (!avrState.val) {
        if (heosAvrState.val) {
            log('HEOS on but AVR off', 'warn');
        }
        // AVR off, no need to group
        log('Someone entered bathroom but AVR off, do nothing', 'info');
        return;
    }

    if (!heosAvrState.val) {
        log('AVR on but off in HEOS', 'warn');
    }

    log('Someone entered bathroom and AVR on, group', 'info');

    await activateMultiroom();
});

/**
 * If input changes while playing multiroom
 */
on({ id: AVR_INPUT_ID, ack: true }, async obj => {
    if (!(await isDynamicMultiroomActive())) {
        return;
    }

    await setHeosInput(obj.state.val);
    await setHeosVolume(obj.state.val);
});

/**
 * If AVR is turned on while someone in bathroom, turn on multiroom
 */
on({ id: AVR_POWER_ID, ack: true, val: true }, async () => {
    if ((await isDynamicMultiroomActive())) {
        log('Received AVR power on while already activated by script, ignore', 'info');
        return;
    }

    const isPresent = await isSomeoneInBath();

    if (!isPresent) {
        log('AVR turned on but nobody in bath', 'info');
        return;
    }

    const isPlayingState = await getStateAsync(SPEAKER_IS_PLAYING_ID);

    if (isPlayingState.val) {
        log('AVR turned on while in bath, but speaker already playing', 'info');
        return;
    }

    log('AVR turned on while in bath, group', 'info');

    await activateMultiroom();
});

/**
 * Activates multiroom on the correct input
 */
async function activateMultiroom(): Promise<void> {
    await setStateAsync(INDICATOR_STATE_ID, true, true);

    const avrInput = (await getStateAsync(AVR_INPUT_ID))?.val;

    await setHeosInput(avrInput);
    await setHeosVolume(avrInput);

    const groupMembers = (await getStateAsync(AVR_GROUP_MBRS_ID))?.val ?? '';
    await addToGroup(AVR_PID, SPEAKER_BATH_PID, groupMembers);
}

/**
 * Remove speaker from the given group
 * 
 * @param oldMemberPid the member to remove from the group
 * @param groupMembers the current group members in csv format
 */
async function removeFromGroup(oldMemberPid: string, groupMembers: string): Promise<void> {
    if (!groupMembers) {
        log(`Cannot ungroup "${oldMemberPid}", because not grouped`, 'warn');
        return;
    }

    const desiredPids = [];
    const currentPids: string[] = groupMembers.split(',');

    const memberPids = currentPids.filter(pid => pid !== oldMemberPid);
    desiredPids.push(...memberPids);

    log(`Set group to ${desiredPids.join(',')}`, 'info');
    
    await setStateAsync('heos.0.command', `group/set_group?pid=${desiredPids.join(',')}`, false);
}

/**
 * Add speaker to the current group of the leader
 * if not yet grouped a group will be created
 * 
 * @param leaderPid the leader of the group
 * @param newMemberPid the member to add to the group
 * @param groupMembers the current group members in csv format
 */
async function addToGroup(leaderPid: string, newMemberPid: string, groupMembers: string): Promise<void> {
    const desiredPids = [leaderPid, newMemberPid];

    if (groupMembers) {
        const currentPids: string[] = groupMembers.split(',');

        const memberPids = currentPids.filter(pid => pid !== leaderPid && pid !== newMemberPid);
        desiredPids.push(...memberPids);
    }

    log(`Set group to ${desiredPids.join(',')}`, 'info');
    
    await setStateAsync('heos.0.command', `group/set_group?pid=${desiredPids.join(',')}`, false);
}

/**
 * Sets Heos volume according to AVR input
 * 
 * @param avrInput current AVR input
 */
async function setHeosVolume(avrInput?: string): Promise<void> {
    if (avrInput in VOLUME_MAPPING) {
        log(`Set ${avrInput} volume`, 'info');
        await setStateAsync(VOLUME_SPEAKER_ID, VOLUME_MAPPING[avrInput], false);
    } else {
        log(`Unknown input ${avrInput} set fallback volume`, 'info');
        await setStateAsync(VOLUME_SPEAKER_ID, FALLBACK_VOLUME, false);
    }
}

/**
 * Sets Heos input by AVR input
 * 
 * @param avrInput current AVR input
 */
async function setHeosInput(avrInput?: string): Promise<void> {
    const avrIsPlayingState = await getStateAsync(AVR_IS_PLAYING_ID);

    if (avrInput === AVR_HEOS_INPUT) {
        log('AVR has changed to HEOS, no input change necessary', 'info');
        return;
    }

    if (!avrIsPlayingState.val) {
        const desiredInput = avrInput === 'GAME' ? INPUT_XBOX_CMD : INPUT_TV_CMD;
        log(`Set ${desiredInput}`, 'info');
        // AVR not playing via HEOS, so it should be TV as source, else source is already active
        await setStateAsync(HEOS_AVR_CMD_ID, `play_input&input=${desiredInput}`, false);
    }
}

/**
 * Checks if someone is in bathroom by light indicators
 */
async function isSomeoneInBath(): Promise<boolean> {
    for (const lightId of LIGHT_IDS) {
        const val = (await getStateAsync(lightId))?.val;
        if (val) {
            return true;
        }
    }

    return false;
}

/**
 * Checks if the speaker is currently on due to this script
 */
async function isDynamicMultiroomActive(): Promise<boolean> {
    const res = await getStateAsync(INDICATOR_STATE_ID);
    return !!res?.val;
}

/**
 * Creates necessary objects for this script
 */
async function createObjects(): Promise<void> {
    await extendObjectAsync(INDICATOR_STATE_ID, {
        type: 'state',
        common: {
            name: 'Dynamic Multiroom Active',
            type: 'boolean',
            read: true, 
            write: false,
            role: 'indicator',
            desc: 'If true the speaker is currently on due to presence'
        }, 
        native: {}
    });
}

stuck at "searching for HEOS devices ..."

Hello I try to implement your adapter but after creating an instance, it stucks at "searching for HEOS devices ..." (log).

I have an denon av receiver, one heos 150 speaker, iobroker installed in a docker-container (but "network_mode: host" because of the multicast for heos) and also loaded the node module node-ssdp within the javascript.0-instance. What else can I do? Can you help me?

Thanks in advance

Denon Home 350 with HEOS not working

Dear Developer,

thanks for developing the HEOS Plugin for IOBroker. I installed it but it seems that it does not support my Denon Home 350 with HEOS. The PLugin does not find the speaker at all. Is it more a technical issue on my side or is the speaker not supported yet?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.