Background task running, this page will reload when it's done.
├── .gitignore
├── .storage
└── lovelace
├── README.md
├── automations.yaml
├── camera.yaml
├── configuration.yaml
├── custom_components
├── __pycache__
│ └── custom_updater.cpython-37.pyc
├── alexa_media
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-37.pyc
│ │ ├── alarm_control_panel.cpython-37.pyc
│ │ ├── const.cpython-37.pyc
│ │ ├── media_player.cpython-37.pyc
│ │ └── notify.cpython-37.pyc
│ ├── alarm_control_panel.py
│ ├── const.py
│ ├── manifest.json
│ ├── media_player.py
│ ├── notify.py
│ └── services.yaml
├── custom_updater.py.bak
├── customizer
│ ├── __init__.py
│ ├── __pycache__
│ │ └── __init__.cpython-37.pyc
│ └── services.yaml
├── hacs
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-37.pyc
│ │ ├── api.cpython-37.pyc
│ │ ├── const.cpython-37.pyc
│ │ ├── http.cpython-37.pyc
│ │ └── sensor.cpython-37.pyc
│ ├── aiogithub
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ ├── aiogithub.cpython-37.pyc
│ │ │ ├── aiogithubrepository.cpython-37.pyc
│ │ │ ├── aiogithubrepositorycontent.cpython-37.pyc
│ │ │ ├── aiogithubrepositoryrelease.cpython-37.pyc
│ │ │ ├── const.cpython-37.pyc
│ │ │ └── exceptions.cpython-37.pyc
│ │ ├── aiogithub.py
│ │ ├── aiogithubrepository.py
│ │ ├── aiogithubrepositorycontent.py
│ │ ├── aiogithubrepositoryrelease.py
│ │ ├── const.py
│ │ └── exceptions.py
│ ├── api.py
│ ├── const.py
│ ├── frontend
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ └── __init__.cpython-37.pyc
│ │ ├── elements
│ │ │ ├── all.min.css.gz
│ │ │ ├── hacs.css
│ │ │ ├── hacs.js
│ │ │ ├── materialize.min.css.gz
│ │ │ ├── materialize.min.js.gz
│ │ │ └── webfonts
│ │ │ │ ├── fa-brands-400.eot.gz
│ │ │ │ ├── fa-brands-400.svg.gz
│ │ │ │ ├── fa-brands-400.ttf.gz
│ │ │ │ ├── fa-brands-400.woff.gz
│ │ │ │ ├── fa-brands-400.woff2.gz
│ │ │ │ ├── fa-regular-400.eot.gz
│ │ │ │ ├── fa-regular-400.svg.gz
│ │ │ │ ├── fa-regular-400.ttf.gz
│ │ │ │ ├── fa-regular-400.woff.gz
│ │ │ │ ├── fa-regular-400.woff2.gz
│ │ │ │ ├── fa-solid-900.eot.gz
│ │ │ │ ├── fa-solid-900.svg.gz
│ │ │ │ ├── fa-solid-900.ttf.gz
│ │ │ │ ├── fa-solid-900.woff.gz
│ │ │ │ └── fa-solid-900.woff2.gz
│ │ └── templates
│ │ │ ├── base.html
│ │ │ ├── error.html
│ │ │ ├── grid.html
│ │ │ ├── message.html
│ │ │ ├── modal
│ │ │ ├── upgrade_all.html
│ │ │ └── wrong_ha_version.html
│ │ │ ├── overviews.html
│ │ │ ├── repository.html
│ │ │ ├── repository
│ │ │ ├── buttons.html
│ │ │ ├── menu.html
│ │ │ ├── note.html
│ │ │ └── versionselect.html
│ │ │ ├── settings.html
│ │ │ ├── settings
│ │ │ ├── buttons.html
│ │ │ ├── custom_repositories.html
│ │ │ ├── dev
│ │ │ │ ├── remove_new_flag.html
│ │ │ │ ├── repositories.html
│ │ │ │ ├── repository.html
│ │ │ │ ├── set_ha_version.html
│ │ │ │ └── token.html
│ │ │ ├── developer.html
│ │ │ ├── hacs_info.html
│ │ │ └── hidden_repositories.html
│ │ │ └── table.html
│ ├── hacsbase
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ ├── configuration.cpython-37.pyc
│ │ │ ├── const.cpython-37.pyc
│ │ │ ├── data.cpython-37.pyc
│ │ │ ├── exceptions.cpython-37.pyc
│ │ │ └── migration.cpython-37.pyc
│ │ ├── configuration.py
│ │ ├── const.py
│ │ ├── data.py
│ │ ├── exceptions.py
│ │ └── migration.py
│ ├── handler
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ ├── download.cpython-37.pyc
│ │ │ ├── logger.cpython-37.pyc
│ │ │ └── template.cpython-37.pyc
│ │ ├── download.py
│ │ ├── logger.py
│ │ └── template.py
│ ├── http.py
│ ├── manifest.json
│ ├── repositories
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ ├── hacsrepositoryappdaemon.cpython-37.pyc
│ │ │ ├── hacsrepositorybase.cpython-37.pyc
│ │ │ ├── hacsrepositorybaseplugin.cpython-37.pyc
│ │ │ ├── hacsrepositoryintegration.cpython-37.pyc
│ │ │ ├── hacsrepositorypythonscript.cpython-37.pyc
│ │ │ ├── hacsrepositorytheme.cpython-37.pyc
│ │ │ ├── repositoryinformation.cpython-37.pyc
│ │ │ └── repositoryinformationview.cpython-37.pyc
│ │ ├── hacsrepositoryappdaemon.py
│ │ ├── hacsrepositorybase.py
│ │ ├── hacsrepositorybaseplugin.py
│ │ ├── hacsrepositoryintegration.py
│ │ ├── hacsrepositorypythonscript.py
│ │ ├── hacsrepositorytheme.py
│ │ ├── repositoryinformation.py
│ │ └── repositoryinformationview.py
│ ├── sensor.py
│ └── services.yaml
└── media_player
│ ├── alexa.py
│ └── ps4.py
├── customize.yaml
├── device_tracker.yaml
├── floorplan
└── floorplan.jpg
├── groups.yaml
├── images
├── 01_Lovelace.png
├── 02_Data.png
├── 03_RPi.png
├── 04_Home.png
├── 05_Bathroom.png
├── 06_Kitchen.png
├── 07_Bedroom.png
├── Lovelace.gif
├── Phone.png
├── Phone_01.png
└── Phone_02.png
├── media_player.yaml
├── remote.yaml
├── scripts.yaml
├── sensor.yaml
├── switch.yaml
├── themes.yaml
├── ui_lovelace.yaml
├── www
├── Lovelace
│ ├── floorplan10.png
│ ├── floorplan7.png
│ ├── floorplan_grey.png
│ ├── floorplan_grey2.png
│ ├── freezer_closed2.png
│ ├── freezer_open2.png
│ ├── fridge_closed2.png
│ ├── fridge_open2.png
│ ├── tree_off.png
│ ├── tree_on.png
│ └── unlock.png
├── Mobile
│ ├── Bathroom.jpg
│ ├── Bedroom.jpg
│ ├── Entrance.jpg
│ ├── Kitchen.jpg
│ ├── Living_Room.jpg
│ └── Living_Room_2.jpg
├── community
│ ├── calendar-card
│ │ ├── calendar-card.js
│ │ ├── calendar-card.js.gz
│ │ └── calendar-card.js.map
│ ├── compact-custom-header
│ │ ├── compact-custom-header-editor.js
│ │ ├── compact-custom-header-editor.js.gz
│ │ ├── compact-custom-header.js
│ │ └── compact-custom-header.js.gz
│ ├── mini-graph-card
│ │ ├── mini-graph-card.js
│ │ └── mini-graph-card.js.gz
│ ├── mini-media-player
│ │ ├── mini-media-player.js
│ │ └── mini-media-player.js.gz
│ ├── radial-menu
│ │ ├── radial-menu.js
│ │ └── radial-menu.js.gz
│ └── simple-thermostat
│ │ ├── simple-thermostat.js
│ │ └── simple-thermostat.js.gz
└── custom-cards
│ ├── calendar-card.js
│ ├── mini-graph-card-bundle.js
│ ├── mini-media-player-bundle.js
│ ├── plan-coordinates.js
│ ├── radial-menu.js
│ └── simple-thermostat.js
└── zone.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Example .gitignore file for your config dir.
2 | # A * ensures that everything will be ignored.
3 | *
4 |
5 | # You can whitelist files/folders with !, these will not be ignored.
6 | !*.yaml
7 | !.gitignore
8 | !*.md
9 | !floorplan
10 | !custom-components/
11 | !custom-components/*
12 | !www
13 | !www/*
14 | !www/custom-cards/
15 | !www/custom-cards/*
16 | !scripts
17 | !*.jpg
18 | !*.svc
19 | !*.png
20 | !*.gif
21 | !.storage/lovelace
22 | !images/
23 | !images/*
24 |
25 | # Ignore folders.
26 | .storage
27 | .cloud
28 | .google.token
29 |
30 | # Ensure these YAML files are ignored, otherwise your secret data/credentials will leak.
31 | google_calendars.yaml
32 | ip_bans.yaml
33 | secrets.yaml
34 | known_devices.yaml
35 | node-red/flows_cred.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 | 
6 |
7 | 
8 |
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | 
16 |
17 | 
18 |
--------------------------------------------------------------------------------
/automations.yaml:
--------------------------------------------------------------------------------
1 | ## Notify of High Disk Use - credit to @sauloonzem thank you
2 | - alias: Disk Use Alarm
3 | initial_state: 'on'
4 | trigger:
5 | platform: numeric_state
6 | entity_id: sensor.disk_use
7 | above: 12
8 | action:
9 | - service: nnotify.mobile_app_mikes_iphone_xs
10 | data:
11 | title: 'HA Server'
12 | message: 'Warning - HA Server Disk Use is {{states.sensor.disk_use.state}}GB'
13 |
14 | ## Notify of High CPU Usage - credit to @sauloonzem thank you
15 | - alias: CPU Use Alarm
16 | initial_state: 'on'
17 | trigger:
18 | platform: numeric_state
19 | entity_id: sensor.processor_use
20 | above: 85
21 | action:
22 | - service: notify.mobile_app_mikes_iphone_xs
23 | data:
24 | title: 'HA Server'
25 | message: 'Warning - HA Server Processor {{states.sensor.processor_use.state}}%'
26 |
27 | ## Notify if CPU Temperature is High - credit to @sauloonzem thank you
28 | - alias: CPU Temp Alarm
29 | initial_state: 'on'
30 | trigger:
31 | platform: numeric_state
32 | entity_id: sensor.cpu_temperature
33 | above: 185
34 | action:
35 | - service: notify.mobile_app_mikes_iphone_xs
36 | data:
37 | title: 'HA Server'
38 | message: 'Warning - HA Server CPU Temp is {{states.sensor.cpu_temperature.state}}f'
39 |
40 | # Set Theme at startup
41 | - alias: 'Set theme at startup'
42 | initial_state: 'on'
43 | trigger:
44 | - platform: homeassistant
45 | event: start
46 | action:
47 | service: frontend.set_theme
48 | data:
49 | name: coned_miro
50 |
--------------------------------------------------------------------------------
/camera.yaml:
--------------------------------------------------------------------------------
1 | - platform: ffmpeg
2 | name: rtsp_1
3 | input: !secret wyze_cam_1
--------------------------------------------------------------------------------
/configuration.yaml:
--------------------------------------------------------------------------------
1 | homeassistant:
2 | name: Home
3 | latitude: !secret home_lat
4 | longitude: !secret home_long
5 | elevation: 0
6 | unit_system: imperial
7 | time_zone: America/New_York
8 | customize: !include customize.yaml
9 |
10 | frontend:
11 | themes: !include themes.yaml
12 | extra_html_url:
13 | - /local/custom_ui/state-card-custom-ui.html
14 | extra_html_url_es5:
15 | - /local/custom_ui/state-card-custom-ui-es5.html
16 |
17 | config:
18 |
19 | mobile_app:
20 |
21 | fastdotcom:
22 |
23 | discovery:
24 | ignore:
25 | - philips_hue
26 |
27 | alexa_media:
28 | accounts:
29 | - email: !secret amazon_email
30 | password: !secret amazon_pass
31 | url: amazon.com
32 |
33 | google:
34 | client_id: !secret google_id
35 | client_secret: !secret google_secret
36 |
37 | life360:
38 | accounts:
39 | - username: !secret life360_user
40 | password: !secret life360_pass
41 |
42 | hacs:
43 | token: !secret hacs_gh_token
44 |
45 | updater:
46 |
47 | history:
48 |
49 | ios:
50 |
51 | logger:
52 |
53 | stream:
54 |
55 | homekit:
56 | filter:
57 | exclude_domains: [calendar]
58 |
59 | system_health:
60 |
61 | panel_iframe:
62 | ide:
63 | title: "IDE"
64 | icon: mdi:code-braces
65 | url: https://michaeldvinci.duckdns.org:8321
66 | pihole:
67 | title: "Pi-hole"
68 | icon: mdi:block-helper
69 | url: https://michaeldvinci.duckdns.org:4865
70 |
71 | http:
72 | ip_ban_enabled: False
73 | ssl_certificate: /ssl/fullchain.pem
74 | ssl_key: /ssl/privkey.pem
75 | base_url: https://michaeldvinci.duckdns.org
76 |
77 | automation: !include automations.yaml
78 | camera: !include camera.yaml
79 | device_tracker: !include device_tracker.yaml
80 | group: !include groups.yaml
81 | media_player: !include media_player.yaml
82 | remote: !include remote.yaml
83 | script: !include scripts.yaml
84 | sensor: !include sensor.yaml
85 | switch: !include switch.yaml
86 | zone: !include zone.yaml
87 |
88 | recorder:
89 | purge_keep_days: 1
90 | purge_interval: 1
91 |
92 |
--------------------------------------------------------------------------------
/custom_components/__pycache__/custom_updater.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/__pycache__/custom_updater.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/alexa_media/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/__pycache__/alarm_control_panel.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/alexa_media/__pycache__/alarm_control_panel.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/__pycache__/const.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/alexa_media/__pycache__/const.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/__pycache__/media_player.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/alexa_media/__pycache__/media_player.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/__pycache__/notify.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/alexa_media/__pycache__/notify.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/alexa_media/alarm_control_panel.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # SPDX-License-Identifier: Apache-2.0
4 | """
5 | Alexa Devices Alarm Control Panel using Guard Mode.
6 |
7 | For more details about this platform, please refer to the documentation at
8 | https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639
9 | """
10 | import logging
11 | from typing import List # noqa pylint: disable=unused-import
12 |
13 | from homeassistant import util
14 | from homeassistant.components.alarm_control_panel import AlarmControlPanel
15 | from homeassistant.const import (STATE_ALARM_ARMED_AWAY,
16 | STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
17 | from homeassistant.exceptions import HomeAssistantError
18 |
19 | from . import DATA_ALEXAMEDIA
20 | from . import DOMAIN as ALEXA_DOMAIN
21 | from . import MIN_TIME_BETWEEN_FORCED_SCANS, MIN_TIME_BETWEEN_SCANS, hide_email
22 |
23 | _LOGGER = logging.getLogger(__name__)
24 |
25 | DEPENDENCIES = [ALEXA_DOMAIN]
26 |
27 |
28 | def setup_platform(hass, config, add_devices_callback,
29 | discovery_info=None):
30 | """Set up the Alexa alarm control panel platform."""
31 | devices = [] # type: List[AlexaAlarmControlPanel]
32 | for account, account_dict in (hass.data[DATA_ALEXAMEDIA]
33 | ['accounts'].items()):
34 | alexa_client = AlexaAlarmControlPanel(account_dict['login_obj'],
35 | hass) \
36 | # type: AlexaAlarmControlPanel
37 | if not (alexa_client and alexa_client.unique_id):
38 | _LOGGER.debug("%s: Skipping creation of uninitialized device: %s",
39 | account,
40 | alexa_client)
41 | continue
42 | devices.append(alexa_client)
43 | (hass.data[DATA_ALEXAMEDIA]
44 | ['accounts']
45 | [account]
46 | ['entities']
47 | ['alarm_control_panel']) = alexa_client
48 | if devices:
49 | _LOGGER.debug("Adding %s", devices)
50 | try:
51 | add_devices_callback(devices, True)
52 | except HomeAssistantError as exception_:
53 | message = exception_.message # type: str
54 | if message.startswith("Entity id already exists"):
55 | _LOGGER.debug("Device already added: %s",
56 | message)
57 | else:
58 | _LOGGER.debug("Unable to add devices: %s : %s",
59 | devices,
60 | message)
61 | return True
62 |
63 |
64 | class AlexaAlarmControlPanel(AlarmControlPanel):
65 | """Implementation of Alexa Media Player alarm control panel."""
66 |
67 | def __init__(self, login, hass):
68 | # pylint: disable=unexpected-keyword-arg
69 | """Initialize the Alexa device."""
70 | from alexapy import AlexaAPI
71 | # Class info
72 | self._login = login
73 | self.alexa_api = AlexaAPI(self, login)
74 | self.alexa_api_session = login.session
75 | self.account = hide_email(login.email)
76 | self.hass = hass
77 |
78 | # Guard info
79 | self._appliance_id = None
80 | self._guard_entity_id = None
81 | self._friendly_name = "Alexa Guard"
82 | self._state = None
83 | self._should_poll = False
84 | self._attrs = {}
85 |
86 | data = self.alexa_api.get_guard_details(self._login)
87 | try:
88 | guard_dict = (data['locationDetails']
89 | ['locationDetails']['Default_Location']
90 | ['amazonBridgeDetails']['amazonBridgeDetails']
91 | ['LambdaBridge_AAA/OnGuardSmartHomeBridgeService']
92 | ['applianceDetails']['applianceDetails'])
93 | except KeyError:
94 | guard_dict = {}
95 | for key, value in guard_dict.items():
96 | if value['modelName'] == "REDROCK_GUARD_PANEL":
97 | self._appliance_id = value['applianceId']
98 | self._guard_entity_id = value['entityId']
99 | self._friendly_name += " " + self._appliance_id[-5:]
100 | _LOGGER.debug("%s: Discovered %s: %s %s",
101 | self.account,
102 | self._friendly_name,
103 | self._appliance_id,
104 | self._guard_entity_id)
105 | if not self._appliance_id:
106 | _LOGGER.debug("%s: No Alexa Guard entity found", self.account)
107 | return None
108 | # Register event handler on bus
109 | hass.bus.listen(('{}_{}'.format(ALEXA_DOMAIN,
110 | hide_email(login.email)))[0:32],
111 | self._handle_event)
112 | self.refresh(no_throttle=True)
113 |
114 | def _handle_event(self, event):
115 | """Handle websocket events.
116 |
117 | Used instead of polling.
118 | """
119 | self.refresh()
120 |
121 | @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
122 | def refresh(self):
123 | """Update Guard state."""
124 | import json
125 | _LOGGER.debug("%s: Refreshing %s", self.account, self.name)
126 | state = None
127 | state_json = self.alexa_api.get_guard_state(self._login,
128 | self._appliance_id)
129 | # _LOGGER.debug("%s: state_json %s", self.account, state_json)
130 | if (state_json and 'deviceStates' in state_json
131 | and state_json['deviceStates']):
132 | cap = state_json['deviceStates'][0]['capabilityStates']
133 | # _LOGGER.debug("%s: cap %s", self.account, cap)
134 | for item_json in cap:
135 | item = json.loads(item_json)
136 | # _LOGGER.debug("%s: item %s", self.account, item)
137 | if item['name'] == 'armState':
138 | state = item['value']
139 | # _LOGGER.debug("%s: state %s", self.account, state)
140 | elif state_json['errors']:
141 | _LOGGER.debug("%s: Error refreshing alarm_control_panel %s: %s",
142 | self.account,
143 | self.name,
144 | json.dumps(state_json['errors']) if state_json
145 | else None)
146 | if state is None:
147 | return
148 | if state == "ARMED_AWAY":
149 | self._state = STATE_ALARM_ARMED_AWAY
150 | elif state == "ARMED_STAY":
151 | self._state = STATE_ALARM_DISARMED
152 | else:
153 | self._state = STATE_ALARM_DISARMED
154 | _LOGGER.debug("%s: Alarm State: %s", self.account, self.state)
155 |
156 | def alarm_disarm(self, code=None):
157 | # pylint: disable=unexpected-keyword-arg
158 | """Send disarm command.
159 |
160 | We use the arm_home state as Alexa does not have disarm state.
161 | """
162 | self.alarm_arm_home()
163 | self.schedule_update_ha_state()
164 |
165 | def alarm_arm_home(self, code=None):
166 | """Send arm home command."""
167 | self.alexa_api.set_guard_state(self._login,
168 | self._guard_entity_id,
169 | "ARMED_STAY")
170 | self.refresh(no_throttle=True)
171 | self.schedule_update_ha_state()
172 |
173 | def alarm_arm_away(self, code=None):
174 | """Send arm away command."""
175 | # pylint: disable=unexpected-keyword-arg
176 | self.alexa_api.set_guard_state(self._login,
177 | self._guard_entity_id,
178 | "ARMED_AWAY")
179 | self.refresh(no_throttle=True)
180 | self.schedule_update_ha_state()
181 |
182 | @property
183 | def unique_id(self):
184 | """Return the unique ID."""
185 | return self._guard_entity_id
186 |
187 | @property
188 | def name(self):
189 | """Return the name of the device."""
190 | return self._friendly_name
191 |
192 | @property
193 | def state(self):
194 | """Return the state of the device."""
195 | return self._state
196 |
197 | @property
198 | def device_state_attributes(self):
199 | """Return the state attributes."""
200 | return self._attrs
201 |
202 | @property
203 | def should_poll(self):
204 | """Return the polling state."""
205 | return self._should_poll or not (self.hass.data[DATA_ALEXAMEDIA]
206 | ['accounts'][self._login.email]
207 | ['websocket'])
208 |
--------------------------------------------------------------------------------
/custom_components/alexa_media/const.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # SPDX-License-Identifier: Apache-2.0
4 | """
5 | Support to interface with Alexa Devices.
6 |
7 | For more details about this platform, please refer to the documentation at
8 | https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639
9 | """
10 | from datetime import timedelta
11 |
12 | __version__ = '1.3.1'
13 | PROJECT_URL = "https://github.com/keatontaylor/alexa_media_player/"
14 | ISSUE_URL = "{}issues".format(PROJECT_URL)
15 |
16 | DOMAIN = 'alexa_media'
17 | DATA_ALEXAMEDIA = 'alexa_media'
18 |
19 | PLAY_SCAN_INTERVAL = 20
20 | SCAN_INTERVAL = timedelta(seconds=60)
21 | MIN_TIME_BETWEEN_SCANS = SCAN_INTERVAL
22 | MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
23 |
24 | ALEXA_COMPONENTS = [
25 | 'media_player',
26 | 'notify',
27 | 'alarm_control_panel'
28 | ]
29 |
30 | CONF_ACCOUNTS = 'accounts'
31 | CONF_DEBUG = 'debug'
32 | CONF_INCLUDE_DEVICES = 'include_devices'
33 | CONF_EXCLUDE_DEVICES = 'exclude_devices'
34 | SERVICE_UPDATE_LAST_CALLED = 'update_last_called'
35 | ATTR_MESSAGE = 'message'
36 | ATTR_EMAIL = 'email'
37 |
38 | STARTUP = """
39 | -------------------------------------------------------------------
40 | {}
41 | Version: {}
42 | This is a custom component
43 | If you have any issues with this you need to open an issue here:
44 | {}
45 | -------------------------------------------------------------------
46 | """.format(DOMAIN, __version__, ISSUE_URL)
47 |
--------------------------------------------------------------------------------
/custom_components/alexa_media/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "alexa_media",
3 | "name": "Alexa Media Player",
4 | "documentation": "https://github.com/keatontaylor/alexa_media_player/wiki",
5 | "dependencies": [],
6 | "codeowners": ["@keatontaylor", "@alandtse"],
7 | "requirements": ["alexapy==0.7.0"]
8 | }
9 |
--------------------------------------------------------------------------------
/custom_components/alexa_media/notify.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # SPDX-License-Identifier: Apache-2.0
4 | """
5 | Alexa Devices notification service.
6 |
7 | For more details about this platform, please refer to the documentation at
8 | https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639
9 | """
10 | import logging
11 |
12 | from homeassistant.components.notify import (
13 | ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT,
14 | BaseNotificationService
15 | )
16 |
17 | from . import (
18 | DOMAIN as ALEXA_DOMAIN,
19 | DATA_ALEXAMEDIA,
20 | hide_email, hide_serial)
21 |
22 | _LOGGER = logging.getLogger(__name__)
23 |
24 | DEPENDENCIES = [ALEXA_DOMAIN]
25 |
26 | EVENT_NOTIFY = "notify"
27 |
28 |
29 | def get_service(hass, config, discovery_info=None):
30 | # pylint: disable=unused-argument
31 | """Get the demo notification service."""
32 | return AlexaNotificationService(hass)
33 |
34 |
35 | class AlexaNotificationService(BaseNotificationService):
36 | """Implement Alexa Media Player notification service."""
37 |
38 | def __init__(self, hass):
39 | """Initialize the service."""
40 | self.hass = hass
41 |
42 | def convert(self, names, type_="entities", filter_matches=False):
43 | """Return a list of converted Alexa devices based on names.
44 |
45 | Names may be matched either by serialNumber, accountName, or
46 | Homeassistant entity_id and can return any of the above plus entities
47 |
48 | Parameters
49 | ----------
50 | names : list(string)
51 | A list of names to convert
52 | type : string
53 | The type to return entities, entity_ids, serialnumbers, names
54 | filter_matches : bool
55 | Whether non-matching items are removed from the returned list.
56 |
57 | Returns
58 | -------
59 | list(string)
60 | List of home assistant entity_ids
61 |
62 | """
63 | devices = []
64 | if isinstance(names, str):
65 | names = [names]
66 | for item in names:
67 | matched = False
68 | for alexa in self.devices:
69 | _LOGGER.debug("Testing item: %s against (%s, %s, %s, %s)",
70 | item,
71 | alexa,
72 | alexa.name,
73 | hide_serial(alexa.unique_id),
74 | alexa.entity_id)
75 | if item in (alexa, alexa.name, alexa.unique_id,
76 | alexa.entity_id):
77 | if type_ == "entities":
78 | converted = alexa
79 | elif type_ == "serialnumbers":
80 | converted = alexa.unique_id
81 | elif type_ == "names":
82 | converted = alexa.name
83 | elif type_ == "entity_ids":
84 | converted = alexa.entity_id
85 | devices.append(converted)
86 | matched = True
87 | _LOGGER.debug("Converting: %s to (%s): %s",
88 | item,
89 | type_,
90 | converted)
91 | if not filter_matches and not matched:
92 | devices.append(item)
93 | return devices
94 |
95 | @property
96 | def targets(self):
97 | """Return a dictionary of Alexa devices."""
98 | devices = {}
99 | for account, account_dict in (self.hass.data[DATA_ALEXAMEDIA]
100 | ['accounts'].items()):
101 | for serial, alexa in (account_dict
102 | ['devices']['media_player'].items()):
103 | devices[alexa['accountName']] = serial
104 | return devices
105 |
106 | @property
107 | def devices(self):
108 | """Return a dictionary of Alexa devices."""
109 | devices = []
110 | if ('accounts' not in self.hass.data[DATA_ALEXAMEDIA] and
111 | not self.hass.data[DATA_ALEXAMEDIA]['accounts'].items()):
112 | return devices
113 | for _, account_dict in (self.hass.data[DATA_ALEXAMEDIA]
114 | ['accounts'].items()):
115 | devices = devices + list(account_dict
116 | ['entities']['media_player'].values())
117 | return devices
118 |
119 | def send_message(self, message="", **kwargs):
120 | """Send a message to a Alexa device."""
121 | _LOGGER.debug("Message: %s, kwargs: %s",
122 | message,
123 | kwargs)
124 | kwargs['message'] = message
125 | targets = kwargs.get(ATTR_TARGET)
126 | title = (kwargs.get(ATTR_TITLE) if ATTR_TITLE in kwargs
127 | else ATTR_TITLE_DEFAULT)
128 | data = kwargs.get(ATTR_DATA)
129 | if isinstance(targets, str):
130 | targets = [targets]
131 | entities = self.convert(targets, type_="entities")
132 | try:
133 | entities.extend(self.hass.components.group.expand_entity_ids(
134 | entities))
135 | except ValueError:
136 | _LOGGER.debug("Invalid Home Assistant entity in %s", entities)
137 | if data['type'] == "tts":
138 | targets = self.convert(entities, type_="entities",
139 | filter_matches=True)
140 | _LOGGER.debug("TTS entities: %s", targets)
141 | for alexa in targets:
142 | _LOGGER.debug("TTS by %s : %s", alexa, message)
143 | alexa.send_tts(message)
144 | elif data['type'] == "announce":
145 | targets = self.convert(entities, type_="serialnumbers",
146 | filter_matches=True)
147 | _LOGGER.debug("Announce targets: %s entities: %s",
148 | list(map(hide_serial, targets)),
149 | entities)
150 | for account, account_dict in (self.hass.data[DATA_ALEXAMEDIA]
151 | ['accounts'].items()):
152 | for alexa in (account_dict['entities']
153 | ['media_player'].values()):
154 | if alexa.unique_id in targets and alexa.available:
155 | _LOGGER.debug(("%s: Announce by %s to "
156 | "targets: %s: %s"),
157 | hide_email(account),
158 | alexa,
159 | list(map(hide_serial, targets)),
160 | message)
161 | alexa.send_announcement(message,
162 | targets=targets,
163 | title=title,
164 | method=(data['method'] if
165 | 'method' in data
166 | else 'all'))
167 | break
168 | elif data['type'] == "push":
169 | targets = self.convert(entities, type_="entities",
170 | filter_matches=True)
171 | for alexa in targets:
172 | _LOGGER.debug("Push by %s : %s %s", alexa, title, message)
173 | alexa.send_mobilepush(message, title=title)
174 |
--------------------------------------------------------------------------------
/custom_components/alexa_media/services.yaml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 | update_last_called:
3 | # Description of the service
4 | description: Forces update of last_called echo device for each Alexa account.
5 | # Different fields that your service accepts
6 | fields:
7 | # Key of the field
8 | email:
9 | # Description of the field
10 | description: List of Alexa accounts to update. If empty, will update all known accounts
11 | # Example value that can be passed for this field
12 | example: 'my_email@alexa.com'
13 |
--------------------------------------------------------------------------------
/custom_components/customizer/__init__.py:
--------------------------------------------------------------------------------
1 | """Customizer component. Bring extra customize features to home-assistant."""
2 |
3 | import asyncio
4 | import logging
5 | import os
6 |
7 | import homeassistant.components.frontend as frontend
8 | import homeassistant.helpers.config_validation as cv
9 | from homeassistant.config import load_yaml_config_file, DATA_CUSTOMIZE
10 | from homeassistant.core import callback
11 |
12 | from homeassistant.helpers.entity import Entity
13 | from homeassistant.helpers.entity_component import EntityComponent
14 | from homeassistant.const import CONF_ENTITY_ID, MINOR_VERSION
15 |
16 | import voluptuous as vol
17 |
18 | _LOGGER = logging.getLogger(__name__)
19 |
20 | DOMAIN = 'customizer'
21 | DEPENDENCIES = ['frontend']
22 |
23 | CONF_CUSTOM_UI = 'custom_ui'
24 |
25 | LOCAL = 'local'
26 | HOSTED = 'hosted'
27 | DEBUG = 'debug'
28 |
29 | CONF_HIDE_ATTRIBUTES = 'hide_attributes'
30 |
31 | CONF_ATTRIBUTE = 'attribute'
32 | CONF_VALUE = 'value'
33 | CONF_COLUMNS = 'columns'
34 |
35 | SERVICE_SET_ATTRIBUTE = 'set_attribute'
36 | SERVICE_SET_ATTRIBUTE_SCHEMA = vol.Schema({
37 | vol.Required(CONF_ENTITY_ID): cv.entity_id,
38 | vol.Required(CONF_ATTRIBUTE): cv.string,
39 | vol.Optional(CONF_VALUE): cv.match_all,
40 | })
41 |
42 | CONFIG_SCHEMA = vol.Schema({
43 | DOMAIN: vol.Schema({
44 | vol.Optional(CONF_CUSTOM_UI): cv.string,
45 | vol.Optional(CONF_COLUMNS): [int],
46 | vol.Optional(CONF_HIDE_ATTRIBUTES):
47 | vol.All(cv.ensure_list, [cv.string]),
48 | })
49 | }, extra=vol.ALLOW_EXTRA)
50 |
51 |
52 | @asyncio.coroutine
53 | def async_setup(hass, config):
54 | """Set up customizer."""
55 | custom_ui = config[DOMAIN].get(CONF_CUSTOM_UI)
56 | if MINOR_VERSION < 53 and custom_ui is not None:
57 | _LOGGER.warning('%s is only supported from Home Assistant 0.53',
58 | CONF_CUSTOM_UI)
59 | elif custom_ui is not None:
60 | def add_extra_html_url(base_url):
61 | """Add extra url using version-dependent function."""
62 | if MINOR_VERSION >= 59:
63 | frontend.add_extra_html_url(
64 | hass, '{}.html'.format(base_url), False)
65 | frontend.add_extra_html_url(
66 | hass, '{}-es5.html'.format(base_url), True)
67 | else:
68 | frontend.add_extra_html_url(hass, '{}.html'.format(base_url))
69 |
70 | if custom_ui == LOCAL:
71 | add_extra_html_url('/local/custom_ui/state-card-custom-ui')
72 | elif custom_ui == HOSTED:
73 | add_extra_html_url(
74 | 'https://raw.githubusercontent.com/andrey-git/'
75 | 'home-assistant-custom-ui/master/state-card-custom-ui')
76 | elif custom_ui == DEBUG:
77 | add_extra_html_url(
78 | 'https://raw.githubusercontent.com/andrey-git/'
79 | 'home-assistant-custom-ui/master/'
80 | 'state-card-custom-ui-dbg')
81 | else:
82 | add_extra_html_url(
83 | 'https://github.com/andrey-git/home-assistant-custom-ui/'
84 | 'releases/download/{}/state-card-custom-ui'
85 | .format(custom_ui))
86 |
87 | component = EntityComponent(_LOGGER, DOMAIN, hass)
88 | yield from component.async_add_entities([CustomizerEntity(config[DOMAIN])])
89 |
90 | @callback
91 | def set_attribute(call):
92 | """Set attribute override."""
93 | data = call.data
94 | entity_id = data[CONF_ENTITY_ID]
95 | attribute = data[CONF_ATTRIBUTE]
96 | value = data.get(CONF_VALUE)
97 | overrides = hass.data[DATA_CUSTOMIZE].get(entity_id)
98 | state = hass.states.get(entity_id)
99 | state_attributes = dict(state.attributes)
100 | if value is None:
101 | if attribute in overrides:
102 | overrides.pop(attribute)
103 | if attribute in state_attributes:
104 | state_attributes.pop(attribute)
105 | else:
106 | overrides[attribute] = value
107 | state_attributes[attribute] = value
108 | hass.states.async_set(entity_id, state.state, state_attributes)
109 |
110 | if MINOR_VERSION >= 61:
111 | hass.services.async_register(DOMAIN, SERVICE_SET_ATTRIBUTE,
112 | set_attribute,
113 | SERVICE_SET_ATTRIBUTE_SCHEMA)
114 | else:
115 | descriptions = yield from hass.async_add_job(
116 | load_yaml_config_file, os.path.join(
117 | os.path.dirname(__file__), 'services.yaml'))
118 | hass.services.async_register(DOMAIN, SERVICE_SET_ATTRIBUTE,
119 | set_attribute,
120 | descriptions[SERVICE_SET_ATTRIBUTE],
121 | SERVICE_SET_ATTRIBUTE_SCHEMA)
122 |
123 | return True
124 |
125 |
126 | class CustomizerEntity(Entity):
127 | """Customizer entity class."""
128 |
129 | def __init__(self, config):
130 | """Constructor that parses the config."""
131 | self.hide_attributes = config.get(CONF_HIDE_ATTRIBUTES)
132 | self.columns = config.get(CONF_COLUMNS)
133 |
134 | @property
135 | def hidden(self):
136 | """Don't show the entity."""
137 | return True
138 |
139 | @property
140 | def name(self):
141 | """Singleton name."""
142 | return DOMAIN
143 |
144 | @property
145 | def state_attributes(self):
146 | """Return the state attributes."""
147 | result = {}
148 | if self.hide_attributes:
149 | result[CONF_HIDE_ATTRIBUTES] = self.hide_attributes
150 | if self.columns:
151 | result[CONF_COLUMNS] = self.columns
152 | return result
153 |
--------------------------------------------------------------------------------
/custom_components/customizer/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/customizer/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/customizer/services.yaml:
--------------------------------------------------------------------------------
1 | set_attribute:
2 | description: >
3 | Set or clear persistent attribute of an entity overriding any value set in code.
4 | Note that calling homeassistant.reload_core_config will reset overrides to their yaml state.
5 | fields:
6 | entity_id:
7 | description: Entity ID to set the attribute on.
8 | example: light.patio
9 | attribute:
10 | description: Name of the attribute to set.
11 | example: friendly_name
12 | value:
13 | description: >
14 | (Optional) Value to set. Leave unspecified in order to clear set value.
15 | Note that when clearing attribute it will be empty (and not set-by-code) until next entity update.
16 | example: 'My light'
17 |
--------------------------------------------------------------------------------
/custom_components/hacs/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Custom element manager for community created elements.
3 |
4 | For more details about this integration, please refer to the documentation at
5 | https://custom-components.github.io/hacs/
6 | """
7 | import logging
8 | import os.path
9 | import json
10 | import asyncio
11 | from datetime import datetime, timedelta
12 | from pkg_resources import parse_version
13 | import aiohttp
14 |
15 | import voluptuous as vol
16 | from homeassistant.const import EVENT_HOMEASSISTANT_START, __version__ as HAVERSION
17 | from homeassistant.helpers.aiohttp_client import async_create_clientsession
18 | import homeassistant.helpers.config_validation as cv
19 | from homeassistant.helpers import discovery
20 |
21 | from .const import (
22 | CUSTOM_UPDATER_LOCATIONS,
23 | STARTUP,
24 | ISSUE_URL,
25 | CUSTOM_UPDATER_WARNING,
26 | NAME_LONG,
27 | NAME_SHORT,
28 | DOMAIN_DATA,
29 | ELEMENT_TYPES,
30 | VERSION,
31 | IFRAME,
32 | DOMAIN,
33 | )
34 |
35 |
36 | # TODO: Remove this when minimum HA version is > 0.93
37 | REQUIREMENTS = ["aiofiles==0.4.0", "backoff==1.8.0", "packaging==19.0"]
38 |
39 | _LOGGER = logging.getLogger("custom_components.hacs")
40 |
41 | CONFIG_SCHEMA = vol.Schema(
42 | {
43 | DOMAIN: vol.Schema(
44 | {
45 | vol.Required("token"): cv.string,
46 | vol.Optional("sidepanel_title"): cv.string,
47 | vol.Optional("sidepanel_icon"): cv.string,
48 | vol.Optional("dev", default=False): cv.boolean,
49 | vol.Optional("appdaemon", default=False): cv.boolean,
50 | vol.Optional("python_script", default=False): cv.boolean,
51 | vol.Optional("theme", default=False): cv.boolean,
52 | }
53 | )
54 | },
55 | extra=vol.ALLOW_EXTRA,
56 | )
57 |
58 |
59 | async def async_setup(hass, config): # pylint: disable=unused-argument
60 | """Set up this integration."""
61 | from .aiogithub.exceptions import AIOGitHubAuthentication, AIOGitHubException, AIOGitHubRatelimit
62 | from .hacsbase import HacsBase as hacs
63 |
64 | _LOGGER.info(STARTUP)
65 | config_dir = hass.config.path()
66 |
67 | # Configure HACS
68 | try:
69 | await configure_hacs(hass, config[DOMAIN], config_dir)
70 | except AIOGitHubAuthentication as exception:
71 | _LOGGER.error(exception)
72 | return False
73 | except AIOGitHubRatelimit as exception:
74 | _LOGGER.warning(exception)
75 | except AIOGitHubException as exception:
76 | _LOGGER.warning(exception)
77 |
78 | # Check if custom_updater exists
79 | for location in CUSTOM_UPDATER_LOCATIONS:
80 | if os.path.exists(location.format(config_dir)):
81 | msg = CUSTOM_UPDATER_WARNING.format(location.format(config_dir))
82 | _LOGGER.critical(msg)
83 | return False
84 |
85 | # Check if HA is the required version.
86 | if parse_version(HAVERSION) < parse_version("0.92.0"):
87 | _LOGGER.critical("You need HA version 92 or newer to use this integration.")
88 | return False
89 |
90 | # Add sensor
91 | hass.async_create_task(
92 | discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config[DOMAIN])
93 | )
94 |
95 | # Setup startup tasks
96 | hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs().startup_tasks())
97 |
98 | await setup_frontend(hass, hacs)
99 |
100 | # Service registration
101 | async def service_hacs_install(call):
102 | """Install a repository."""
103 | repository = str(call.data["repository"])
104 | if repository not in hacs().store.repositories:
105 | _LOGGER.error("%s is not a konwn repository!", repository)
106 | return
107 | repository = hacs().store.repositories[repository]
108 | await repository.install()
109 |
110 | async def service_hacs_register(call):
111 | """register a repository."""
112 | repository = call.data["repository"]
113 | repository_type = call.data["repository_type"]
114 | if await hacs().is_known_repository(repository):
115 | _LOGGER.error("%s is already a konwn repository!", repository)
116 | return
117 | await hacs().register_new_repository(repository_type, repository)
118 |
119 | hass.services.async_register("hacs", 'install', service_hacs_install)
120 | hass.services.async_register("hacs", 'register', service_hacs_register)
121 |
122 | # Mischief managed!
123 | return True
124 |
125 |
126 | async def configure_hacs(hass, configuration, hass_config_dir):
127 | """Configure HACS."""
128 | from .aiogithub import AIOGitHub
129 | from .hacsbase import HacsBase as hacs
130 | from .hacsbase.configuration import HacsConfiguration
131 | from .hacsbase.data import HacsData
132 | from . import const as const
133 | from .hacsbase import const as hacsconst
134 | from .hacsbase.migration import HacsMigration
135 | #from .hacsbase.storage import HacsStorage
136 |
137 | hacs.config = HacsConfiguration(configuration)
138 |
139 | if hacs.config.appdaemon:
140 | ELEMENT_TYPES.append("appdaemon")
141 | if hacs.config.python_script:
142 | ELEMENT_TYPES.append("python_script")
143 | if hacs.config.theme:
144 | ELEMENT_TYPES.append("theme")
145 |
146 | # Print DEV warning
147 | if hacs.config.dev:
148 | _LOGGER.error(
149 | const.DEV_MODE
150 | )
151 | hass.components.persistent_notification.create(
152 | title="HACS DEV MODE",
153 | message=const.DEV_MODE,
154 | notification_id="hacs_dev_mode"
155 | )
156 |
157 | hacs.migration = HacsMigration()
158 | #hacs.storage = HacsStorage()
159 |
160 | hacs.aiogithub = AIOGitHub(
161 | hacs.config.token, hass.loop, async_create_clientsession(hass)
162 | )
163 |
164 | hacs.hacs_github = await hacs.aiogithub.get_repo("custom-components/hacs")
165 |
166 | hacs.hass = hass
167 | hacs.const = const
168 | hacs.hacsconst = hacsconst
169 | hacs.config_dir = hass_config_dir
170 | hacs.store = HacsData(hass_config_dir)
171 | hacs.store.restore_values()
172 | hacs.element_types = sorted(ELEMENT_TYPES)
173 |
174 |
175 | async def setup_frontend(hass, hacs):
176 | """Configure the HACS frontend elements."""
177 | from .api import HacsAPI, HacsRunningTask
178 | from .http import HacsWebResponse, HacsPluginView, HacsPlugin
179 |
180 | # Define views
181 | hass.http.register_view(HacsAPI())
182 | hass.http.register_view(HacsPlugin())
183 | hass.http.register_view(HacsPluginView())
184 | hass.http.register_view(HacsRunningTask())
185 | hass.http.register_view(HacsWebResponse())
186 |
187 | # Add to sidepanel
188 | # TODO: Remove this check when minimum HA version is > 0.94
189 | if parse_version(HAVERSION) < parse_version("0.93.9"):
190 | await hass.components.frontend.async_register_built_in_panel(
191 | "iframe",
192 | hacs.config.sidepanel_title,
193 | hacs.config.sidepanel_icon,
194 | hacs.config.sidepanel_title.lower().replace(" ", "_").replace("-", "_"),
195 | {"url": hacs.hacsweb + "/overview"},
196 | require_admin=IFRAME["require_admin"],
197 | )
198 | else:
199 | hass.components.frontend.async_register_built_in_panel(
200 | "iframe",
201 | hacs.config.sidepanel_title,
202 | hacs.config.sidepanel_icon,
203 | hacs.config.sidepanel_title.lower().replace(" ", "_").replace("-", "_"),
204 | {"url": hacs.hacsweb + "/overview"},
205 | require_admin=IFRAME["require_admin"],
206 | )
207 |
--------------------------------------------------------------------------------
/custom_components/hacs/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/__pycache__/api.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/__pycache__/api.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/__pycache__/const.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/__pycache__/const.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/__pycache__/http.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/__pycache__/http.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/__pycache__/sensor.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/__pycache__/sensor.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__init__.py:
--------------------------------------------------------------------------------
1 | """Async Github API implementation."""
2 | from .aiogithub import AIOGitHub
3 |
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/aiogithub.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/aiogithub.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/aiogithubrepository.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/aiogithubrepository.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/aiogithubrepositorycontent.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/aiogithubrepositorycontent.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/aiogithubrepositoryrelease.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/aiogithubrepositoryrelease.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/const.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/const.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/__pycache__/exceptions.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/aiogithub/__pycache__/exceptions.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/aiogithub.py:
--------------------------------------------------------------------------------
1 | """AioGitHub: Base"""
2 | # pylint: disable=super-init-not-called,missing-docstring,invalid-name,redefined-builtin
3 | import logging
4 | from asyncio import CancelledError, TimeoutError
5 |
6 | import async_timeout
7 | from aiohttp import ClientError
8 |
9 | import backoff
10 |
11 | from .const import BASE_HEADERS, BASE_URL
12 | from .exceptions import AIOGitHubAuthentication, AIOGitHubException, AIOGitHubRatelimit
13 |
14 | _LOGGER = logging.getLogger("AioGitHub")
15 |
16 |
17 | class AIOGitHub(object):
18 | """Base Github API implementation."""
19 |
20 | def __init__(self, token, loop, session):
21 | """Must be called before anything else."""
22 | self.token = token
23 | self.loop = loop
24 | self.session = session
25 | self.ratelimit_remaining = None
26 | self.headers = BASE_HEADERS
27 | self.headers["Authorization"] = "token {}".format(token)
28 |
29 | @backoff.on_exception(
30 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
31 | )
32 | async def get_repo(self, repo: str):
33 | """Retrun AIOGithubRepository object."""
34 | from .aiogithubrepository import AIOGithubRepository
35 | if self.ratelimit_remaining == "0":
36 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
37 |
38 | endpoint = "/repos/" + repo
39 | url = BASE_URL + endpoint
40 |
41 | headers = self.headers
42 | headers["Accept"] = "application/vnd.github.mercy-preview+json"
43 |
44 | async with async_timeout.timeout(20, loop=self.loop):
45 | response = await self.session.get(url, headers=headers)
46 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
47 | response = await response.json()
48 |
49 | if self.ratelimit_remaining == "0":
50 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
51 |
52 | if response.get("message"):
53 | if response["message"] == "Bad credentials":
54 | raise AIOGitHubAuthentication("Access token is not valid!")
55 | else:
56 | raise AIOGitHubException(response["message"])
57 |
58 | return AIOGithubRepository(response, self.token, self.loop, self.session)
59 |
60 | @backoff.on_exception(
61 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
62 | )
63 | async def get_org_repos(self, org: str, page=1):
64 | """Retrun a list of AIOGithubRepository objects."""
65 | from .aiogithubrepository import AIOGithubRepository
66 | if self.ratelimit_remaining == "0":
67 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
68 | endpoint = "/orgs/" + org + "/repos?page=" + str(page)
69 | url = BASE_URL + endpoint
70 |
71 | params = {"per_page": 100}
72 |
73 | headers = self.headers
74 | headers["Accept"] = "application/vnd.github.mercy-preview+json"
75 |
76 | async with async_timeout.timeout(20, loop=self.loop):
77 | response = await self.session.get(url, headers=headers, params=params)
78 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
79 | response = await response.json()
80 |
81 | if self.ratelimit_remaining == "0":
82 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
83 |
84 | if not isinstance(response, list):
85 | if response["message"] == "Bad credentials":
86 | raise AIOGitHubAuthentication("Access token is not valid!")
87 | else:
88 | raise AIOGitHubException(response["message"])
89 |
90 | repositories = []
91 |
92 | for repository in response:
93 | repositories.append(
94 | AIOGithubRepository(repository, self.token, self.loop, self.session)
95 | )
96 |
97 | return repositories
98 |
99 | @backoff.on_exception(
100 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
101 | )
102 | async def render_markdown(self, content: str):
103 | """Retrun AIOGithubRepository object."""
104 | if self.ratelimit_remaining == "0":
105 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
106 | endpoint = "/markdown/raw"
107 | url = BASE_URL + endpoint
108 |
109 | headers = self.headers
110 | headers["Content-Type"] = "text/plain"
111 |
112 | async with async_timeout.timeout(20, loop=self.loop):
113 | response = await self.session.post(url, headers=headers, data=content)
114 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
115 | response = await response.text()
116 |
117 | if self.ratelimit_remaining == "0":
118 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
119 |
120 | if isinstance(response, dict):
121 | if response.get("message"):
122 | if response["message"] == "Bad credentials":
123 | raise AIOGitHubAuthentication("Access token is not valid!")
124 | else:
125 | raise AIOGitHubException(response["message"])
126 |
127 | return response
128 |
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/aiogithubrepository.py:
--------------------------------------------------------------------------------
1 | """AioGitHub: Repository"""
2 | from asyncio import CancelledError, TimeoutError
3 | from datetime import datetime
4 |
5 | import async_timeout
6 | import backoff
7 | from aiohttp import ClientError
8 |
9 | from .aiogithub import AIOGitHub
10 | from .aiogithubrepositorycontent import AIOGithubRepositoryContent
11 | from .aiogithubrepositoryrelease import AIOGithubRepositoryRelease
12 | from .const import BASE_URL
13 | from .exceptions import AIOGitHubException, AIOGitHubRatelimit
14 |
15 |
16 | class AIOGithubRepository(AIOGitHub):
17 | """Repository Github API implementation."""
18 |
19 | def __init__(self, attributes, token, loop, session):
20 | """Initialize."""
21 | super().__init__(token, loop, session)
22 | self.attributes = attributes
23 | self._last_commit = None
24 |
25 | @property
26 | def id(self):
27 | return self.attributes.get("id")
28 |
29 | @property
30 | def full_name(self):
31 | return self.attributes.get("full_name")
32 |
33 | @property
34 | def pushed_at(self):
35 | return datetime.strptime(self.attributes.get("pushed_at"), "%Y-%m-%dT%H:%M:%SZ")
36 |
37 | @property
38 | def archived(self):
39 | return self.attributes.get("archived")
40 |
41 | @property
42 | def description(self):
43 | return self.attributes.get("description")
44 |
45 | @property
46 | def topics(self):
47 | return self.attributes.get("topics")
48 |
49 | @property
50 | def default_branch(self):
51 | return self.attributes.get("default_branch")
52 |
53 | @property
54 | def last_commit(self):
55 | return self._last_commit
56 |
57 | @backoff.on_exception(
58 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
59 | )
60 | async def get_contents(self, path, ref=None):
61 | """Retrun a list of repository content objects."""
62 | if self.ratelimit_remaining == "0":
63 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
64 | endpoint = "/repos/" + self.full_name + "/contents/" + path
65 | url = BASE_URL + endpoint
66 |
67 | params = {"path": path}
68 | if ref is not None:
69 | params["ref"] = ref.replace("tags/", "")
70 |
71 | async with async_timeout.timeout(20, loop=self.loop):
72 | response = await self.session.get(url, headers=self.headers, params=params)
73 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
74 | response = await response.json()
75 |
76 | if self.ratelimit_remaining == "0":
77 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
78 |
79 | if not isinstance(response, list):
80 | if response.get("message"):
81 | if response.get("message") == "Not Found":
82 | raise AIOGitHubException(
83 | "{} does not exist in the repository.".format(path)
84 | )
85 | else:
86 | raise AIOGitHubException(response["message"])
87 | return AIOGithubRepositoryContent(response)
88 |
89 | contents = []
90 |
91 | for content in response:
92 | contents.append(AIOGithubRepositoryContent(content))
93 |
94 | return contents
95 |
96 | @backoff.on_exception(
97 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
98 | )
99 | async def get_releases(self, prerelease=False):
100 | """Retrun a list of repository release objects."""
101 | if self.ratelimit_remaining == "0":
102 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
103 | endpoint = "/repos/{}/releases".format(self.full_name)
104 | url = BASE_URL + endpoint
105 |
106 | async with async_timeout.timeout(20, loop=self.loop):
107 | response = await self.session.get(url, headers=self.headers)
108 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
109 | response = await response.json()
110 |
111 | if self.ratelimit_remaining == "0":
112 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
113 |
114 | if not isinstance(response, list):
115 | if response.get("message"):
116 | return False
117 |
118 | contents = []
119 |
120 | for content in response:
121 | if len(contents) == 5:
122 | break
123 | if not prerelease:
124 | if content.get("prerelease", False):
125 | continue
126 | contents.append(AIOGithubRepositoryRelease(content))
127 |
128 | return contents
129 |
130 | @backoff.on_exception(
131 | backoff.expo, (ClientError, CancelledError, TimeoutError, KeyError), max_tries=5
132 | )
133 | async def set_last_commit(self):
134 | """Retrun a list of repository release objects."""
135 | if self.ratelimit_remaining == "0":
136 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
137 | endpoint = "/repos/" + self.full_name + "/commits/" + self.default_branch
138 | url = BASE_URL + endpoint
139 |
140 | async with async_timeout.timeout(20, loop=self.loop):
141 | response = await self.session.get(url, headers=self.headers)
142 | self.ratelimit_remaining = response.headers.get("x-ratelimit-remaining")
143 | response = await response.json()
144 |
145 | if self.ratelimit_remaining == "0":
146 | raise AIOGitHubRatelimit("GitHub Ratelimit error")
147 |
148 | if response.get("message"):
149 | raise AIOGitHubException("No commits")
150 |
151 | self._last_commit = response["sha"][0:7]
152 |
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/aiogithubrepositorycontent.py:
--------------------------------------------------------------------------------
1 | """AioGitHub: Repository Release"""
2 | import base64
3 |
4 |
5 | class AIOGithubRepositoryContent:
6 | """Repository Conetent Github API implementation."""
7 |
8 | def __init__(self, attributes):
9 | """Initialize."""
10 | self.attributes = attributes
11 |
12 | @property
13 | def type(self):
14 | return self.attributes.get("type", "file")
15 |
16 | @property
17 | def encoding(self):
18 | return self.attributes.get("encoding")
19 |
20 | @property
21 | def name(self):
22 | return self.attributes.get("name")
23 |
24 | @property
25 | def path(self):
26 | return self.attributes.get("path")
27 |
28 | @property
29 | def content(self):
30 | return base64.b64decode(
31 | bytearray(self.attributes.get("content"), "utf-8")
32 | ).decode()
33 |
34 | @property
35 | def download_url(self):
36 | return self.attributes.get("download_url") or self.attributes.get(
37 | "browser_download_url"
38 | )
39 |
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/aiogithubrepositoryrelease.py:
--------------------------------------------------------------------------------
1 | """AioGitHub: Repository Release"""
2 | from datetime import datetime
3 | from .aiogithubrepositorycontent import AIOGithubRepositoryContent
4 |
5 |
6 | class AIOGithubRepositoryRelease:
7 | """Repository Release Github API implementation."""
8 |
9 | def __init__(self, attributes):
10 | """Initialize."""
11 | self.attributes = attributes
12 |
13 | @property
14 | def tag_name(self):
15 | return self.attributes.get("tag_name")
16 |
17 | @property
18 | def name(self):
19 | return self.attributes.get("name")
20 |
21 | @property
22 | def published_at(self):
23 | return datetime.strptime(
24 | self.attributes.get("published_at"), "%Y-%m-%dT%H:%M:%SZ"
25 | )
26 |
27 | @property
28 | def draft(self):
29 | return self.attributes.get("draft")
30 |
31 | @property
32 | def prerelease(self):
33 | return self.attributes.get("prerelease")
34 |
35 | @property
36 | def assets(self):
37 | assetlist = []
38 | for item in self.attributes.get("assets"):
39 | assetlist.append(AIOGithubRepositoryContent(item))
40 | return assetlist
41 |
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/const.py:
--------------------------------------------------------------------------------
1 | """AioGithub: Constants"""
2 | BASE_URL = "https://api.github.com"
3 | BASE_HEADERS = {
4 | "Accept": "application/vnd.github.v3.raw+json",
5 | "User-Agent": "python/AIOGitHub",
6 | }
--------------------------------------------------------------------------------
/custom_components/hacs/aiogithub/exceptions.py:
--------------------------------------------------------------------------------
1 | """AioGithub: Exceptions"""
2 | class AIOGitHubException(BaseException):
3 | """Raise this when something is off."""
4 |
5 |
6 | class AIOGitHubRatelimit(AIOGitHubException):
7 | """Raise this when we hit the ratelimit."""
8 |
9 | class AIOGitHubAuthentication(AIOGitHubException):
10 | """Raise this when there is an authentication issue."""
11 |
--------------------------------------------------------------------------------
/custom_components/hacs/const.py:
--------------------------------------------------------------------------------
1 | """Constants for HACS"""
2 | VERSION = "0.12.1"
3 | NAME_LONG = "HACS (Home Assistant Community Store)"
4 | NAME_SHORT = "HACS"
5 | DOMAIN = "hacs"
6 | PROJECT_URL = "https://github.com/custom-components/hacs/"
7 | CUSTOM_UPDATER_LOCATIONS = [
8 | "{}/custom_components/custom_updater.py",
9 | "{}/custom_components/custom_updater/__init__.py",
10 | ]
11 |
12 | ISSUE_URL = "{}issues".format(PROJECT_URL)
13 | DOMAIN_DATA = "{}_data".format(NAME_SHORT.lower())
14 |
15 | ELEMENT_TYPES = ["integration", "plugin"]
16 |
17 | IFRAME = {
18 | "title": "Community",
19 | "icon": "mdi:alpha-c-box",
20 | "url": "/community_overview",
21 | "path": "community",
22 | "require_admin": True,
23 | }
24 |
25 |
26 | # Messages
27 | CUSTOM_UPDATER_WARNING = """
28 | This cannot be used with custom_updater.
29 | To use this you need to remove custom_updater form {}
30 | """
31 |
32 | DEV_MODE = "You have 'dev' enabled for HACS, this is not intended for regular use, no support will be given if you break something."
33 |
34 | STARTUP = """
35 | -------------------------------------------------------------------
36 | {}
37 | Version: {}
38 | This is a custom integration
39 | If you have any issues with this you need to open an issue here:
40 | {}
41 | -------------------------------------------------------------------
42 | """.format(
43 | NAME_LONG, VERSION, ISSUE_URL
44 | )
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/__init__.py:
--------------------------------------------------------------------------------
1 | """Initialize frontend objects."""
2 |
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/all.min.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/all.min.css.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/hacs.js:
--------------------------------------------------------------------------------
1 | // Copy active HA theme
2 | document.getElementsByTagName("html").item(0).setAttribute("style", parent.document.getElementsByTagName("html").item(0).style.cssText)
3 |
4 | // Copy yhe content of the Lovelace example to the clipboard.
5 | function CopyToLovelaceExampleToClipboard() {
6 | window.getSelection().selectAllChildren( document.getElementById("LovelaceExample"));
7 | document.execCommand("copy");
8 | document.getSelection().empty()
9 | document.getElementById('lovelacecopy').style.color = 'forestgreen';
10 | }
11 |
12 | // Show progress bar
13 | function ShowProgressBar() {
14 | document.getElementById('progressbar').style.display = 'block';
15 | }
16 |
17 | // Searchbar
18 | function Search() {
19 | var input = document.getElementById("Search");
20 | if (input) {
21 | var filter = input.value.toLowerCase();
22 | var nodes = document.getElementsByClassName('hacs-card');
23 | for (i = 0; i < nodes.length; i++) {
24 | if (nodes[i].innerHTML.toLowerCase().includes(filter)) {
25 | nodes[i].style.display = "block";
26 | } else {
27 | nodes[i].style.display = "none";
28 | }
29 | }
30 | var nodes = document.getElementsByClassName('hacs-table-row');
31 | for (i = 0; i < nodes.length; i++) {
32 | if (nodes[i].innerHTML.toLowerCase().includes(filter)) {
33 | nodes[i].style.display = "table-row";
34 | } else {
35 | nodes[i].style.display = "none";
36 | }
37 | }
38 | }
39 | }
40 |
41 |
42 | // Dropdown
43 | document.addEventListener('DOMContentLoaded', function() {
44 | var elems = document.querySelectorAll('.dropdown-trigger');
45 | var instances = M.Dropdown.init(elems, {hover: true, constrainWidth: false});
46 | });
47 |
48 | // Modal
49 | document.addEventListener('DOMContentLoaded', function() {
50 | var elems = document.querySelectorAll('.modal');
51 | var instances = M.Modal.init(elems, {});
52 | });
53 |
54 |
55 | // Loader
56 | function toggleLoading(){
57 | var loadingOverlay = document.querySelector('.loading');
58 | loadingOverlay.classList.remove('hidden')
59 | document.activeElement.blur();
60 | }
61 |
62 |
63 | // Check if we can reload
64 | function sleep (time) {
65 | return new Promise((resolve) => setTimeout(resolve, time));
66 | }
67 | function CheckIfWeCanReload() {
68 | var data = true;
69 | const hacsrequest = new XMLHttpRequest()
70 | hacsrequest.open('GET', '/hacs_task', true)
71 | hacsrequest.onload = function() {
72 | data = JSON.parse(this.response)
73 | data = data["task"]
74 | if (!data) {
75 | console.log("Background task is no longer running, reloading in 5s...")
76 | sleep(5000).then(() => {
77 | location.reload()
78 | });
79 | }
80 | }
81 | hacsrequest.send()
82 | }
83 |
84 | function IsTaskRunning() {
85 | let retval = false;
86 | let disp = document.getElementsByClassName("progress")
87 | if (disp) {
88 | disp = disp.item(0).style.display;
89 | if (disp == "block") {
90 | retval = true
91 | } else {
92 | retval = false
93 | }
94 | }
95 | return retval
96 | }
97 |
98 | window.setInterval(function(){
99 | var running = false;
100 | running = IsTaskRunning();
101 | if (running) {
102 | CheckIfWeCanReload();
103 | }
104 | }, 10000);
105 |
106 |
107 |
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/materialize.min.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/materialize.min.css.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/materialize.min.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/materialize.min.js.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.eot.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.eot.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.svg.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.svg.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.ttf.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.ttf.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.woff.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.woff.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.woff2.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-brands-400.woff2.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.eot.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.eot.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.svg.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.svg.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.ttf.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.ttf.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.woff.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.woff.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.woff2.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-regular-400.woff2.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.eot.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.eot.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.svg.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.svg.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.ttf.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.ttf.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.woff.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.woff.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.woff2.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/custom_components/hacs/frontend/elements/webfonts/fa-solid-900.woff2.gz
--------------------------------------------------------------------------------
/custom_components/hacs/frontend/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 | {% set progressbar = 'block' if hacs.store.task_running else 'none' %}
3 | {% set url = namespace(
4 | base=hacs.url_path['base'],
5 | static=hacs.url_path['base']+'/static',
6 | api=hacs.url_path['api'],
7 | adminapi=hacs.url_path['admin-api'],
8 | store=hacs.url_path['base']+'/store',
9 | overview=hacs.url_path['base']+'/overview',
10 | settings=hacs.url_path['base']+'/settings',
11 | repository=hacs.url_path['base']+'/repository') %}
12 |
13 |
14 |
15 |
Background task running, this page will reload when it's done.
{{repository.description}}
23 | 24 |{{repository.description}}
23 | {% if repository.installed %} 24 |Installed {{repository.repository.version_or_commit}}: {{repository.installed_version}}
25 | {% endif %} 26 | {% if repository.published_tags and repository.repository_id != "172733314" %} 27 | {% include 'repository/versionselect.html' with context %} 28 | {% else %} 29 |Available {{repository.repository.version_or_commit}}: {{repository.available_version}}
30 | {% endif %} 31 | 32 | {{repository.display_authors}} 33 |31 | - url: /community_plugin/{{repository.full_name}}/{{name}}.js 32 | {% if repository.repository.javascript_type -%} 33 | type: {{repository.repository.javascript_type}} 34 | {%- else -%} 35 | type: Could not determine the type of this plugin, check the repository. 36 | {%- endif -%} 37 |38 | 39 | 40 | 41 | {% endif %} 42 | -------------------------------------------------------------------------------- /custom_components/hacs/frontend/templates/repository/versionselect.html: -------------------------------------------------------------------------------- 1 | {% set tags = repository.published_tags | sort(True) %} 2 | {% set tags = tags + [repository.default_branch] %} 3 | 4 | {% if repository.selected_tag %} 5 | {% set selected_tag = repository.selected_tag %} 6 | {% else %} 7 | {% set selected_tag = repository.available_version %} 8 | {% endif %} 9 | 10 | Available versions: 11 | -------------------------------------------------------------------------------- /custom_components/hacs/frontend/templates/settings.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 | {% if hacs.config.dev %} 4 | {% set message = "You have 'dev' enabled for HACS, this is not intended for regular use, no support will be given if you break something." %} 5 | {% include 'message.html' with context %} 6 | {% endif %} 7 | 8 | {% include 'modal/upgrade_all.html' with context %} 9 | 10 | 11 | {% include 'settings/hacs_info.html' with context %} 12 | {% include 'settings/buttons.html' with context %} 13 | {% include 'settings/custom_repositories.html' with context %} 14 | {% include 'settings/hidden_repositories.html' with context %} 15 | 16 | 17 | {% if hacs.config.dev and not hacs.store.task_running %} 18 | {% include 'settings/developer.html' with context %} 19 | {% endif %} 20 | {% endblock %} -------------------------------------------------------------------------------- /custom_components/hacs/frontend/templates/settings/buttons.html: -------------------------------------------------------------------------------- 1 |
Status | 8 |Name | 9 |Description | 10 | {% if location == 'overview' %} 11 |Installed | 12 |Available | 13 | {% endif %} 14 |
---|---|---|---|---|
20 | {% if location == 'store' and repository.new %} 21 | NEW 22 | {% else %} 23 | | 24 | {% endif %} 25 |26 | {{repository.name}} 27 | | 28 |{{repository.description}} | 29 | {% if location == 'overview' %} 30 |{{repository.installed_version}} | 31 |{{repository.available_version}} | 32 | {% endif %} 33 |
Exception type: {}
216 |Exception message: {}
217 |{}
218 | """.format(
219 | ex_type.__name__, ex_value, stacks
220 | )
221 | else:
222 | codeblock = ""
223 |
224 | # Generate content
225 | content = """
226 | Author(s): " 31 | for author in self.repository.authors: 32 | if "@" in author: 33 | author = author.split("@")[-1] 34 | authors += " @{author}".format( 35 | author=author 36 | ) 37 | authors += "
" 38 | else: 39 | authors = "Author: {}
".format(self.repository.authors) 40 | else: 41 | authors = "" 42 | return authors 43 | 44 | @property 45 | def local_path(self): 46 | return self.repository.local_path 47 | 48 | @property 49 | def javascript_type(self): 50 | return self.repository.javascript_type 51 | 52 | @property 53 | def show_beta(self): 54 | return self.repository.show_beta 55 | 56 | @property 57 | def full_name(self): 58 | return self.repository_name.split("/")[-1] 59 | -------------------------------------------------------------------------------- /custom_components/hacs/sensor.py: -------------------------------------------------------------------------------- 1 | """Sensor platform for HACS.""" 2 | from homeassistant.helpers.entity import Entity 3 | from .hacsbase import HacsBase as hacs 4 | 5 | 6 | async def async_setup_platform( 7 | hass, config, async_add_entities, discovery_info=None 8 | ): # pylint: disable=unused-argument 9 | """Setup sensor platform.""" 10 | async_add_entities([HACSSensor()]) 11 | 12 | 13 | class HACSSensor(Entity): 14 | """HACS Sensor class.""" 15 | 16 | def __init__(self): 17 | """Initialize.""" 18 | self._state = None 19 | self.has_update = [] 20 | 21 | async def async_update(self): 22 | """Update the sensor.""" 23 | if hacs.store.task_running: 24 | return 25 | 26 | updates = 0 27 | has_update = [] 28 | prev_has_update = self.has_update 29 | 30 | for repository in hacs.store.repositories: 31 | repository = hacs.store.repositories[repository] 32 | if repository.pending_update: 33 | if repository.repository_id not in prev_has_update: 34 | await repository.update() 35 | if repository.pending_update: 36 | hacs.logger.debug(repository.repository_name, "sensor.pending_update") 37 | updates += 1 38 | has_update.append(repository.repository_id) 39 | 40 | self._state = updates 41 | self.has_update = has_update 42 | 43 | @property 44 | def name(self): 45 | """Return the name of the sensor.""" 46 | return "hacs" 47 | 48 | @property 49 | def state(self): 50 | """Return the state of the sensor.""" 51 | return self._state 52 | 53 | @property 54 | def icon(self): 55 | """Return the icon of the sensor.""" 56 | return "mdi:package" 57 | 58 | @property 59 | def unit_of_measurement(self): 60 | """Return the unit of measurement.""" 61 | return "pending update(s)" 62 | -------------------------------------------------------------------------------- /custom_components/hacs/services.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | description: This is NOT intended to be used here, this is intended for developers! 3 | fields: 4 | repository: 5 | description: The repository ID 6 | example: '"123456789"' 7 | register: 8 | description: This is NOT intended to be used here, this is intended for developers! 9 | fields: 10 | repository: 11 | description: The full name of the repository 12 | example: 'developer/repo' 13 | repository_type: 14 | description: The repository type 15 | example: 'plugin' -------------------------------------------------------------------------------- /custom_components/media_player/ps4.py: -------------------------------------------------------------------------------- 1 | """Playstation 4 media_player using ps4-waker.""" 2 | import json 3 | import logging 4 | import urllib.request 5 | from datetime import timedelta 6 | from urllib.parse import urlparse 7 | import requests 8 | import voluptuous as vol 9 | 10 | import homeassistant.util as util 11 | from homeassistant.components.media_player import ( 12 | PLATFORM_SCHEMA, 13 | MEDIA_TYPE_CHANNEL, 14 | SUPPORT_TURN_ON, 15 | SUPPORT_TURN_OFF, 16 | SUPPORT_STOP, 17 | SUPPORT_SELECT_SOURCE, 18 | ENTITY_IMAGE_URL, 19 | MediaPlayerDevice 20 | ) 21 | from homeassistant.const import ( 22 | STATE_IDLE, 23 | STATE_UNKNOWN, 24 | STATE_OFF, 25 | STATE_PLAYING, 26 | CONF_NAME, 27 | CONF_HOST 28 | ) 29 | from homeassistant.helpers import config_validation as cv 30 | 31 | REQUIREMENTS = [] 32 | 33 | _LOGGER = logging.getLogger(__name__) 34 | 35 | SUPPORT_PS4 = SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \ 36 | SUPPORT_STOP | SUPPORT_SELECT_SOURCE 37 | 38 | DEFAULT_NAME = 'Playstation 4' 39 | DEFAULT_PORT = '' 40 | ICON = 'mdi:playstation' 41 | CONF_GAMES_FILENAME = 'games_filename' 42 | CONF_IMAGEMAP_JSON = 'imagemap_json' 43 | CONF_CMD = 'cmd' 44 | CONF_LOCAL_STORE = "local_store" 45 | CONF_PS4_IP = "ps4_ip" 46 | 47 | PS4_GAMES_FILE = 'ps4-games.json' 48 | MEDIA_IMAGE_DEFAULT = '' 49 | MEDIA_IMAGEMAP_JSON = 'https://github.com/hmn/ps4-imagemap/raw/master/games.json' 50 | LOCAL_STORE = '' 51 | 52 | MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) 53 | MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) 54 | 55 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 56 | vol.Required(CONF_HOST): cv.string, 57 | vol.Optional(CONF_PS4_IP): cv.string, 58 | vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, 59 | vol.Optional(CONF_GAMES_FILENAME, default=PS4_GAMES_FILE): cv.string, 60 | vol.Optional(CONF_IMAGEMAP_JSON, default=MEDIA_IMAGEMAP_JSON): cv.string, 61 | vol.Optional(CONF_LOCAL_STORE, default=LOCAL_STORE): cv.string, 62 | }) 63 | 64 | 65 | def setup_platform(hass, config, add_devices, discovery_info=None): 66 | """Setup PS4 platform.""" 67 | if discovery_info is not None: 68 | ip = urlparse(discovery_info[1]).hostname 69 | else: 70 | ip = config.get(CONF_PS4_IP) 71 | 72 | if ip is None: 73 | _LOGGER.error("No PS4 found in configuration file or with discovery") 74 | return False 75 | 76 | host = config.get(CONF_HOST) 77 | name = config.get(CONF_NAME) 78 | games_filename = hass.config.path(config.get(CONF_GAMES_FILENAME)) 79 | games_map_json = config.get(CONF_IMAGEMAP_JSON) 80 | local_store = config.get(CONF_LOCAL_STORE) 81 | 82 | ps4 = PS4Waker(host, ip, games_filename) 83 | add_devices([PS4Device(name, ps4, games_map_json, local_store)], True) 84 | 85 | 86 | class PS4Device(MediaPlayerDevice): 87 | """Representation of a PS4.""" 88 | 89 | def __init__(self, name, ps4, gamesmap_json, local_store): 90 | """Initialize the ps4 device.""" 91 | self.ps4 = ps4 92 | self._name = name 93 | self._state = STATE_UNKNOWN 94 | self._media_content_id = None 95 | self._media_title = None 96 | self._current_source = None 97 | self._current_source_id = None 98 | self._media_image = None 99 | self._games_map_json = gamesmap_json 100 | self._games_map = {} 101 | self._local_store = local_store 102 | if self._local_store == '': 103 | self.load_games_map() 104 | self.update() 105 | 106 | @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) 107 | def update(self): 108 | """Retrieve the latest data.""" 109 | data = self.ps4.search() 110 | self._media_title = data.get('running-app-name') 111 | self._media_content_id = data.get('running-app-titleid') 112 | self._current_source = data.get('running-app-name') 113 | self._current_source_id = data.get('running-app-titleid') 114 | 115 | if data.get('status') == 'Ok': 116 | if self._media_content_id is not None: 117 | self._state = STATE_PLAYING 118 | else: 119 | self._state = STATE_IDLE 120 | else: 121 | self._state = STATE_OFF 122 | self._media_title = None 123 | self._media_content_id = None 124 | self._current_source = None 125 | self._current_source_id = None 126 | self._media_image = None 127 | 128 | def load_games_map(self): 129 | import urllib.request, json 130 | 131 | try: 132 | with urllib.request.urlopen(self._games_map_json) as url: 133 | self._games_map = json.loads(url.read().decode()) 134 | #self._games_map = json.loads(requests.get(self._games_map_json, verify=False)) 135 | except Exception as e: 136 | _LOGGER.error("gamesmap json file could not be loaded, %s" % e) 137 | 138 | @property 139 | def entity_picture(self): 140 | if self.state == STATE_OFF: 141 | return None 142 | 143 | if self._local_store == '': 144 | image_hash = self.media_image_hash 145 | 146 | if image_hash is None: 147 | return None 148 | 149 | return ENTITY_IMAGE_URL.format( 150 | self.entity_id, self.access_token, image_hash) 151 | 152 | if self._media_content_id is None: 153 | return None 154 | 155 | filename = "/local/%s/%s.jpg" % (self._local_store, self._media_content_id) 156 | self._media_image = filename 157 | return filename 158 | 159 | @property 160 | def name(self): 161 | """Return the name of the device.""" 162 | return self._name 163 | 164 | @property 165 | def state(self): 166 | """Return the state of the device.""" 167 | return self._state 168 | 169 | @property 170 | def icon(self): 171 | """Icon.""" 172 | return ICON 173 | 174 | @property 175 | def media_content_id(self): 176 | """Content ID of current playing media.""" 177 | return self._media_content_id 178 | 179 | @property 180 | def media_content_type(self): 181 | """Content type of current playing media.""" 182 | return MEDIA_TYPE_CHANNEL 183 | 184 | @property 185 | def media_image_url(self): 186 | """Image url of current playing media.""" 187 | if self._media_content_id == '': 188 | return MEDIA_IMAGE_DEFAULT 189 | 190 | if self._local_store == '': 191 | try: 192 | return self._games_map[self._media_content_id] 193 | except KeyError: 194 | return MEDIA_IMAGE_DEFAULT 195 | 196 | try: 197 | return self._media_image 198 | except KeyError: 199 | return MEDIA_IMAGE_DEFAULT 200 | 201 | @property 202 | def media_title(self): 203 | """Title of current playing media.""" 204 | return self._media_title 205 | 206 | @property 207 | def supported_features(self): 208 | """Media player features that are supported.""" 209 | return SUPPORT_PS4 210 | 211 | @property 212 | def source(self): 213 | """Return the current input source.""" 214 | return self._current_source 215 | 216 | @property 217 | def source_list(self): 218 | """List of available input sources.""" 219 | return sorted(self.ps4.games.values()) 220 | 221 | def turn_off(self): 222 | """Turn off media player.""" 223 | self.ps4.standby() 224 | 225 | def turn_on(self): 226 | """Turn on the media player.""" 227 | self.ps4.wake() 228 | self.update() 229 | 230 | def media_pause(self): 231 | """Send keypress ps to return to menu.""" 232 | self.ps4.remote('ps') 233 | self.update() 234 | 235 | def media_stop(self): 236 | """Send keypress ps to return to menu.""" 237 | self.ps4.remote('ps') 238 | self.update() 239 | 240 | def select_source(self, source): 241 | """Select input source.""" 242 | for titleid, game in self.ps4.games.items(): 243 | if source == game: 244 | self.ps4.start(titleid) 245 | self._current_source_id = titleid 246 | self._current_source = game 247 | self._media_content_id = titleid 248 | self._media_title = game 249 | self._media_image = self.entity_picture() 250 | self.update() 251 | 252 | 253 | class PS4Waker(object): 254 | """Rest client for handling the data retrieval.""" 255 | 256 | def __init__(self, url, ip, games_filename): 257 | """Initialize the data object.""" 258 | self._url = url 259 | self._ip = ip 260 | self._games_filename = games_filename 261 | self.games = {} 262 | self._load_games() 263 | 264 | def __call(self, command, param=None): 265 | url = '{0}/ps4/{1}/{2}'.format(self._url, self._ip, command) 266 | if param is not None: 267 | url += '/{0}'.format(param) 268 | 269 | try: 270 | response = requests.get(url, verify=False) 271 | if 200 != response.status_code: 272 | raise Exception(response.text) 273 | return response.text 274 | except Exception as e: 275 | _LOGGER.error('Failed to call %s: %s', command, e) 276 | return None 277 | 278 | def _load_games(self): 279 | try: 280 | with open(self._games_filename, 'r') as f: 281 | self.games = json.load(f) 282 | f.close() 283 | except FileNotFoundError: 284 | self._save_games() 285 | except ValueError as e: 286 | _LOGGER.error('Games json file wrong: %s', e) 287 | 288 | def _save_games(self): 289 | try: 290 | with open(self._games_filename, 'w') as f: 291 | json.dump(self.games, f) 292 | f.close() 293 | except FileNotFoundError: 294 | pass 295 | 296 | def wake(self): 297 | """Wake PS4 up.""" 298 | return self.__call('on') 299 | 300 | def standby(self): 301 | """Set PS4 into standby mode.""" 302 | return self.__call('off') 303 | 304 | def start(self, title_id): 305 | """Start game using titleId.""" 306 | return self.__call('start', title_id) 307 | 308 | def remote(self, key): 309 | """Send remote key press.""" 310 | return self.__call('key', key) 311 | 312 | def search(self): 313 | """List current info.""" 314 | value = self.__call('info') 315 | 316 | if value is None: 317 | return {} 318 | 319 | try: 320 | data = json.loads(value) 321 | except json.decoder.JSONDecodeError as e: 322 | _LOGGER.error("Error decoding ps4 json : %s", e) 323 | data = {} 324 | 325 | """Save current game""" 326 | if data.get('running-app-titleid'): 327 | if data.get('running-app-titleid') not in self.games.keys(): 328 | game = {data.get('running-app-titleid'): 329 | data.get('running-app-name')} 330 | self.games.update(game) 331 | self._save_games() 332 | 333 | return data -------------------------------------------------------------------------------- /customize.yaml: -------------------------------------------------------------------------------- 1 | light.bar_2: 2 | templates: 3 | rgb_color: "if (state === 'off') return [68, 115, 158];" 4 | 5 | light.bathroom_1: 6 | templates: 7 | rgb_color: "if (state === 'off') return [68, 115, 158];" 8 | 9 | light.bathroom_2: 10 | templates: 11 | rgb_color: "if (state === 'off') return [68, 115, 158];" 12 | 13 | light.bathroom_3: 14 | templates: 15 | rgb_color: "if (state === 'off') return [68, 115, 158];" 16 | 17 | light.bed_left_2: 18 | templates: 19 | rgb_color: "if (state === 'off') return [68, 115, 158];" 20 | 21 | light.bed_right_2: 22 | templates: 23 | rgb_color: "if (state === 'off') return [68, 115, 158];" 24 | 25 | light.bedroom_overhead: 26 | templates: 27 | rgb_color: "if (state === 'off') return [68, 115, 158];" 28 | 29 | light.corner_2: 30 | templates: 31 | rgb_color: "if (state === 'off') return [68, 115, 158];" 32 | 33 | light.dining_2: 34 | templates: 35 | rgb_color: "if (state === 'off') return [68, 115, 158];" 36 | 37 | light.entrance_2: 38 | templates: 39 | rgb_color: "if (state === 'off') return [68, 115, 158];" 40 | 41 | light.far_corner_2: 42 | templates: 43 | rgb_color: "if (state === 'off') return [68, 115, 158];" 44 | 45 | light.hall_2: 46 | templates: 47 | rgb_color: "if (state === 'off') return [68, 115, 158];" 48 | 49 | sensor.litter_box: 50 | templates: 51 | icon_color: "if (state === 'Open') return [211, 47, 47];" 52 | 53 | sensor.pi_hole_ads_blocked_today: 54 | friendly_name: Ads Blocked 55 | 56 | sensor.pi_hole_ads_percentage_blocked_today: 57 | friendly_name: Percentage Blocked 58 | 59 | sensor.pi_hole_dns_queries_today: 60 | friendly_name: DNS Queries -------------------------------------------------------------------------------- /device_tracker.yaml: -------------------------------------------------------------------------------- 1 | - platform: tile 2 | username: !secret tile_user 3 | password: !secret tile_pass -------------------------------------------------------------------------------- /floorplan/floorplan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/floorplan/floorplan.jpg -------------------------------------------------------------------------------- /groups.yaml: -------------------------------------------------------------------------------- 1 | default_view: 2 | name: Home 3 | view: yes 4 | icon: mdi:home 5 | entities: 6 | - media_player.apartment 7 | - media_player.rx_v681_fd72a2 8 | - group.august 9 | - group.living_room_multi 10 | - group.living_room_lights 11 | - group.late_lights 12 | - group.right_side_lights 13 | 14 | bathroom: 15 | name: Bathroom 16 | view: yes 17 | icon: mdi:human-male-female 18 | entities: 19 | - group.bathroom_multi 20 | - group.bathroom_motion 21 | - group.bathroom_lights 22 | 23 | ### GROUPS ### 24 | amplifi: 25 | name: Ubiquiti Amplifi 26 | entities: 27 | - sensor.amplifi_router_bytes_received 28 | - sensor.amplifi_router_bytes_sent 29 | - sensor.amplifi_router_kbyte_sec_received 30 | - sensor.amplifi_router_kbyte_sec_sent 31 | - sensor.amplifi_router_packets_received 32 | - sensor.amplifi_router_packets_sent 33 | - sensor.amplifi_router_packets_sec_received 34 | - sensor.amplifi_router_packets_sec_sent 35 | 36 | bathroom_lights: 37 | name: Bathroom Lights 38 | entities: 39 | - light.bathroom_1 40 | - light.bathroom_2 41 | - light.bathroom_3 42 | 43 | bathroom_motion: 44 | name: Bathroom Motion Sensor 45 | entities: 46 | - sensor.bathroom_motion 47 | 48 | bathroom_multi: 49 | name: Bathroom Multi Sensor 50 | entities: 51 | - sensor.bathroom_multi_humid 52 | - sensor.bathroom_multi_lume 53 | - sensor.bathroom_multi_motion 54 | - sensor.bathroom_multi_temp 55 | 56 | living_room_lights: 57 | name: Living Room Lights 58 | entities: 59 | - light.bar_2 60 | - light.entrance_2 61 | - light.dining_2 62 | - light.far_corner_2 63 | - light.corner_2 64 | - light.hall_2 65 | 66 | right_side_lights: 67 | name: Right Side Lights 68 | entities: 69 | - light.far_corner_2 70 | - light.corner_2 71 | 72 | late_lights: 73 | name: Late Lights 74 | entities: 75 | - light.far_corner_2 76 | - light.corner_2 77 | - light.hall_2 78 | 79 | pi_hole: 80 | name: Pi-Hole 81 | entities: 82 | - sensor.pi_hole_ads_blocked_today 83 | - sensor.pi_hole_ads_percentage_today 84 | - sensor.pi_hole_dns_queries_today 85 | 86 | system_monitor: 87 | name: System Monitor 88 | entities: 89 | - sensor.disk_use_percent 90 | - sensor.disk_use 91 | - sensor.dick_free 92 | - sensor.memory_use 93 | - sensor.memory_free 94 | - sensor.processor_use 95 | - sensor.last_boot 96 | - sensor.network_in 97 | - sensor.network_out 98 | - sensor.ipv4_address 99 | - sensor.load_1m 100 | - sensor.load_5m 101 | - sensor.load_15m -------------------------------------------------------------------------------- /images/01_Lovelace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/01_Lovelace.png -------------------------------------------------------------------------------- /images/02_Data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/02_Data.png -------------------------------------------------------------------------------- /images/03_RPi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/03_RPi.png -------------------------------------------------------------------------------- /images/04_Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/04_Home.png -------------------------------------------------------------------------------- /images/05_Bathroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/05_Bathroom.png -------------------------------------------------------------------------------- /images/06_Kitchen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/06_Kitchen.png -------------------------------------------------------------------------------- /images/07_Bedroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/07_Bedroom.png -------------------------------------------------------------------------------- /images/Lovelace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/Lovelace.gif -------------------------------------------------------------------------------- /images/Phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/Phone.png -------------------------------------------------------------------------------- /images/Phone_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/Phone_01.png -------------------------------------------------------------------------------- /images/Phone_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/images/Phone_02.png -------------------------------------------------------------------------------- /media_player.yaml: -------------------------------------------------------------------------------- 1 | - platform: androidtv 2 | name: Fire Stick 4K 3 | host: 192.168.50.23 4 | adb_server_ip: 127.0.0.1 5 | 6 | - platform: kodi 7 | name: kodi_appletv 8 | host: 192.168.50.11 9 | username: !secret kodi_user 10 | password: !secret kodi_pass 11 | 12 | - platform: kodi 13 | name: kodi_firetv 14 | host: 192.168.50.23 15 | username: !secret kodi_user 16 | password: !secret kodi_pass -------------------------------------------------------------------------------- /remote.yaml: -------------------------------------------------------------------------------- 1 | - platform: harmony 2 | name: "Harmony Hub Bedroom" 3 | host: 192.168.50.40 4 | - platform: harmony 5 | name: "Harmony Hub Living Room" 6 | host: 192.168.50.41 -------------------------------------------------------------------------------- /scripts.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/scripts.yaml -------------------------------------------------------------------------------- /sensor.yaml: -------------------------------------------------------------------------------- 1 | ### Network ### 2 | # - platform: pi_hole 3 | # host: localhost:4865 4 | # location: "" 5 | # monitored_conditions: 6 | # - ads_blocked_today 7 | # - ads_percentage_today 8 | # - dns_queries_today 9 | 10 | - platform: systemmonitor 11 | resources: 12 | - type: disk_use_percent 13 | arg: / 14 | - type: disk_use 15 | arg: / 16 | - type: disk_free 17 | arg: / 18 | - type: memory_use 19 | - type: memory_free 20 | - type: processor_use 21 | - type: last_boot 22 | - type: network_in 23 | arg: eth0 24 | - type: network_out 25 | arg: eth0 26 | - type: ipv4_address 27 | arg: eth0 28 | - type: load_1m 29 | - type: load_5m 30 | - type: load_15m 31 | 32 | - platform: template 33 | sensors: 34 | living_motion_all: 35 | friendly_name: "Living Room Motion" 36 | icon_template: >- 37 | {% if is_state('binary_sensor.living_room_motion_motion', 'on') %} 38 | mdi:run 39 | {% else %} 40 | mdi:walk 41 | {% endif %} 42 | value_template: >- 43 | {% if is_state('binary_sensor.living_room_motion_motion', 'on') %} 44 | active 45 | {% else %} 46 | inactive 47 | {% endif %} 48 | - platform: template 49 | sensors: 50 | bedroom_motion_all: 51 | friendly_name: "Bedroom Motion All" 52 | icon_template: >- 53 | {% if is_state('binary_sensor.bedroom_motion_motion', 'on') or is_state('binary_sensor.bedroom_multi_sensor_motion', 'on') %} 54 | mdi:run 55 | {% else %} 56 | mdi:walk 57 | {% endif %} 58 | value_template: >- 59 | {% if is_state('binary_sensor.bedroom_motion_motion', 'on') or is_state('binary_sensor.bedroom_multi_sensor_motion', 'on') %} 60 | active 61 | {% else %} 62 | inactive 63 | {% endif %} 64 | - platform: template 65 | sensors: 66 | bathroom_motion_all: 67 | friendly_name: "Bathroom Motion All" 68 | icon_template: >- 69 | {% if is_state('binary_sensor.bathroom_motion_motion', 'on') or is_state('binary_sensor.shower_motion_motion', 'on') %} 70 | mdi:run 71 | {% else %} 72 | mdi:walk 73 | {% endif %} 74 | value_template: >- 75 | {% if is_state('binary_sensor.bathroom_motion_motion', 'on') or is_state('binary_sensor.shower_motion_motion', 'on') %} 76 | active 77 | {% else %} 78 | inactive 79 | {% endif %} 80 | - platform: template 81 | sensors: 82 | litter_box: 83 | value_template: >- 84 | {% if states.binary_sensor.litter_box_2_contact_2.state == 'on' %} 85 | Open 86 | {% elif states.binary_sensor.litter_box_2_contact_2.state == 'off' %} 87 | Closed 88 | {% else %} 89 | Failed 90 | {% endif %} 91 | icon_template: >- 92 | {% if states.binary_sensor.litter_box_2_contact_2.state == 'on' %} 93 | mdi:delete 94 | {% elif states.binary_sensor.litter_box_2_contact_2.state == 'off' %} 95 | mdi:cat 96 | {% endif %} 97 | whole_fridge: 98 | value_template: >- 99 | {% if states.binary_sensor.fridge_contact.state == 'on' and states.binary_sensor.freezer_contact.state == 'off' %} 100 | Fridge Open 101 | {% elif states.binary_sensor.fridge_contact.state == 'off' and states.binary_sensor.freezer_contact.state == 'on' %} 102 | Freezer Open 103 | {% elif states.binary_sensor.fridge_contact.state == 'off' and states.binary_sensor.freezer_contact.state == 'off' %} 104 | Closed 105 | {% elif states.binary_sensor.fridge_contact.state == 'on' and states.binary_sensor.freezer_contact.state == 'on' %} 106 | Open 107 | {% endif %} 108 | icon_template: >- 109 | {% if states.binary_sensor.fridge_contact.state == 'on' and states.binary_sensor.freezer_contact.state == 'off' %} 110 | mdi:fridge-bottom 111 | {% elif states.binary_sensor.fridge_contact.state == 'off' and states.binary_sensor.freezer_contact.state == 'on' %} 112 | mdi:fridge-top 113 | {% elif states.binary_sensor.fridge_contact.state == 'off' and states.binary_sensor.freezer_contact.state == 'off' %} 114 | mdi:fridge 115 | {% elif states.binary_sensor.fridge_contact.state == 'on' and states.binary_sensor.freezer_contact.state == 'on' %} 116 | mdi:fridge-outline 117 | {% endif %} 118 | - platform: command_line 119 | name: CPU Temperature 120 | command: "cat /sys/class/thermal/thermal_zone0/temp" 121 | unit_of_measurement: "°C" 122 | value_template: '{{ value | multiply(0.001) | round(1) }}' 123 | 124 | - platform: rest 125 | name: "HASS Current Version" 126 | resource: https://pypi.python.org/pypi/homeassistant/json 127 | value_template: '{{ value_json.info.version }}' 128 | scan_interval: 3600 129 | 130 | - platform: command_line 131 | name: "HASS Installed version" 132 | command: "head -5 /config/.HA_VERSION" -------------------------------------------------------------------------------- /switch.yaml: -------------------------------------------------------------------------------- 1 | - platform: template 2 | switches: 3 | watch_cable_tv: 4 | friendly_name: Watch Cable TV 5 | value_template: "{{ is_state_attr('remote.harmony_hub_living_room', 'current_activity', 'Watch tv') }}" 6 | turn_on: 7 | service: remote.turn_on 8 | data: 9 | entity_id: remote.harmony_hub_living_room 10 | activity: 'Watch TV' 11 | turn_off: 12 | service: remote.turn_on 13 | data: 14 | entity_id: remote.harmony_hub_living_room 15 | activity: 'PowerOff' 16 | - platform: template 17 | switches: 18 | watch_apple_tv: 19 | friendly_name: Watch Apple TV 20 | value_template: "{{ is_state_attr('remote.harmony_hub_living_room', 'current_activity', 'Watch Apple TV') }}" 21 | turn_on: 22 | service: remote.turn_on 23 | data: 24 | entity_id: remote.harmony_hub_living_room 25 | activity: 'Watch Apple TV' 26 | turn_off: 27 | service: remote.turn_on 28 | data: 29 | entity_id: remote.harmony_hub_living_room 30 | activity: 'PowerOff' 31 | - platform: template 32 | switches: 33 | start_ps4: 34 | friendly_name: Start PS4 35 | value_template: "{{ is_state_attr('remote.harmony_hub_living_room', 'current_activity', 'Start PS4') }}" 36 | turn_on: 37 | service: remote.turn_on 38 | data: 39 | entity_id: remote.harmony_hub_living_room 40 | activity: 'Start PS4' 41 | turn_off: 42 | service: remote.turn_on 43 | data: 44 | entity_id: remote.harmony_hub_living_room 45 | activity: 'PowerOff' 46 | - platform: template 47 | switches: 48 | nintendo_switch: 49 | friendly_name: Nintendo Switch 50 | value_template: "{{ is_state_attr('remote.harmony_hub_living_room', 'current_activity', 'Nintendo Switch') }}" 51 | turn_on: 52 | service: remote.turn_on 53 | data: 54 | entity_id: remote.harmony_hub_living_room 55 | activity: 'Nintendo Switch' 56 | turn_off: 57 | service: remote.turn_on 58 | data: 59 | entity_id: remote.harmony_hub_living_room 60 | activity: 'PowerOff' -------------------------------------------------------------------------------- /themes.yaml: -------------------------------------------------------------------------------- 1 | dark: 2 | # Background image 3 | background-image: 'center / cover no-repeat url("/local/night.jpg") fixed' 4 | 5 | # Colors 6 | text-color: '#DADADB' # Grey text 7 | text-medium-light-color: '#A0A2A8' # Medium-light grey text 8 | text-medium-color: '#80828A' # Medium grey text 9 | text-dark-color: '#6A6B74' # Dark grey text 10 | accent-color: '#008bef' # Blue 11 | accent-medium-color: '#2686c1' # Decent blue 12 | background-color: '#3b4049' # Dark grey background 13 | background-color-2: '#484E59' # Light grey background 14 | background-card-color: '#434952' # Grey background 15 | border-color: '#383C46' # Grey border 16 | 17 | # Header 18 | primary-color: '#363941' # Background 19 | text-primary-color: 'var(--text-color)' # Text 20 | 21 | # Left Menu 22 | paper-listbox-background-color: 'var(--background-color)' # Background 23 | sidebar-icon-color: 'var(--text-medium-color)' # icons 24 | sidebar-selected-icon-color: 'var(--text-medium-light-color)' # Selected row icon and background (15%) 25 | sidebar-selected-text-color: 'var(--text-color)' # Selected row label 26 | 27 | # UI 28 | paper-card-header-color: 'var(--text-color)' # Title in settings 29 | primary-background-color: 'var(--background-color)' # Background (also title background in left menu) 30 | mdc-theme-primary: 'var(--accent-medium-color)' # Action Buttons (save, restart etc.) 31 | 32 | # Card 33 | paper-card-background-color: 'var(--background-card-color)' # Background 34 | dark-primary-color: 'var(--text-color)' 35 | primary-text-color: 'var(--text-color)' 36 | paper-listbox-color: 'var(--text-color)' 37 | light-primary-color: 'var(--text-dark-color)' 38 | secondary-text-color: 'var(--text-medium-color)' 39 | disabled-text-color: 'var(--text-dark-color)' 40 | paper-dialog-button-color: 'var(--text-color)' 41 | secondary-background-color: 'var(--background-color-2)' # Background more info title 42 | 43 | # Icons 44 | paper-item-icon-color: 'var(--text-dark-color)' # Off 45 | paper-item-icon-active-color: 'var(--accent-color)' # On 46 | 47 | # Switches 48 | paper-toggle-button-checked-button-color: 'var(--text-medium-light-color)' # Knob On 49 | paper-toggle-button-checked-bar-color: '#009FFF' # Background On 50 | paper-toggle-button-unchecked-button-color: 'var(--text-medium-light-color)' # Knob Off 51 | paper-toggle-button-unchecked-bar-color: '#767682' # Background Off 52 | 53 | # Shadows 54 | shadow-elevation-2dp_-_box-shadow: 'inset 0px 0px 0px 1px var(--border-color)' 55 | shadow-elevation-4dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 56 | shadow-elevation-6dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 57 | shadow-elevation-8dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 58 | shadow-elevation-10dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 59 | shadow-elevation-12dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 60 | shadow-elevation-14dp_-_box-shadow: 'var(--shadow-elevation-2dp_-_box-shadow)' 61 | shadow-elevation-16dp_-_box-shadow: '0px 0px 0px 3px var(--text-dark-color)' 62 | 63 | coned_miro: 64 | # Main colors 65 | primary-color: '#d32f2f' 66 | accent-color: '#b58e31' 67 | dark-primary-color: '#c66900' 68 | light-primary-color: '#e06c6c' 69 | # Text colors 70 | primary-text-color: '#FFFFFF' 71 | text-primary-color: 'var(--primary-text-color)' 72 | secondary-text-color: '#b58e31' 73 | disabled-text-color: '#777777' 74 | label-badge-border-color: 'green' 75 | # Sidebar 76 | sidebar-icon-color: '#777777' 77 | # Background colors 78 | primary-background-color: '#222222' 79 | secondary-background-color: '#222222' 80 | divider-color: 'rgba(0, 0, 0, .12)' 81 | table-row-background-color: '#292929' 82 | table-row-alternative-background-color: '#292929' 83 | # Nav Menu 84 | paper-listbox-color: '#777777' 85 | paper-listbox-background-color: '#272822' 86 | paper-grey-50: 'var(--primary-text-color)' 87 | paper-grey-200: '#222222' 88 | # Paper card 89 | paper-card-header-color: '#2980b9' 90 | paper-card-background-color: '#292929' 91 | paper-dialog-background-color: '#292929' 92 | paper-item-icon-color: '#44739e' 93 | paper-item-icon-active-color: '#b58e31' 94 | paper-item-icon_-_color: 'green' 95 | paper-item-selected_-_background-color: '#292929' 96 | paper-tabs-selection-bar-color: 'green' 97 | # Labels 98 | label-badge-red: 'var(--primary-color)' 99 | label-badge-text-color: 'var(--primary-text-color)' 100 | label-badge-background-color: '#222222' 101 | # Switches 102 | paper-toggle-button-checked-button-color: '#2980b9' 103 | paper-toggle-button-checked-bar-color: '#2980b9' 104 | paper-toggle-button-checked-ink-color: '#2980b9' 105 | paper-toggle-button-unchecked-button-color: 'var(--disabled-text-color)' 106 | paper-toggle-button-unchecked-bar-color: 'var(--disabled-text-color)' 107 | paper-toggle-button-unchecked-ink-color: 'var(--disabled-text-color)' 108 | # Sliders 109 | paper-slider-knob-color: '#2980b9' 110 | paper-slider-knob-start-color: '#2980b9' 111 | paper-slider-pin-color: '#2980b9' 112 | paper-slider-active-color: '#2980b9' 113 | paper-slider-container-color: 'linear-gradient(var(--primary-background-color), var(--secondary-background-color)) no-repeat' 114 | paper-slider-secondary-color: 'var(--secondary-background-color)' 115 | paper-slider-disabled-active-color: 'var(--disabled-text-color)' 116 | paper-slider-disabled-secondary-color: 'var(--disabled-text-color)' 117 | # Google colors 118 | google-red-500: '#b93829' 119 | google-green-500: '#2980b9' 120 | #Changes to fix history/logbook menus 121 | lumo-primary-text-color: '#2980b9' 122 | lumo-secondary-text-color: '#2980b9' 123 | lumo-primary-color: '#2980b9' 124 | #Calendar day numbers 125 | lumo-body-text-color: '#b58e31' 126 | #Calendar/Date-Picker Background 127 | lumo-base-color: '#222222' 128 | #Month/Year header 129 | lumo-header-text-color: 'var(--lumo-body-text-color)' 130 | #DayOfWeek Header 131 | lumo-tertiary-text-color: 'var(--lumo-body-text-color)' 132 | lumo-shade: '#222222' 133 | lumo-shade-90pct: 'rgba(34, 34, 34, .9)' 134 | lumo-shade-80pct: 'rgba(34, 34, 34, .8)' 135 | lumo-shade-70pct: 'rgba(34, 34, 34, .7)' 136 | lumo-shade-60pct: 'rgba(34, 34, 34, .6)' 137 | lumo-shade-50pct: 'rgba(34, 34, 34, .5)' 138 | lumo-shade-40pct: 'rgba(34, 34, 34, .4)' 139 | lumo-shade-30pct: 'rgba(34, 34, 34, .3)' 140 | lumo-shade-20pct: 'rgba(34, 34, 34, .2)' 141 | lumo-shade-10pct: 'rgba(34, 34, 34, .1)' 142 | lumo-shade-5pct: 'rgba(34, 34, 34, .05)' 143 | lumo-tint-5pct: '#222222' 144 | -------------------------------------------------------------------------------- /www/Lovelace/floorplan10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/floorplan10.png -------------------------------------------------------------------------------- /www/Lovelace/floorplan7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/floorplan7.png -------------------------------------------------------------------------------- /www/Lovelace/floorplan_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/floorplan_grey.png -------------------------------------------------------------------------------- /www/Lovelace/floorplan_grey2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/floorplan_grey2.png -------------------------------------------------------------------------------- /www/Lovelace/freezer_closed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/freezer_closed2.png -------------------------------------------------------------------------------- /www/Lovelace/freezer_open2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/freezer_open2.png -------------------------------------------------------------------------------- /www/Lovelace/fridge_closed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/fridge_closed2.png -------------------------------------------------------------------------------- /www/Lovelace/fridge_open2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/fridge_open2.png -------------------------------------------------------------------------------- /www/Lovelace/tree_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/tree_off.png -------------------------------------------------------------------------------- /www/Lovelace/tree_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/tree_on.png -------------------------------------------------------------------------------- /www/Lovelace/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Lovelace/unlock.png -------------------------------------------------------------------------------- /www/Mobile/Bathroom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Bathroom.jpg -------------------------------------------------------------------------------- /www/Mobile/Bedroom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Bedroom.jpg -------------------------------------------------------------------------------- /www/Mobile/Entrance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Entrance.jpg -------------------------------------------------------------------------------- /www/Mobile/Kitchen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Kitchen.jpg -------------------------------------------------------------------------------- /www/Mobile/Living_Room.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Living_Room.jpg -------------------------------------------------------------------------------- /www/Mobile/Living_Room_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/Mobile/Living_Room_2.jpg -------------------------------------------------------------------------------- /www/community/calendar-card/calendar-card.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/calendar-card/calendar-card.js.gz -------------------------------------------------------------------------------- /www/community/compact-custom-header/compact-custom-header-editor.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/compact-custom-header/compact-custom-header-editor.js.gz -------------------------------------------------------------------------------- /www/community/compact-custom-header/compact-custom-header.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/compact-custom-header/compact-custom-header.js.gz -------------------------------------------------------------------------------- /www/community/mini-graph-card/mini-graph-card.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/mini-graph-card/mini-graph-card.js.gz -------------------------------------------------------------------------------- /www/community/mini-media-player/mini-media-player.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/mini-media-player/mini-media-player.js.gz -------------------------------------------------------------------------------- /www/community/radial-menu/radial-menu.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/radial-menu/radial-menu.js.gz -------------------------------------------------------------------------------- /www/community/simple-thermostat/simple-thermostat.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldvinci/Home-AssistantConfig/b45a163344af5e6dd48c95bc063c2645f4386b91/www/community/simple-thermostat/simple-thermostat.js.gz -------------------------------------------------------------------------------- /www/custom-cards/plan-coordinates.js: -------------------------------------------------------------------------------- 1 | class PlanCoordinates extends HTMLElement { 2 | 3 | constructor() { 4 | super(); 5 | this.attachShadow({ mode: 'open' }); 6 | } 7 | 8 | setConfig(config) { 9 | const root = this.shadowRoot; 10 | if (root.lastChild) root.removeChild(root.lastChild); 11 | this.style.display = 'none'; 12 | const card = document.createElement('div'); 13 | card.id = "plan-coordinates" 14 | const content = document.createElement('div'); 15 | const style = document.createElement('style'); 16 | style.textContent = ` 17 | #plan-coordinates { 18 | position: absolute; 19 | right: 0px; 20 | top: 16px; 21 | z-index: 1000; 22 | } 23 | #content { 24 | background-color: var(--paper-card-background-color); 25 | border: 1px solid var(--label-badge-blue); 26 | padding: 16px; 27 | } 28 | `; 29 | content.id = "content"; 30 | card.appendChild(content); 31 | root.appendChild(style); 32 | root.appendChild(card); 33 | document.addEventListener("mousemove", el => { 34 | let calc_top = 16 - (document.body.querySelector('home-assistant').getBoundingClientRect().top); 35 | card.style.top = `${calc_top}px`; 36 | if (el.path[0] && el.path[0].tagName == 'IMG') { 37 | this.style.display = 'block'; 38 | const percentX = Math.ceil((el.clientX - el.path[0].x) * 100 / el.path[0].width); 39 | const percentY = Math.ceil((el.clientY - el.path[0].y) * 100 / el.path[0].height); 40 | content.innerHTML = `left: ${percentX}%