├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── automation ├── audio.yaml ├── div.yaml └── soverom.yaml ├── binary_sensor.yaml ├── configuration.yaml ├── custom_components ├── effect_control.py ├── find.py ├── news.py ├── restore_states.py ├── resume_mp.py ├── sensor │ └── brain_fm.py ├── switch │ ├── gmusic.py │ └── switchmate.py └── tibber_prices.py ├── customize.yaml ├── group.yaml ├── input_select.yaml ├── lights.yaml ├── ml_script └── stovsuger.py ├── packages ├── alarm_clock.yaml ├── heating.yaml └── varmeteppe.yaml ├── scripts └── soverom.yaml ├── sensors.yaml └── switches.yaml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Danielhiversen 4 | custom: http://paypal.me/dahoiv 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config/* 2 | !config/home-assistant.conf.default 3 | 4 | # There is not a better solution afaik.. 5 | !config/custom_components 6 | config/custom_components/* 7 | !config/custom_components/example.py 8 | !config/custom_components/hello_world.py 9 | !config/custom_components/mqtt_example.py 10 | !config/panels 11 | config/panels/* 12 | !config/panels/react.html 13 | 14 | tests/testing_config/deps 15 | tests/testing_config/home-assistant.log 16 | 17 | # Hide sublime text stuff 18 | *.sublime-project 19 | *.sublime-workspace 20 | 21 | # Hide some OS X stuff 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | Icon 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | .idea 31 | 32 | # pytest 33 | .cache 34 | 35 | # GITHUB Proposed Python stuff: 36 | *.py[cod] 37 | 38 | # C extensions 39 | *.so 40 | 41 | # Packages 42 | *.egg 43 | *.egg-info 44 | dist 45 | build 46 | eggs 47 | .eggs 48 | parts 49 | bin 50 | var 51 | sdist 52 | develop-eggs 53 | .installed.cfg 54 | lib 55 | lib64 56 | 57 | # Logs 58 | *.log 59 | pip-log.txt 60 | 61 | # Unit test / coverage reports 62 | .coverage 63 | .tox 64 | nosetests.xml 65 | htmlcov/ 66 | 67 | # Translations 68 | *.mo 69 | 70 | # Mr Developer 71 | .mr.developer.cfg 72 | .project 73 | .pydevproject 74 | 75 | .python-version 76 | 77 | # emacs auto backups 78 | *~ 79 | *# 80 | *.orig 81 | 82 | # venv stuff 83 | pyvenv.cfg 84 | pip-selfcheck.json 85 | venv 86 | .venv 87 | 88 | # vimmy stuff 89 | *.swp 90 | *.swo 91 | 92 | ctags.tmp 93 | 94 | # vagrant stuff 95 | virtualization/vagrant/setup_done 96 | virtualization/vagrant/.vagrant 97 | virtualization/vagrant/config 98 | 99 | # Visual Studio Code 100 | .vscode 101 | 102 | # Built docs 103 | docs/build 104 | 105 | # Windows Explorer 106 | desktop.ini 107 | 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # home-assistant_config 2 | Example Home Assistant Configs, home-assistant.io 3 | 4 | 5 | Tested with version 0.73 6 | 7 | [Buy me a coffee :)](http://paypal.me/dahoiv) 8 | 9 | 10 | More custom components: https://github.com/Danielhiversen/home-assistant_custom_components 11 | -------------------------------------------------------------------------------- /automation/audio.yaml: -------------------------------------------------------------------------------- 1 | # https://www.nrk.no/mp3aac/ 2 | - alias: 'Radio stue' 3 | trigger: 4 | platform: state 5 | entity_id: input_select.radio_stue 6 | action: 7 | - service: media_player.play_media 8 | entity_id: media_player.stue_kjokken 9 | data_template: 10 | media_content_id: >- 11 | {% if is_state('input_select.radio_stue', 'Alltid Nyheter') %} 12 | http://lyd.nrk.no/nrk_radio_alltid_nyheter_mp3_h 13 | {% elif is_state('input_select.radio_stue', 'P1') %} 14 | http://lyd.nrk.no/nrk_radio_p1_trondelag_mp3_h 15 | {% elif is_state('input_select.radio_stue', 'P3') %} 16 | http://nrk-mms-live.online.no/nrk_radio_p3_mp3_h 17 | {% elif is_state('input_select.radio_stue', 'P2') %} 18 | http://nrk-mms-live.online.no/nrk_radio_p2_mp3_h 19 | {% endif %} 20 | media_content_type: audio/mp3 21 | 22 | - alias: "Auto radio stue" 23 | initial_state: False 24 | trigger: 25 | - platform: state 26 | entity_id: media_player.stue_kjokken 27 | to: 'idle' 28 | for: 29 | seconds: 3 30 | action: 31 | - service: homeassistant.turn_off 32 | entity_id: automation.auto_radio_stue 33 | - service: input_select.select_option 34 | entity_id: input_select.radio_stue 35 | data: 36 | option: "P3" 37 | - service: automation.trigger 38 | entity_id: automation.radio_stue 39 | 40 | - alias: "P2 stue" 41 | trigger: 42 | - platform: event 43 | event_type: cube_action 44 | event_data: 45 | entity_id: binary_sensor.cube_158d0001118a8a 46 | action_type: flip90 47 | condition: 48 | - condition: template 49 | value_template: > 50 | {% if is_state('media_player.stue', 'playing') or is_state('media_player.stue_kjokken', 'playing') %} 51 | false 52 | {% else %} 53 | true 54 | {% endif %} 55 | action: 56 | - service: homeassistant.turn_off 57 | entity_id: switch.google_music 58 | - service: media_player.turn_on 59 | entity_id: media_player.stue_kjokken 60 | - service: media_player.volume_set 61 | data: 62 | entity_id: media_player.stue_kjokken 63 | volume_level: 0.23 64 | - service: input_select.select_option 65 | entity_id: input_select.radio_stue 66 | data: 67 | option: "P2" 68 | - service: automation.trigger 69 | entity_id: automation.radio_stue 70 | 71 | - alias: "Endre radiokanal stue" 72 | trigger: 73 | - platform: event 74 | event_type: cube_action 75 | event_data: 76 | entity_id: binary_sensor.cube_158d0001118a8a 77 | action_type: flip90 78 | condition: 79 | - condition: template 80 | value_template: > 81 | {% if is_state('media_player.stue', 'playing') or is_state('media_player.stue_kjokken', 'playing') %} 82 | true 83 | {% else %} 84 | false 85 | {% endif %} 86 | action: 87 | - service: homeassistant.turn_off 88 | entity_id: switch.google_music 89 | - service: input_select.select_next 90 | entity_id: input_select.radio_stue 91 | 92 | - alias: "Nyheter stue" 93 | trigger: 94 | - platform: event 95 | event_type: cube_action 96 | event_data: 97 | entity_id: binary_sensor.cube_158d0001118a8a 98 | action_type: shake_air 99 | condition: 100 | - condition: template 101 | value_template: > 102 | {% if is_state('media_player.stue', 'playing') or is_state('media_player.stue_kjokken', 'playing') %} 103 | false 104 | {% else %} 105 | true 106 | {% endif %} 107 | action: 108 | - service: media_player.turn_on 109 | entity_id: media_player.stue 110 | - service: media_player.volume_set 111 | data: 112 | entity_id: media_player.stue_kjokken 113 | volume_level: 0.23 114 | - service: news.read_news 115 | entity_id: media_player.stue_kjokken 116 | - service: homeassistant.turn_on 117 | entity_id: automation.auto_radio_stue 118 | 119 | - alias: "Nyheter stue stop" 120 | trigger: 121 | - platform: event 122 | event_type: cube_action 123 | event_data: 124 | entity_id: binary_sensor.cube_158d0001118a8a 125 | action_type: shake_air 126 | condition: 127 | - condition: template 128 | value_template: > 129 | {% if is_state('media_player.stue', 'playing') or is_state('media_player.stue_kjokken', 'playing') %} 130 | true 131 | {% else %} 132 | false 133 | {% endif %} 134 | action: 135 | - service: homeassistant.turn_off 136 | entity_id: switch.google_music 137 | - service: homeassistant.turn_off 138 | entity_id: automation.auto_radio_stue 139 | - service: media_player.turn_off 140 | entity_id: media_player.stue_kjokken 141 | 142 | - alias: "Cube i fritt fall, stue" 143 | trigger: 144 | platform: event 145 | event_type: cube_action 146 | event_data: 147 | entity_id: binary_sensor.cube_158d0001118a8a 148 | action_type: free_fall 149 | action: 150 | - service: media_player.turn_on 151 | entity_id: media_player.stue 152 | - service: media_player.volume_set 153 | data: 154 | entity_id: media_player.stue_kjokken 155 | volume_level: 0.23 156 | - service: tts.google_say 157 | entity_id: media_player.stue_kjokken 158 | data_template: 159 | message: 'Au, au. Vær litt forsiktig da.' 160 | cache: true 161 | 162 | - alias: "Volum stue" 163 | trigger: 164 | platform: event 165 | event_type: cube_action 166 | event_data: 167 | entity_id: binary_sensor.cube_158d0001118a8a 168 | action_type: rotate 169 | action: 170 | - service: media_player.volume_set 171 | data_template: 172 | entity_id: media_player.stue_kjokken 173 | volume_level: > 174 | {% set state = states.media_player.stue_kjokken.attributes.volume_level + (trigger.event.data.action_value|float)/90 -%} 175 | {%- if state > 1 -%} 176 | {%- set state = 1 -%} 177 | {%- elif state < 0 -%} 178 | {%- set state = 0 -%} 179 | {%- endif %} 180 | {{ state }} 181 | - alias: "Gmusic stue" 182 | trigger: 183 | platform: event 184 | event_type: cube_action 185 | event_data: 186 | entity_id: binary_sensor.cube_158d0001118a8a 187 | action_type: tap_twice 188 | action: 189 | - service: homeassistant.turn_off 190 | entity_id: switch.google_music 191 | - service: input_select.select_option 192 | entity_id: input_select.media_player 193 | data: 194 | option: "stue" 195 | - service: input_select.select_next 196 | entity_id: input_select.music 197 | - service: homeassistant.turn_on 198 | entity_id: switch.google_music 199 | 200 | 201 | 202 | 203 | # SOVEROM 204 | 205 | 206 | - alias: "Radio soverom av" 207 | trigger: 208 | - platform: state 209 | entity_id: input_select.radio_soverom 210 | to: 'Av' 211 | action: 212 | - service: media_player.turn_off 213 | entity_id: media_player.soverom 214 | 215 | # https://www.nrk.no/mp3aac/ 216 | - alias: 'Radio soverom' 217 | trigger: 218 | platform: state 219 | entity_id: input_select.radio_soverom 220 | action: 221 | - service: media_player.play_media 222 | entity_id: media_player.soverom 223 | data_template: 224 | media_content_id: >- 225 | {% if is_state('input_select.radio_soverom', 'Alltid Nyheter') %} 226 | http://lyd.nrk.no/nrk_radio_alltid_nyheter_mp3_h 227 | {% elif is_state('input_select.radio_soverom', 'P1') %} 228 | http://lyd.nrk.no/nrk_radio_p1_trondelag_mp3_h 229 | {% elif is_state('input_select.radio_soverom', 'P3') %} 230 | http://nrk-mms-live.online.no/nrk_radio_p3_mp3_h 231 | {% elif is_state('input_select.radio_bad', 'P2') %} 232 | http://nrk-mms-live.online.no/nrk_radio_p2_mp3_h 233 | {% endif %} 234 | media_content_type: audio/mp3 235 | 236 | - alias: "Nyheter soverom" 237 | trigger: 238 | - platform: event 239 | event_type: click 240 | event_data: 241 | entity_id: binary_sensor.switch_158d0001165868 242 | click_type: double 243 | condition: 244 | - condition: template 245 | value_template: > 246 | {% if is_state('media_player.soverom', 'playing') %} 247 | false 248 | {% else %} 249 | true 250 | {% endif %} 251 | action: 252 | - service: media_player.volume_set 253 | data: 254 | entity_id: media_player.soverom 255 | volume_level: 0.55 256 | - service: news.read_news 257 | data: 258 | message_type: "1" 259 | entity_id: media_player.soverom 260 | - delay: '00:00:05' 261 | - service: homeassistant.turn_on 262 | entity_id: automation.auto_radio_soverom 263 | 264 | - alias: "Nyheter soverom stop" 265 | trigger: 266 | - platform: event 267 | event_type: click 268 | event_data: 269 | entity_id: binary_sensor.switch_158d0001165868 270 | click_type: double 271 | condition: 272 | - condition: state 273 | entity_id: media_player.soverom 274 | state: 'playing' 275 | action: 276 | - service: homeassistant.turn_off 277 | entity_id: automation.auto_radio_soverom 278 | - service: media_player.turn_off 279 | entity_id: media_player.soverom 280 | 281 | 282 | 283 | #GENERELT 284 | 285 | 286 | - alias: "Radio av" 287 | trigger: 288 | - platform: state 289 | entity_id: input_select.radio 290 | to: 'Av' 291 | action: 292 | - service: media_player.turn_off 293 | entity_id: media_player.hele_huset 294 | 295 | # https://www.nrk.no/mp3aac/ 296 | - alias: 'Radio' 297 | trigger: 298 | platform: state 299 | entity_id: input_select.radio 300 | action: 301 | - service: media_player.play_media 302 | entity_id: media_player.hele_huset 303 | data_template: 304 | media_content_id: >- 305 | {% if is_state('input_select.radio', 'Alltid Nyheter') %} 306 | http://lyd.nrk.no/nrk_radio_alltid_nyheter_mp3_h 307 | {% elif is_state('input_select.radio', 'P1') %} 308 | http://lyd.nrk.no/nrk_radio_p1_trondelag_mp3_h 309 | {% elif is_state('input_select.radio', 'P3') %} 310 | http://nrk-mms-live.online.no/nrk_radio_p3_mp3_h 311 | {% elif is_state('input_select.radio_bad', 'P2') %} 312 | http://nrk-mms-live.online.no/nrk_radio_p2_mp3_h 313 | {% endif %} 314 | media_content_type: audio/mp3 315 | - service: media_player.volume_set 316 | data: 317 | entity_id: media_player.hele_huset 318 | volume_level: 0.23 319 | -------------------------------------------------------------------------------- /automation/div.yaml: -------------------------------------------------------------------------------- 1 | 2 | - alias: "Lade telefonen" 3 | trigger: 4 | - platform: numeric_state 5 | entity_id: sensor.phone_battery 6 | below: 7 7 | condition: 8 | - condition: time 9 | after: '07:00:00' 10 | before: '23:00:00' 11 | - condition: state 12 | entity_id: device_tracker.daniel 13 | state: 'home' 14 | action: 15 | - service: media_player.turn_on 16 | entity_id: media_player.stue 17 | - service: media_player.volume_set 18 | data: 19 | entity_id: media_player.stue 20 | volume_level: 0.5 21 | - service: tts.google_say 22 | entity_id: media_player.stue 23 | data_template: 24 | message: 'Husk å lade telefonen Daniel, da batterienivået er {{states.sensor.phone_battery.state | float | round(0)}} prosent.' 25 | cache: false 26 | 27 | 28 | - alias: "Stopp lading av telefonen" 29 | trigger: 30 | - platform: numeric_state 31 | entity_id: sensor.phone_battery 32 | above: 85 33 | condition: 34 | - condition: time 35 | after: '07:00:00' 36 | before: '23:00:00' 37 | action: 38 | - service: notify.pushbullet 39 | data: 40 | title: "Batteriet er fult" 41 | message: 'Husk å koble fra mobiladeren, da batterienivået er {{states.sensor.phone_battery.state | float | round(0)}} prosent.' 42 | - condition: and 43 | conditions: 44 | - condition: state 45 | entity_id: device_tracker.daniel 46 | state: 'home' 47 | # - service: media_player.turn_on 48 | # entity_id: media_player.stue 49 | # - service: media_player.volume_set 50 | # data: 51 | # entity_id: media_player.stue 52 | # volume_level: 0.5 53 | # - service: tts.google_say 54 | # entity_id: media_player.stue 55 | # data_template: 56 | # message: 'Husk å koble fra mobiladeren, da batterienivået er {{states.sensor.phone_battery.state | float | round(0)}} prosent.' 57 | # cache: false 58 | 59 | - alias: 'Dim lights when playing Netflix in Living Room' 60 | trigger: 61 | platform: state 62 | entity_id: media_player.tv 63 | to: 'playing' 64 | condition: 65 | condition: and 66 | conditions: 67 | - condition: state 68 | entity_id: light.stue 69 | state: 'on' 70 | - condition: sun 71 | after: sunset 72 | after_offset: "-0:20:00" 73 | - condition: template 74 | value_template: '{{ states.media_player.tv.attributes.app_name == "Netflix" }}' 75 | action: 76 | - service: homeassistant.turn_on 77 | entity_id: light.stue 78 | data: 79 | brightness: 101 80 | -------------------------------------------------------------------------------- /automation/soverom.yaml: -------------------------------------------------------------------------------- 1 | 2 | - alias: "Lysstyrke soverom, knapp" 3 | trigger: 4 | - platform: event 5 | event_type: click 6 | event_data: 7 | entity_id: binary_sensor.switch_158d0001165868 8 | click_type: long_click_press 9 | condition: 10 | - condition: state 11 | entity_id: light.soverom1 12 | state: 'off' 13 | - condition: state 14 | entity_id: light.soverom2 15 | state: 'off' 16 | action: 17 | - service: homeassistant.turn_on 18 | entity_id: script.brightness_soverom 19 | 20 | - alias: "Lys av pA soverom, knapp" 21 | trigger: 22 | - platform: event 23 | event_type: click 24 | event_data: 25 | entity_id: binary_sensor.switch_158d0001165868 26 | click_type: long_click_press 27 | condition: 28 | - condition: or 29 | conditions: 30 | - condition: state 31 | entity_id: light.soverom1 32 | state: 'on' 33 | - condition: state 34 | entity_id: light.soverom2 35 | state: 'on' 36 | action: 37 | - service: homeassistant.turn_off 38 | entity_id: 39 | - light.soverom 40 | - script.vekke_lys 41 | - service: homeassistant.turn_off 42 | entity_id: 43 | - light.soverom 44 | 45 | 46 | - alias: "Lysstyrke soverom stopp, knapp" 47 | trigger: 48 | - platform: event 49 | event_type: click 50 | event_data: 51 | entity_id: binary_sensor.switch_158d0001165868 52 | click_type: hold 53 | condition: 54 | action: 55 | - service: homeassistant.turn_off 56 | entity_id: script.brightness_soverom, script.brightness_soverom_temp 57 | 58 | 59 | - alias: "Klokke soverom" 60 | trigger: 61 | - platform: event 62 | event_type: click 63 | event_data: 64 | entity_id: binary_sensor.switch_158d0001165868 65 | click_type: single 66 | action: 67 | - service: media_player.turn_on 68 | entity_id: media_player.soverom 69 | - service: media_player.volume_set 70 | data: 71 | entity_id: media_player.soverom 72 | volume_level: 0.55 73 | - service: tts.google_say 74 | entity_id: media_player.soverom 75 | data_template: 76 | message: 'Klokken er {{ now().hour}} {{ "%0.02d" | format(now().strftime("%-M") | int) }}' 77 | cache: false 78 | -------------------------------------------------------------------------------- /binary_sensor.yaml: -------------------------------------------------------------------------------- 1 | - platform: trend 2 | sensors: 3 | daniel_position: 4 | entity_id: sensor.phone_to_home 5 | invert: True 6 | max_samples: 5 7 | sample_duration: 600 8 | -------------------------------------------------------------------------------- /configuration.yaml: -------------------------------------------------------------------------------- 1 | homeassistant: 2 | # Location required to calculate the time the sun rises and sets 3 | latitude: !secret latitude 4 | longitude: !secret longitude 5 | elevation: 47 6 | unit_system: metric 7 | # Pick yours from here: 8 | # http://en.wikipedia.org/wiki/List_of_tz_database_time_zones 9 | time_zone: Europe/Oslo 10 | # Name of the location where Home Assistant is running 11 | name: _ 12 | customize: !include customize.yaml 13 | packages: !include_dir_named packages 14 | 15 | frontend: 16 | 17 | updater: 18 | include_used_components: true 19 | 20 | config: 21 | 22 | http: 23 | api_password: !secret http_password 24 | # Set to 1 to enable development mode 25 | # development: 1 26 | #sessions_enabled: 0 27 | trusted_networks: 28 | # - 127.0.0.1 29 | - 192.168.80.0/24 30 | - 192.168.1.0/24 31 | ip_ban_enabled: True 32 | login_attempts_threshold: 5 33 | # ssl_certificate: !secret ssl_certificate 34 | # ssl_key: !secret ssl_key 35 | # base_url: !secret base_url 36 | 37 | recorder: 38 | purge_interval: 10 39 | purge_keep_days: 10 40 | db_url: !secret db_url 41 | 42 | logbook: 43 | 44 | logger: 45 | default: warning 46 | logs: 47 | homeassistant.components.switch.broadlink: info 48 | homeassistant.components.automation: info 49 | homeassistant.components.mqtt: debug 50 | homeassistant.components.light.hue: debug 51 | homeassistant.components.hue: debug 52 | custom_components.effect_control: debug 53 | homeassistant.components.switch.xiaomi_aqara: debug 54 | # homeassistant.components.device_tracker: debug 55 | # homeassistant.components.http: info 56 | # homeassistant.components.rfxtrx: debug 57 | # homeassistant.components.sensor.miflora: debug 58 | pyTibber: debug 59 | 60 | history: 61 | exclude: 62 | entities: 63 | - sensor.no_updates 64 | domains: 65 | - proximity 66 | 67 | sun: 68 | 69 | 70 | script: !include_dir_merge_named scripts/ 71 | #script: !include scripts.yaml 72 | 73 | automation: !include_dir_merge_list automation/ 74 | 75 | #scene: !include scenes.yaml 76 | 77 | #light: !include lights.yaml 78 | 79 | binary_sensor: !include binary_sensor.yaml 80 | 81 | sensor: !include sensors.yaml 82 | 83 | switch: !include switches.yaml 84 | 85 | group: !include group.yaml 86 | 87 | zone: !include zone.yaml 88 | 89 | input_select: !include input_select.yaml 90 | 91 | #input_boolean: !include input_boolean.yaml 92 | 93 | # update_ip: 94 | 95 | effect_control: 96 | 97 | 98 | proximity: 99 | home: 100 | devices: 101 | - device_tracker.daniel 102 | tolerance: 50 103 | unit_of_measurement: m 104 | 105 | notify: 106 | platform: pushbullet 107 | api_key: !secret pushbullet 108 | encryption_password: !secret pushbullet_encryption_password 109 | name: pushbullet 110 | 111 | device_tracker: 112 | - platform: nmap_tracker 113 | hosts: 114 | - 192.168.80.151 115 | - 192.168.80.161 116 | # track_new_devices: yes 117 | consider_home: 600 118 | interval_seconds: 60 119 | home_interval: 5 120 | scan_options: " --privileged -sP " 121 | # sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /usr/bin/nmap 122 | 123 | - platform: gpslogger 124 | # http://:8123/api/gpslogger?api_password=XXX&latitude=%LAT&longitude=%LON&device=daniel&accuracy=%ACC&battery=%BATT&speed=%SPD&direction=%DIR&altitude=%ALT&provider=%PROV&activity=%ACT 125 | 126 | hue: 127 | bridges: 128 | - host: 192.168.80.105 129 | 130 | cast: 131 | media_player: 132 | - host: 192.168.80.158 133 | - host: 192.168.80.13 134 | 135 | #- platform: vlc 136 | # arguments: 'front:CARD=SB,DEV=0' 137 | # name: Stue 138 | 139 | rfxtrx: 140 | device: /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0 141 | debug: False 142 | dummy: False 143 | 144 | climate: 145 | - platform: sensibo 146 | api_key: !secret sensibo_key 147 | 148 | tts: 149 | - platform: google 150 | language: 'no' 151 | 152 | resume_mp 1: 153 | media_player: stue 154 | resume_mp 3: 155 | media_player: soverom 156 | 157 | #news: 158 | # strava_token: !secret strava_token 159 | 160 | find: 161 | 162 | #xiaomi_aqara: 163 | # gateways: 164 | # - mac: 165 | # key: !secret xiaomi 166 | 167 | #emulated_hue: 168 | # type: google_home 169 | # listen_port: 80 170 | # expose_by_default: false 171 | # host_ip: 192.168.1.140 172 | 173 | zwave: 174 | usb_path: /dev/ttyACM0 175 | autoheal: False 176 | 177 | ifttt: 178 | key: !secret ifttt_key 179 | 180 | mqtt: 181 | # password: WryfINz4YZrR$rvCqkBhOzsZRh 182 | 183 | netatmo: 184 | api_key: !secret netatmo_api_key 185 | secret_key: !secret netatmo_secret_key 186 | username: !secret email 187 | password: !secret netatmo_password 188 | 189 | 190 | history_graph: 191 | gr1: 192 | name: Stromforbruk_48 193 | entities: 194 | - sensor.electricity_price_hamretunet_10 195 | - sensor.netatmo_indoor_temperature 196 | - sensor.set_temp 197 | - sensor.diff_cumulative_hourly_active_import_energy 198 | # - sensor.total_effect 199 | - sensor.filtered_total_effect 200 | hours_to_show: 48 201 | refresh: 60 202 | gr2: 203 | name: Stromforbruk_1 204 | entities: 205 | - sensor.total_effect 206 | - sensor.varmtvann_temperature 207 | - sensor.varmtvann_nede_temperature 208 | - sensor.varmtvann_ut_temperature 209 | - sensor.effekt_vaskemaskin 210 | - sensor.aeotec_zw096_smart_switch_6_power 211 | hours_to_show: 1 212 | refresh: 60 213 | gr3: 214 | name: Stromforbruk_24 215 | entities: 216 | - sensor.electricity_price_hamretunet_10 217 | - sensor.total_effect 218 | - sensor.varmtvann_temperature 219 | - sensor.varmtvann_nede_temperature 220 | - sensor.varmtvann_ut_temperature 221 | - sensor.effekt_vaskemaskin 222 | - sensor.aeotec_zw096_smart_switch_6_power 223 | hours_to_show: 24 224 | refresh: 60 225 | 226 | tibber_prices: 227 | access_token: !secret tibber 228 | 229 | camera: 230 | - platform: local_file 231 | name: "Prices" 232 | file_path: /tmp/prices.png 233 | -------------------------------------------------------------------------------- /custom_components/effect_control.py: -------------------------------------------------------------------------------- 1 | """light trigger component.""" 2 | import logging 3 | import datetime 4 | 5 | from homeassistant.util import dt as dt_util 6 | import homeassistant.loader as loader 7 | from homeassistant.components import device_tracker, light, notify 8 | from homeassistant.helpers.event import track_state_change 9 | from homeassistant.helpers.event import track_point_in_time 10 | from homeassistant.const import STATE_ON, STATE_OFF, EVENT_TIME_CHANGED 11 | from homeassistant.components.sun import STATE_ATTR_ELEVATION 12 | from homeassistant.components import light 13 | 14 | from collections import deque 15 | 16 | DOMAIN = "effect_control" 17 | MAX_CONS = 3000 18 | 19 | DEPENDENCIES = ['group'] 20 | 21 | 22 | def setup(hass, config): 23 | """ Sets up the simple alarms. """ 24 | logger = logging.getLogger(__name__) 25 | effect = deque(maxlen=5) 26 | effect_usage = [("vvb", 1000, 'switch.vvb'), ("varmepumpe", 1000, 'switch.sensibo')] 27 | 28 | def keep_effects(entity_id, old_state, new_state): 29 | print(new_state.state) 30 | effect.append(float(new_state.state)) 31 | 32 | def activate(entity_id, old_state, new_state): 33 | now = dt_util.now() 34 | if len(effect) < 2 or now.minute < 5: 35 | return 36 | 37 | print(effect) 38 | print(sum(effect)/ len(effect), new_state.state, now.minute) 39 | 40 | avg_effect = 0 if len(effect) < 1 else sum(effect) / len(effect) 41 | est_total_cons = float(new_state.state)*1000 + avg_effect * (60 - now.minute) * 60/3.6 42 | print("est total cons, ", est_total_cons) 43 | def _update_est_cons(): 44 | print('est_total_cons', est_total_cons) 45 | hass.states.set('sensor.est_cons', est_total_cons, {'unit_of_measurement': 'Wh'}) 46 | 47 | if now.minute < 5: 48 | _update_est_cons() 49 | return 50 | 51 | if est_total_cons < 0.9 * MAX_CONS: 52 | print(effect_usage) 53 | for name, dev_effect, entity_id in reversed(effect_usage): 54 | # if entity_id is off 55 | # est_total_cons += dev_effect * (60 - now.minute) / 60 56 | print("turn on ", name) 57 | if est_total_cons > 0.9 * MAX_CONS: 58 | # _update_est_cons() 59 | # print('est_total_cons', est_total_cons) 60 | # effect.clear() 61 | return 62 | # call tur on 63 | 64 | if est_total_cons < MAX_CONS: 65 | _update_est_cons() 66 | return 67 | 68 | for name, dev_effect, entity_id in effect_usage: 69 | # if entity_id is on 70 | # check if we need to turn off device imediately or if we can wait 71 | print("turn off ", name) 72 | est_total_cons -= dev_effect * (60 - now.minute) / 60 73 | if est_total_cons < MAX_CONS: 74 | print('est_total_cons', est_total_cons) 75 | # _update_est_cons() 76 | # effect.clear() 77 | return 78 | 79 | # 80 | 81 | track_state_change(hass, 'sensor.houtly_cons', activate) 82 | track_state_change(hass, 'sensor.total_effect', keep_effects) 83 | 84 | return True 85 | -------------------------------------------------------------------------------- /custom_components/find.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | """ 3 | """ 4 | import logging 5 | 6 | from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START 7 | from homeassistant.util import dt as dt_util 8 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 9 | from homeassistant.helpers.event import track_state_change, track_time_change 10 | from homeassistant.helpers.event import track_point_in_utc_time, async_track_point_in_utc_time 11 | from homeassistant.components.sensor.rest import RestData 12 | 13 | 14 | # The domain of your component. Should be equal to the name of your component. 15 | DOMAIN = "find" 16 | 17 | # List of component names (string) your component depends upon. 18 | # We depend on group because group will be loaded after all the components that 19 | # initialize devices have been setup. 20 | DEPENDENCIES = ['group', ] 21 | 22 | # Shortcut for the logger 23 | _LOGGER = logging.getLogger(__name__) 24 | 25 | 26 | def setup(hass, config): 27 | """Setup component.""" 28 | 29 | def daniel(service): 30 | 31 | print(hass.states.get('zone.home')) 32 | print(hass.states.get('zone.jobb').state) 33 | print(hass.states.get('device_tracker.daniel')) 34 | print(hass.states.get('sensor.phone_speed')) 35 | print(hass.states.get('sensor.phone_to_home').state) 36 | print(hass.states.get('sensor.phone_to_home_car').state) 37 | print(hass.states.get('binary_sensor.daniel_position').state) 38 | # try: 39 | if hass.states.get('device_tracker.daniel').state == 'home' or float(hass.states.get('sensor.phone_to_home').state) < 2: 40 | msg = "Daniel er hjemme." 41 | elif hass.states.get('device_tracker.daniel').state.lower() == 'jobb': 42 | msg = "Daniel er på jobb." 43 | elif float(hass.states.get('sensor.phone_to_home').state) < 60: 44 | if hass.states.get('binary_sensor.daniel_position').state == 'off': 45 | msg = "Daniel er på vei hjem og kommer til å bruke {} minutter dersom han sykler".format(hass.states.get('sensor.phone_to_home').state) 46 | else: 47 | msg = "Daniel er {} kilometer unna".format(str(round(float(hass.states.get('sensor.phone_to_home_car').distance) / 1000), 2)) 48 | else: 49 | msg = "Daniel er et stykke unna, og kommer til å bruke {} minutter hit med bil.".format(hass.states.get('sensor.phone_to_home').state) 50 | # except: 51 | # msg = "Jeg vet ikke hvor Daniel er. Beklager" 52 | 53 | msg = str(msg)#.encode('utf-8') 54 | data = {} 55 | data[ATTR_ENTITY_ID] = "media_player.stue" 56 | data['message'] = msg 57 | data['cache'] = False 58 | 59 | #_LOGGER.error(msg) 60 | print(msg.encode("utf-8")) 61 | hass.services.call('tts', 'google_say', data) 62 | 63 | 64 | hass.services.register(DOMAIN, "daniel", daniel) 65 | return True 66 | -------------------------------------------------------------------------------- /custom_components/news.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | import asyncio 4 | import async_timeout 5 | import aiohttp 6 | 7 | import logging 8 | import time 9 | import feedparser 10 | from html.parser import HTMLParser 11 | from bs4 import BeautifulSoup 12 | from xml.parsers.expat import ExpatError 13 | import requests 14 | import xmltodict 15 | from datetime import timedelta 16 | 17 | from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START 18 | from homeassistant.util import dt as dt_util 19 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 20 | from homeassistant.helpers.event import track_state_change, track_time_change 21 | from homeassistant.helpers.event import track_point_in_utc_time, async_track_point_in_utc_time 22 | from homeassistant.components.sensor.rest import RestData 23 | 24 | 25 | # The domain of your component. Should be equal to the name of your component. 26 | DOMAIN = "news" 27 | 28 | # List of component names (string) your component depends upon. 29 | # We depend on group because group will be loaded after all the components that 30 | # initialize devices have been setup. 31 | DEPENDENCIES = ['group', ] 32 | 33 | # Shortcut for the logger 34 | _LOGGER = logging.getLogger(__name__) 35 | 36 | class MLStripper(HTMLParser): 37 | def __init__(self): 38 | self.reset() 39 | self.strict = False 40 | self.convert_charrefs= True 41 | self.fed = [] 42 | def handle_data(self, d): 43 | self.fed = d 44 | def get_data(self): 45 | return ''.join(self.fed) 46 | 47 | def strip_tags(html): 48 | s = MLStripper() 49 | s.feed(html) 50 | return s.get_data() 51 | 52 | def num2str(num): 53 | num = float(num) 54 | if (num - int(num)) == 0: 55 | num = int(num) 56 | return str(round(num, 1)).replace("."," komma ") 57 | 58 | 59 | def setup(hass, config): 60 | """Setup component.""" 61 | yr_precipitation = {} 62 | nowcast_precipitation = None 63 | news_rss = [] 64 | workout_text = None 65 | strava_token = config[DOMAIN].get("strava_token") 66 | 67 | def _get_text(message_type=None): 68 | news = "" 69 | now = dt_util.now() 70 | if message_type == 2: 71 | news = news + "Sov godt " 72 | elif now.hour < 4: 73 | news = news + "God natt " 74 | elif now.hour < 10: 75 | news = news + "God morgen " 76 | elif now.hour < 18: 77 | news = news + "Hei " 78 | else: 79 | news = news + "God kveld " 80 | 81 | persons= [] 82 | state = hass.states.get('group.tracker') 83 | if state: 84 | tracker = state.attributes.get('entity_id') 85 | for person in tracker: 86 | person = hass.states.get(person) 87 | if not person: 88 | continue 89 | if not person.state == 'home': 90 | continue 91 | persons.append(person.attributes.get('friendly_name')) 92 | if len(persons) > 1: 93 | persons.insert(-1,"og") 94 | news = news + ' '.join(persons) + '. ' 95 | 96 | if message_type == 0: 97 | news = news + "Velkommen hjem. " 98 | 99 | nonlocal workout_text 100 | if workout_text and 'daniel' in news.lower(): 101 | news = news + workout_text 102 | workout_text = None 103 | 104 | if yr_precipitation: 105 | res = 0 106 | for time, value in yr_precipitation.items(): 107 | # print(time, value) 108 | if time < now and time > now - timedelta(hours=3): 109 | res += value 110 | precipitation = num2str(res) 111 | news = news + "De siste 3 timene har det kommet " + precipitation + " mm nedbør " 112 | 113 | temp = hass.states.get('sensor.ute_veranda_temperature') 114 | if temp and temp.state != "unknown": 115 | news = news + "og temperaturen er nå " + num2str(temp.state) + " grader ute. " 116 | if nowcast_precipitation and nowcast_precipitation > 0: 117 | news = news + "Den neste timen er det ventet " + num2str(nowcast_precipitation) + " mm nedbør. " 118 | 119 | if message_type == 2: 120 | alarm_state = hass.states.get('automation.wake_me_up') 121 | if alarm_state and alarm_state.state == "on": 122 | time_to_alarm = float(hass.states.get('sensor.relative_alarm_time').state) 123 | alarm_time_hour = int(time_to_alarm / 60) 124 | alarm_time_min = int(time_to_alarm - alarm_time_hour*60) 125 | news = news + "Vekkerklokken ringer om " + str(alarm_time_hour) + " timer og " + str(alarm_time_min) + " minutter." 126 | 127 | if message_type in [0, 1, 2]: 128 | return news 129 | 130 | news = news + "Her kommer siste nytt: " 131 | if False: 132 | _news_rss = news_rss 133 | else: 134 | _news_rss = news_rss[:2] 135 | for case in _news_rss: 136 | news = news + case + " " 137 | return news 138 | 139 | @asyncio.coroutine 140 | def _read_news(service): 141 | message_type = int(service.data.get("message_type", -1)) 142 | news = _get_text(message_type) 143 | 144 | data = {} 145 | if service and service.data.get(ATTR_ENTITY_ID): 146 | data[ATTR_ENTITY_ID] = service.data.get(ATTR_ENTITY_ID) 147 | data['message'] = news 148 | data['cache'] = False 149 | 150 | autoradio = True if service.data.get("entity_id_radio") else False 151 | yield from hass.services.async_call('tts', 'google_say', data, blocking=autoradio) 152 | return 153 | if not autoradio: 154 | return 155 | # if vekking: 156 | 157 | data = {} 158 | data[ATTR_ENTITY_ID] = service.data.get("entity_id_radio") 159 | if service.data.get("radio_option"): 160 | data['option'] = service.data.get("radio_option") 161 | else: 162 | data['option'] = "P3" 163 | hass.services.call('input_select', 'select_option', data) 164 | 165 | def _rss_news(time=None): 166 | nonlocal news_rss 167 | resource = "https://www.nrk.no/nyheter/" 168 | method = 'GET' 169 | payload = auth = headers = None 170 | rest = RestData(method, resource, auth, headers, payload, verify_ssl=True) 171 | rest.update() 172 | news_rss = [] 173 | if rest.data is None: 174 | return 175 | raw_data = BeautifulSoup(rest.data, 'html.parser') 176 | prew_text = "" 177 | for raw_text in raw_data.select("p"): 178 | text = strip_tags(str(raw_text)) 179 | if text == prew_text: 180 | continue 181 | prew_text = text 182 | news_rss.append(text) 183 | if len(news_rss) > 2: 184 | break 185 | _feed = feedparser.parse("http://www.yr.no/sted/Norge/S%C3%B8r-Tr%C3%B8ndelag/Trondheim/Trondheim/varsel.xml") 186 | summary = _feed.feed.get("summary") 187 | if summary is None: 188 | return 189 | news_rss.append("Værvarsel " + strip_tags(summary).replace("","").replace("","")) 190 | 191 | @asyncio.coroutine 192 | def _yr_precipitation(*_): 193 | url = "http://api.met.no/weatherapi/nowcast/0.9/" 194 | urlparams = {'lat': str(hass.config.latitude), 195 | 'lon': str(hass.config.longitude)} 196 | 197 | #print(url, urlparams) 198 | now = dt_util.utcnow() 199 | 200 | try: 201 | websession = async_get_clientsession(hass) 202 | with async_timeout.timeout(10, loop=hass.loop): 203 | resp = yield from websession.get(url, params=urlparams) 204 | if resp.status != 200: 205 | async_track_point_in_utc_time(hass, _yr_precipitation, now + timedelta(minutes=2)) 206 | return 207 | text = yield from resp.text() 208 | 209 | except (asyncio.TimeoutError, aiohttp.ClientError) as err: 210 | async_track_point_in_utc_time(hass, _yr_precipitation, now + timedelta(minutes=2)) 211 | return 212 | 213 | nonlocal nowcast_precipitation 214 | nonlocal yr_precipitation 215 | nowcast_precipitation = 0 216 | try: 217 | data = xmltodict.parse(text)['weatherdata'] 218 | model = data['meta']['model'] 219 | if '@nextrun' not in model: 220 | model = model[0] 221 | nextrun = dt_util.parse_datetime(model['@nextrun']) 222 | #print(nextrun, model['@nextrun']) 223 | nextrun = nextrun if (nextrun > now) else now + timedelta(minutes=2) 224 | for time_entry in data['product']['time']: 225 | loc_data = time_entry['location'] 226 | time = dt_util.parse_datetime(time_entry['@to']) 227 | #print(loc_data['precipitation']['@value']) 228 | #print(dt_util.as_local(time)) 229 | 230 | value = float(loc_data['precipitation']['@value']) 231 | if time > now and time < now + timedelta(hours=1): 232 | nowcast_precipitation += value 233 | yr_precipitation[time] = value 234 | 235 | for time in yr_precipitation.copy().keys(): 236 | if time < now - timedelta(hours=3): 237 | del yr_precipitation[time] 238 | 239 | except (ExpatError, IndexError) as err: 240 | async_track_point_in_utc_time(hass, _yr_precipitation, now + timedelta(minutes=2)) 241 | return 242 | #print(data['product']['time'], nextrun) 243 | # print(nowcast_precipitation) 244 | #print(yr_precipitation) 245 | async_track_point_in_utc_time(hass, _yr_precipitation, nextrun + timedelta(seconds=2)) 246 | 247 | # @asyncio.coroutine 248 | def _workout_text(now=None): 249 | url = " https://www.strava.com/api/v3/athlete/activities/?include_all_efforts=true" 250 | headers = {'Authorization': 'Bearer ' + strava_token} 251 | res = requests.post(url,headers=headers).json() 252 | nonlocal workout_text 253 | last_workout = res[0] 254 | last_workout_time = dt_util.as_local(dt_util.parse_datetime(last_workout.get('start_date_local'))) 255 | if not now: 256 | now = dt_util.utcnow() 257 | now = dt_util.as_local(now) 258 | 259 | if (now - last_workout_time).total_seconds() > 3600*10: 260 | workout_text = None 261 | return 262 | 263 | 264 | timer = (str(int(last_workout.get('elapsed_time')/3600)) + " timer og ") if int(last_workout.get('elapsed_time')/3600) > 0 else '' 265 | workout_text = "Bra jobbet Daniel! I dag har du trent i " + timer + str(int((last_workout.get('elapsed_time') - int(last_workout.get('elapsed_time')/3600)*3600)/60)) + " minutter. " 266 | workout_text += "Distanse " + num2str(last_workout.get('distance')/1000) + " kilometer. \n" 267 | 268 | if last_workout.get('has_heartrate'): 269 | workout_text += 'Høyeste puls var ' + num2str(last_workout.get('max_heartrate')) + '. \n' 270 | 271 | if last_workout.get('kudos_count'): 272 | workout_text += 'Du har fått ' + num2str(last_workout.get('kudos_count')) + ' kudos. \n' 273 | 274 | 275 | _rss_news() 276 | _workout_text() 277 | print(workout_text) 278 | return True 279 | hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _yr_precipitation) 280 | # track_time_change(hass, _yr_precipitation, minute=[31], second=0) 281 | track_time_change(hass, _rss_news, minute=[10, 26, 41, 56], second=0) 282 | track_time_change(hass, _workout_text, minute=[11, 26, 41, 56], second=8) 283 | 284 | hass.services.register(DOMAIN, "read_news", _read_news) 285 | #print(_get_text()) 286 | workout_text = None 287 | 288 | return True 289 | -------------------------------------------------------------------------------- /custom_components/restore_states.py: -------------------------------------------------------------------------------- 1 | """ 2 | Event parser and human readable log generator. 3 | 4 | For more details about this component, please refer to the documentation at 5 | https://home-assistant.io/components/resotre_states/ 6 | """ 7 | import logging 8 | 9 | import voluptuous as vol 10 | 11 | import homeassistant.util.dt as dt_util 12 | from homeassistant.components import recorder 13 | from homeassistant.const import (EVENT_HOMEASSISTANT_START, ATTR_ENTITY_ID, 14 | STATE_OFF, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF) 15 | from homeassistant.components import (input_boolean, input_select, group, climate, 16 | input_slider, automation, switch, light) 17 | 18 | DOMAIN = "restore_states" 19 | DEPENDENCIES = ['recorder', 'frontend'] 20 | 21 | _LOGGER = logging.getLogger(__name__) 22 | 23 | CONFIG_SCHEMA = vol.Schema({ 24 | DOMAIN: {} 25 | }, extra=vol.ALLOW_EXTRA) 26 | 27 | EVENT_LOGBOOK_ENTRY = 'logbook_entry' 28 | 29 | GROUP_BY_MINUTES = 15 30 | 31 | 32 | def setup(hass, config): 33 | """Restore states from database.""" 34 | 35 | def _restore_states(service): 36 | """Restore states.""" 37 | run = recorder.run_information(dt_util.utcnow()) 38 | if run is None: 39 | return 40 | 41 | from sqlalchemy import and_, func 42 | 43 | states = recorder.get_model('States') 44 | most_recent_state_ids = recorder.query( 45 | func.max(states.state_id).label('max_state_id') 46 | ) 47 | 48 | most_recent_state_ids = most_recent_state_ids.group_by( 49 | states.entity_id).subquery() 50 | 51 | query = recorder.query('States').join(most_recent_state_ids, and_( 52 | states.state_id == most_recent_state_ids.c.max_state_id)) 53 | 54 | states = recorder.execute(query) 55 | 56 | data = {ATTR_ENTITY_ID: 'group.all_automations'} 57 | hass.services.call('homeassistant', SERVICE_TURN_OFF, data, True) 58 | 59 | last_services = [] 60 | for state in states: 61 | if state.domain == group.DOMAIN: 62 | continue 63 | if state.domain == input_slider.DOMAIN: 64 | data = {ATTR_ENTITY_ID: state.entity_id, 65 | input_slider.ATTR_VALUE: state.state} 66 | service = input_slider.SERVICE_SELECT_VALUE 67 | elif state.domain == input_select.DOMAIN: 68 | data = {ATTR_ENTITY_ID: state.entity_id, 69 | input_select.ATTR_OPTION: state.state} 70 | service = input_select.SERVICE_SELECT_OPTION 71 | elif state.domain == climate.DOMAIN: 72 | data = {ATTR_ENTITY_ID: state.entity_id, 73 | climate.ATTR_TEMPERATURE: state.attributes.get('temperature')} 74 | service = climate.SERVICE_SET_TEMPERATURE 75 | elif (state.domain in [input_boolean.DOMAIN, automation.DOMAIN]): 76 | #or state.attributes.get('assumed_state')): 77 | data = {ATTR_ENTITY_ID: state.entity_id} 78 | if state.state == STATE_OFF: 79 | service = SERVICE_TURN_OFF 80 | if state.state == STATE_ON: 81 | service = SERVICE_TURN_ON 82 | else: 83 | continue 84 | if state.domain == light.DOMAIN: 85 | continue 86 | if state.domain == automation.DOMAIN: 87 | last_services.append((state.domain, service, data)) 88 | continue 89 | elif (state.domain in [switch.DOMAIN]): 90 | continue 91 | else: 92 | continue 93 | if hass.states.get(state.entity_id) is None: 94 | continue 95 | hass.services.call(state.domain, service, data, True) 96 | 97 | for (domain, service, data) in last_services: 98 | hass.services.call(domain, service, data, True) 99 | 100 | hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _restore_states) 101 | return True 102 | -------------------------------------------------------------------------------- /custom_components/resume_mp.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | """ 4 | import logging 5 | import time 6 | 7 | from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, EVENT_HOMEASSISTANT_START 8 | from homeassistant.util import dt as dt_util 9 | from homeassistant.helpers import extract_domain_configs 10 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 11 | from homeassistant.helpers.event import track_state_change, track_time_change 12 | from homeassistant.components.sensor.rest import RestData 13 | from homeassistant.components.media_player import ( 14 | SERVICE_PLAY_MEDIA, MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_ID, 15 | ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP) 16 | from homeassistant.config import get_default_config_dir 17 | 18 | import homeassistant.components.input_select as input_select 19 | 20 | 21 | # The domain of your component. Should be equal to the name of your component. 22 | DOMAIN = "resume_mp" 23 | 24 | # Shortcut for the logger 25 | _LOGGER = logging.getLogger(__name__) 26 | 27 | def setup(hass, config): 28 | """Setup the .""" 29 | def _resume_mp(config): 30 | media_player = "media_player." + config.get("media_player","") 31 | resume_state = None 32 | resume = False 33 | 34 | def _state_change(entity_id=None, old_state=None, new_state=None): 35 | nonlocal resume_state, resume 36 | content_id = hass.states.get(media_player).attributes.get("media_content_id", []) 37 | if resume_state and new_state.state != 'playing' and resume: 38 | print(resume_state) 39 | data = { 40 | ATTR_MEDIA_CONTENT_ID: resume_state.attributes.get("media_content_id", ""), 41 | ATTR_MEDIA_CONTENT_TYPE: "audio/mp3", 42 | ATTR_ENTITY_ID: media_player, 43 | } 44 | resume_state = None 45 | resume = True 46 | hass.services.call(DOMAIN_MP, SERVICE_PLAY_MEDIA, data) 47 | elif (new_state.state == 'playing' and 48 | ("nrk.no" in content_id or "nrk-mms-live.online.no" in content_id)): 49 | resume_state = new_state 50 | elif (resume_state and new_state.state == 'playing' and 51 | "tts_proxy" in content_id): 52 | resume = True 53 | else: 54 | resume_state = None 55 | resume = False 56 | track_state_change(hass, media_player, _state_change) 57 | 58 | for config_key in extract_domain_configs(config, DOMAIN): 59 | _resume_mp(config[config_key]) 60 | return True 61 | -------------------------------------------------------------------------------- /custom_components/sensor/brain_fm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Support for brain.fm. 3 | 4 | For more details about this platform, please refer to the documentation at 5 | https://home-assistant.io/components/sensor.brainfm/ 6 | """ 7 | from datetime import timedelta 8 | from random import shuffle 9 | from urllib.request import urlopen 10 | import voluptuous as vol 11 | 12 | import homeassistant.helpers.config_validation as cv 13 | from homeassistant.components.sensor import PLATFORM_SCHEMA 14 | from homeassistant.helpers.entity import Entity 15 | from homeassistant.helpers.event import track_time_change 16 | from homeassistant.const import (ATTR_ATTRIBUTION, CONF_EMAIL, CONF_PASSWORD) 17 | from homeassistant.util import Throttle 18 | 19 | REQUIREMENTS = ['brainfm==0.2.1'] 20 | 21 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 22 | vol.Required(CONF_EMAIL): cv.string, 23 | vol.Required(CONF_PASSWORD): cv.string, 24 | }) 25 | 26 | MIN_TIME_BETWEEN_UPDATES = timedelta(hours=24) 27 | 28 | def setup_platform(hass, config, add_devices, discovery_info=None): 29 | """Setup the sensor.""" 30 | import brainfm 31 | email = config.get(CONF_EMAIL) 32 | password = config.get(CONF_PASSWORD) 33 | client = brainfm.Connection(email, password) 34 | 35 | dev = [] 36 | for sensor_type in ["sleep"]:#, "relax", "focus"]: 37 | dev.append(BrainfmSensor(sensor_type, client, hass)) 38 | add_devices(dev) 39 | 40 | class BrainfmSensor(Entity): 41 | """Representation of a brain.fm sensor.""" 42 | 43 | def __init__(self, sensor_type, client, hass): 44 | """Initialize the sensor.""" 45 | self._sensor_type = sensor_type 46 | self._client = client 47 | self._state = None 48 | self._channel_name = None 49 | self._web_adress = None 50 | 51 | def _update(now=None): 52 | stations = client.get_stations() 53 | shuffle(stations) 54 | for station in stations: 55 | print(station) 56 | if self._sensor_type in station["canonical_name"]: 57 | station_id = station["station_id"] 58 | print(client.get_station(station_id=station_id)) 59 | while client.get_station(station_id=station_id)['playable'] == 0: 60 | sub_stations = client.get_stations_by_id(parent_id=station_id) 61 | shuffle(sub_stations) 62 | station_id = sub_stations[0]['station_id'] 63 | for sub_station in sub_stations: 64 | print(station) 65 | if '8hours' in station["canonical_name"]: 66 | station_id = sub_station['station_id'] 67 | break 68 | try: 69 | token_data = client.get_token(station_id=station_id) 70 | self._web_adress = "https://stream.brain.fm/?tkn=" + token_data["session_token"] 71 | mp3file = urlopen(self._web_adress) 72 | with open('/home/dahoiv/.homeassistant/www/sleep.mp3','wb') as output: 73 | output.write(mp3file.read()) 74 | self._state = token_data["name"] 75 | print(token_data) 76 | break 77 | except: 78 | continue 79 | track_time_change(hass, _update, hour=9, minute=14, second=36) 80 | 81 | @property 82 | def name(self): 83 | """Return the name of the sensor.""" 84 | return 'brainfm {}'.format(self._sensor_type) 85 | 86 | @property 87 | def state(self): 88 | """Return the state of the device.""" 89 | return self._state 90 | 91 | @property 92 | def device_state_attributes(self): 93 | """Return the state attributes.""" 94 | return { 95 | "Web adress": self._web_adress, 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /custom_components/switch/gmusic.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | import asyncio 4 | import logging 5 | import time 6 | import random 7 | import pickle 8 | import os.path 9 | from datetime import timedelta 10 | 11 | from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, EVENT_HOMEASSISTANT_START 12 | from homeassistant.util import dt as dt_util 13 | from homeassistant.components.switch import ( 14 | ENTITY_ID_FORMAT, SwitchDevice, PLATFORM_SCHEMA) 15 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 16 | from homeassistant.helpers.event import track_state_change, track_time_change 17 | from homeassistant.components.sensor.rest import RestData 18 | from homeassistant.components.media_player import ( 19 | SERVICE_PLAY_MEDIA, MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_ID, 20 | ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP) 21 | from homeassistant.config import get_default_config_dir 22 | 23 | import homeassistant.components.input_select as input_select 24 | 25 | REQUIREMENTS = ['gmusicapi==10.1.2'] 26 | # The domain of your component. Should be equal to the name of your component. 27 | DOMAIN = "gmusic" 28 | 29 | DEPENDENCIES = ['group', ] 30 | 31 | # Shortcut for the logger 32 | _LOGGER = logging.getLogger(__name__) 33 | 34 | def setup_platform(hass, config, add_devices, discovery_info=None): 35 | """Setup the Gmusic switch.""" 36 | add_devices([GmusicComponent(hass, config)]) 37 | return True 38 | 39 | class GmusicComponent(SwitchDevice): 40 | 41 | def __init__(self, hass, config): 42 | 43 | from gmusicapi import Mobileclient 44 | # https://github.com/simon-weber/gmusicapi/issues/424 45 | class GMusic(Mobileclient): 46 | def login(self, username, password, device_id, authtoken=None): 47 | if authtoken: 48 | self.android_id = device_id 49 | self.session._authtoken = authtoken 50 | self.session.is_authenticated = True 51 | 52 | try: 53 | # Send a test request to ensure our authtoken is still valide and working 54 | self.get_registered_devices() 55 | return True 56 | except: 57 | # Faild with the test-request so we set "is_authenticated=False" 58 | # and go through the login-process again to get a new "authtoken" 59 | self.session.is_authenticated = False 60 | 61 | if device_id: 62 | if super(GMusic, self).login(username, password, device_id): 63 | return True 64 | 65 | # Prevent further execution in case we failed with the login-process 66 | raise SystemExit 67 | 68 | self.hass = hass 69 | authtoken_path = get_default_config_dir() + "gmusic_authtoken" 70 | if os.path.isfile(authtoken_path): 71 | with open(authtoken_path, 'rb') as handle: 72 | authtoken = pickle.load(handle) 73 | else: 74 | authtoken = None 75 | self._api = GMusic() 76 | logged_in = self._api.login(config.get('user'), config.get('password'), config.get('device_id'), authtoken) 77 | if not logged_in: 78 | _LOGGER.error("Failed to log in, check http://unofficial-google-music-api.readthedocs.io/en/latest/reference/mobileclient.html#gmusicapi.clients.Mobileclient.login") 79 | return False 80 | with open(authtoken_path, 'wb') as f: 81 | pickle.dump(self._api.session._authtoken, f) 82 | 83 | 84 | self._playlist = "input_select." + config.get("playlist","") 85 | self._media_player = "input_select." + config.get("media_player","") 86 | self._entity_ids = [] 87 | self._playing = False 88 | self._playlists = [] 89 | self._tracks = [] 90 | self._next_track_no = 0 91 | self._playlist_to_index = {} 92 | self._unsub_tracker = None 93 | self._name = "Google music" 94 | track_time_change(hass, self._update_playlist, hour=[15, 6], minute=46, second=46) 95 | hass.bus.listen_once(EVENT_HOMEASSISTANT_START, self._update_playlist) 96 | 97 | @property 98 | def icon(self): 99 | return 'mdi:music-note' 100 | 101 | @property 102 | def name(self): 103 | """Return the name of the switch.""" 104 | return self._name 105 | 106 | @property 107 | def is_on(self): 108 | """Return true if device is on.""" 109 | return self._playing 110 | 111 | @property 112 | def should_poll(self): 113 | """No polling needed.""" 114 | return False 115 | 116 | def turn_on(self, **kwargs): 117 | """Fire the on action.""" 118 | if self._playing: 119 | return 120 | self._play() 121 | 122 | def turn_off(self, **kwargs): 123 | """Fire the off action.""" 124 | self._turn_off_media_player() 125 | 126 | def _update_playlist(self, now=None): 127 | if self.hass.states.get(self._playlist) is None: 128 | _LOGGER.error("%s is not a valid input_select entity.", self._playlist) 129 | return 130 | self._playlists = self._api.get_all_user_playlist_contents() 131 | self._playlist_to_index = {} 132 | idx = -1 133 | for playlist in self._playlists: 134 | idx = idx + 1 135 | name = playlist.get('name','') 136 | if len(name) < 1: 137 | continue 138 | self._playlist_to_index[name] = idx 139 | data = {"options": list(self._playlist_to_index.keys()), "entity_id": self._playlist} 140 | self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SET_OPTIONS, data) 141 | 142 | def _turn_off_media_player(self): 143 | self._playing = False 144 | if self._unsub_tracker is not None: 145 | self._unsub_tracker() 146 | self._unsub_tracker = None 147 | data = {ATTR_ENTITY_ID: self._entity_ids} 148 | # self.hass.services.call(DOMAIN_MP, SERVICE_TURN_OFF, data, blocking=True) 149 | self.schedule_update_ha_state() 150 | 151 | def _update_entity_ids(self): 152 | media_player = self.hass.states.get(self._media_player) 153 | if media_player is None: 154 | _LOGGER.error("%s is not a valid input_select entity.", self._media_player) 155 | return False 156 | _entity_ids = "media_player." + media_player.state 157 | if self.hass.states.get(_entity_ids) is None: 158 | _LOGGER.error("%s is not a valid media player.", media_player.state) 159 | return False 160 | self._entity_ids = _entity_ids 161 | return True 162 | 163 | def _next_track(self, entity_id=None, old_state=None, new_state=None, retry=3): 164 | if not self._playing: 165 | return 166 | if self._next_track_no >= len(self._tracks): 167 | self._next_track_no = 0 168 | track = self._tracks[self._next_track_no] 169 | if track is None: 170 | self._turn_off_media_player() 171 | return 172 | print(track) 173 | try: 174 | url = self._api.get_stream_url(track['trackId']) 175 | except Exception as err: 176 | self._next_track_no = self._next_track_no + 1 177 | _LOGGER.error("Failed to get track (%s)", err) 178 | if retry < 1: 179 | self._turn_off_media_player() 180 | return 181 | return self._next_track(retry=retry-1) 182 | data = { 183 | ATTR_MEDIA_CONTENT_ID: url, 184 | ATTR_MEDIA_CONTENT_TYPE: "audio/mp3", 185 | } 186 | 187 | data[ATTR_ENTITY_ID] = self._entity_ids 188 | self.schedule_update_ha_state() 189 | self.hass.services.call(DOMAIN_MP, SERVICE_PLAY_MEDIA, data) 190 | self._next_track_no = self._next_track_no + 1 191 | 192 | def _play(self): 193 | if not self._update_entity_ids(): 194 | return 195 | _playlist = self.hass.states.get(self._playlist) 196 | if _playlist is None: 197 | _LOGGER.error("%s is not a valid input_select entity.", self._playlist) 198 | return 199 | option = _playlist.state 200 | idx = self._playlist_to_index.get(option) 201 | if idx is None: 202 | self._turn_off_media_player() 203 | return 204 | 205 | self._tracks = self._playlists[idx]['tracks'] 206 | random.shuffle(self._tracks) 207 | self._next_track_no = 0 208 | self._playing = True 209 | self._next_track() 210 | self._unsub_tracker = track_state_change(self.hass, self._entity_ids, self._next_track, from_state='playing', to_state='idle') 211 | -------------------------------------------------------------------------------- /custom_components/switch/switchmate.py: -------------------------------------------------------------------------------- 1 | """Support for Switchmate""" 2 | import logging 3 | from datetime import timedelta 4 | 5 | import homeassistant.helpers.config_validation as cv 6 | import voluptuous as vol 7 | from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA 8 | from homeassistant.const import CONF_FRIENDLY_NAME, CONF_MAC 9 | from homeassistant.exceptions import PlatformNotReady 10 | from homeassistant.util import Throttle 11 | 12 | _LOGGER = logging.getLogger(__name__) 13 | DEFAULT_NAME = 'Switchmate' 14 | MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) 15 | REQUIREMENTS = ['bluepy==1.1.4'] 16 | 17 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 18 | vol.Required(CONF_MAC): cv.string, 19 | vol.Optional(CONF_FRIENDLY_NAME, default=DEFAULT_NAME): cv.string, 20 | }) 21 | 22 | def setup_platform(hass, config, add_devices, discovery_info=None) -> None: 23 | """Perform the setup for Xiaomi devices.""" 24 | friendly_name = config.get(CONF_FRIENDLY_NAME) 25 | mac_addr = config.get(CONF_MAC) 26 | add_devices([Switchmate(mac_addr, friendly_name)], True) 27 | 28 | class Switchmate(SwitchDevice): 29 | """Representation of a Switchmate.""" 30 | 31 | def __init__(self, mac, friendly_name) -> None: 32 | """Initialize the Switchmate.""" 33 | from bluepy.btle import ADDR_TYPE_RANDOM, Peripheral, BTLEException 34 | self._state = False 35 | self._friendly_name = friendly_name 36 | self._handle = 0x2e 37 | self._mac = mac 38 | try: 39 | self._device = Peripheral(self._mac, ADDR_TYPE_RANDOM) 40 | except BTLEException as ex: 41 | _LOGGER.error("Failed to setup switchmate: " + ex.message) 42 | raise PlatformNotReady() 43 | 44 | @property 45 | def unique_id(self) -> str: 46 | """Return a unique, HASS-friendly identifier for this entity.""" 47 | return '{0}_{1}'.format(self._mac.replace(':', ''), self.entity_id) 48 | 49 | @property 50 | def name(self) -> str: 51 | """Return the name of the switch.""" 52 | return self._friendly_name 53 | 54 | @Throttle(MIN_TIME_BETWEEN_UPDATES) 55 | def update(self) -> None: 56 | """Synchronize state with switch.""" 57 | self._state = b'\x00' == self._device.readCharacteristic(self._handle) 58 | print("state", self._state) 59 | 60 | @property 61 | def is_on(self) -> bool: 62 | """Return true if it is on.""" 63 | return self._state 64 | 65 | def turn_on(self, **kwargs) -> None: 66 | """Turn the switch on.""" 67 | self._device.writeCharacteristic(self._handle, b'\x00', True) 68 | self._state = True 69 | self.schedule_update_ha_state() 70 | 71 | def turn_off(self, **kwargs) -> None: 72 | """Turn the switch off.""" 73 | self._device.writeCharacteristic(self._handle, b'\x01', True) 74 | self._state = False 75 | self.schedule_update_ha_state() 76 | -------------------------------------------------------------------------------- /custom_components/tibber_prices.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | import datetime 4 | import logging 5 | 6 | import matplotlib 7 | 8 | matplotlib.use("Agg") 9 | import matplotlib.dates as mdates 10 | import matplotlib.pyplot as plt 11 | from dateutil import tz 12 | 13 | from homeassistant.const import CONF_ACCESS_TOKEN, EVENT_HOMEASSISTANT_START 14 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 15 | from homeassistant.helpers.event import async_track_time_change 16 | from homeassistant.util import dt as dt_util 17 | 18 | # The domain of your component. Should be equal to the name of your component. 19 | DOMAIN = "tibber_prices" 20 | 21 | # List of component names (string) your component depends upon. 22 | # We depend on group because group will be loaded after all the components that 23 | # initialize devices have been setup. 24 | DEPENDENCIES = ['group', ] 25 | 26 | # Shortcut for the logger 27 | _LOGGER = logging.getLogger(__name__) 28 | 29 | 30 | def setup(hass, config): 31 | """Setup component.""" 32 | 33 | import tibber 34 | tibber_home = None 35 | 36 | prices = [] 37 | dates = [] 38 | 39 | async def load_data(now=None): 40 | nonlocal tibber_home, prices, dates 41 | 42 | def skip(): 43 | if not dates: 44 | return False 45 | for _date in dates: 46 | if (dt_util.now() - datetime.timedelta(days=1)).date == _date.date: 47 | return False 48 | for _date in dates: 49 | if (dt_util.now() + datetime.timedelta(days=1)).date == _date.date and dt_util.now().hour >= 12: 50 | return True 51 | return False 52 | 53 | if skip(): 54 | return 55 | 56 | if tibber_home is None: 57 | tibber_connection = tibber.Tibber(config[DOMAIN].get(CONF_ACCESS_TOKEN), 58 | websession=async_get_clientsession(hass)) 59 | await tibber_connection.update_info() 60 | for tibber_home in tibber_connection.get_homes(): 61 | await tibber_home.update_info() 62 | if 'hamretunet' in tibber_home.info['viewer']['home']['appNickname'].lower(): 63 | break 64 | 65 | await tibber_home.update_price_info() 66 | prices = [] 67 | dates = [] 68 | for key, price_total in tibber_home.price_total.items(): 69 | prices.append(price_total*100) 70 | dates.append(dt_util.as_local(dt_util.parse_datetime(key))) 71 | 72 | if len(dates) > 24 or dt_util.utcnow().hour < 12: 73 | await generate_fig_call() 74 | 75 | async def generate_fig_call(_=None): 76 | hass.add_job(generate_fig) 77 | 78 | def generate_fig(_=None): 79 | now = dt_util.now() 80 | 81 | hour = now.hour 82 | dt = datetime.timedelta(minutes=now.minute) 83 | 84 | plt.style.use('ggplot') 85 | xFmt = mdates.DateFormatter('%H', tz=tz.gettz('Europe/Berlin')) 86 | fig = plt.figure() 87 | ax = fig.add_subplot(111) 88 | ax.grid(which='major', axis='x', linestyle='-', color='gray', alpha=0.25) 89 | plt.tick_params(axis="both", which="both", bottom=False, top=False, 90 | labelbottom=True, left=False, right=False, labelleft=True) 91 | ax.plot([dates[hour]+dt, dates[hour]+dt], [min(prices)-3, max(prices)+3], 'r', alpha=0.35, linestyle='-') 92 | ax.plot(dates, prices, '#039be5') 93 | 94 | ax.fill_between(dates, 0, prices, facecolor='#039be5', alpha=0.25) 95 | plt.text(dates[hour]+dt, prices[hour], str(round(prices[hour], 1)), fontsize=14) 96 | min_length = 5 if len(dates) > 24 else 3 97 | last_hour = -1 * min_length 98 | for _hour in range(1, len(prices)-1): 99 | if abs(_hour - last_hour) < min_length or abs(_hour - hour) < min_length: 100 | continue 101 | if (prices[_hour - 1] > prices[_hour] < prices[_hour + 1]) \ 102 | or (prices[_hour - 1] < prices[_hour] > prices[_hour + 1]): 103 | last_hour = _hour 104 | plt.text(dates[_hour], prices[_hour], 105 | str(round(prices[_hour], 1)) + "\n{:02}".format(_hour%24), 106 | fontsize=14, va='bottom') 107 | 108 | ax.set_xlim((dates[0] - datetime.timedelta(minutes=3), dates[-1] + datetime.timedelta(minutes=3))) 109 | ax.set_ylim((min(prices)-0.5, max(prices)+0.5)) 110 | ax.set_facecolor('white') 111 | # import plotly.plotly as py 112 | ax.xaxis.set_major_formatter(xFmt) 113 | fig.autofmt_xdate() 114 | fig.savefig('/tmp/prices.png') # file name in your local system 115 | plt.close(fig) 116 | plt.close('all') 117 | 118 | async_track_time_change(hass, generate_fig_call, minute=[1, 11, 21, 31, 41, 51], second=15) 119 | async_track_time_change(hass, load_data, hour=[12, 13, 14, 0, 8], minute=range(0, 60, 5), second=8) 120 | hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, load_data) 121 | return True 122 | -------------------------------------------------------------------------------- /customize.yaml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /group.yaml: -------------------------------------------------------------------------------- 1 | div: 2 | name: Div 3 | view: yes 4 | entities: 5 | # - switch.mobillader 6 | - sensor.phone_battery 7 | - sensor.phone_to_home 8 | - sensor.phone_to_home_car 9 | - binary_sensor.dark 10 | - group.tracker 11 | - script.borte 12 | - group.stovsuger 13 | - input_select.radio 14 | - media_player.hele_huset 15 | 16 | strom: 17 | name: Strom 18 | view: yes 19 | entities: 20 | - history_graph.stromforbruk_1 21 | - history_graph.stromforbruk_24 22 | - history_graph.stromforbruk_48 23 | - sensor.electricity_price_hamretunet_10 24 | - sensor.cumulative_hourly_active_import_energy 25 | - sensor.diff_cumulative_hourly_active_import_energy 26 | - sensor.total_effect 27 | - sensor.filtered_total_effect 28 | - sensor.est_cons 29 | - sensor.houtly_cons 30 | - switch.vv 31 | - switch.vv_automation 32 | - camera.prices 33 | 34 | stue: 35 | name: Stue 36 | view: yes 37 | entities: 38 | - script.stovsuger 39 | - sun.sun 40 | - light.stue 41 | - sensor.yr_symbol 42 | - sensor.yr_condition 43 | - sensor.stue_temperature 44 | - sensor.ute_veranda_temperature 45 | - sensor.netatmo_indoor_co2 46 | - sensor.netatmo_veranda_temperature 47 | - sensor.netatmo_indoor_noise 48 | - sensor.netatmo_indoor_temperature 49 | - group.media_stue 50 | - media_player.stue_kjokken 51 | - group.stue_scene 52 | - climate.stue 53 | - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor 54 | - binary_sensor.neo_coolcam_battery_powered_pir_sensor_sensor 55 | 56 | Soverom: 57 | view: yes 58 | entities: 59 | - group.alarmclock 60 | - group.alarmclock_varmeteppe 61 | - light.soverom1 62 | - light.soverom2 63 | - switch.varmeteppe 64 | - group.media_soverom 65 | 66 | Kjokken: 67 | view: yes 68 | entities: 69 | - group.media_kjokken 70 | 71 | tracker: 72 | - device_tracker.daniel 73 | - device_tracker.tinehjensiphone 74 | 75 | alarmclock: 76 | name: Wake Me Up 77 | entities: 78 | - sensor.alarm_time 79 | - input_number.alarmhour 80 | - input_number.alarmminutes 81 | - automation.wake_me_up 82 | - script.stop_alarm 83 | 84 | 85 | alarmclock_varmeteppe: 86 | name: Varmeteppe 87 | entities: 88 | - sensor.alarm_time_varmeteppe 89 | - input_number.alarmhour_varmeteppe 90 | - input_number.alarmminutes_varmeteppe 91 | - automation.turn_on_varmeteppe 92 | - switch.varmeteppe 93 | 94 | 95 | stovsuger: 96 | name: Støvsuger 97 | entities: 98 | - switch.stovsuger 99 | - binary_sensor.stovsuger 100 | - binary_sensor.stovsuger_ml 101 | - script.stovsuger_checker 102 | 103 | media_soverom: 104 | name: Media 105 | entities: 106 | - media_player.soverom 107 | - input_select.radio_soverom 108 | 109 | media_kjokken: 110 | name: Media 111 | entities: 112 | - media_player.kjokken 113 | - input_select.radio_kjokken 114 | 115 | media_stue: 116 | name: Media 117 | control: hidden 118 | entities: 119 | - media_player.stue 120 | - media_player.tv 121 | - input_select.radio_stue 122 | - switch.tv 123 | - switch.lydplanke 124 | 125 | hw_devices: 126 | entities: 127 | - device_tracker.broadlink_remote 128 | - device_tracker.broadlink_sensor 129 | - device_tracker.chromecast 130 | - device_tracker.chromecast_soverom 131 | - device_tracker.chromecast_bad 132 | - device_tracker.chromecast_stue 133 | - device_tracker.lys_do 134 | - device_tracker.lys_veranda 135 | - device_tracker.lys_yttergang 136 | - device_tracker.philipshue 137 | - device_tracker.cam1 138 | - device_tracker.cam2 139 | - device_tracker.xiaomi 140 | - device_tracker.netatmopersonalweatherstationlan 141 | -------------------------------------------------------------------------------- /input_select.yaml: -------------------------------------------------------------------------------- 1 | radio_stue: 2 | name: Radio 3 | options: 4 | - P3 5 | - P1 6 | - P2 7 | - Alltid Nyheter 8 | - Av 9 | initial: Av 10 | icon: mdi:music-note 11 | 12 | radio_soverom: 13 | name: Radio 14 | options: 15 | - P3 16 | - P1 17 | - P2 18 | - Alltid Nyheter 19 | - Av 20 | initial: Av 21 | icon: mdi:music-note 22 | 23 | 24 | radio_kjokken: 25 | name: Radio 26 | options: 27 | - P3 28 | - P1 29 | - P2 30 | - Alltid Nyheter 31 | - Av 32 | initial: Av 33 | icon: mdi:music-note 34 | 35 | 36 | radio: 37 | name: Radio 38 | options: 39 | - P3 40 | - P1 41 | - P2 42 | - Alltid Nyheter 43 | - Av 44 | initial: Av 45 | icon: mdi:music-note 46 | 47 | -------------------------------------------------------------------------------- /lights.yaml: -------------------------------------------------------------------------------- 1 | #- platform: rfxtrx 2 | # automatic_add: False 3 | # signal_repetitions: 2 4 | # devices: 5 | # 0b11000e00e9e5660b010f70: 6 | # name: Lys_stue 7 | # 0b11001000e9e5660c010f70: 8 | # name: Lys_TV 9 | # 0b11000c00e6ce7603010f50: 10 | # name: lys_bokhylle 11 | # 0b11000100e6ce7609010f70: 12 | # name: lys_bilder 13 | # 0b11000466bc0cfe05010f70: 14 | # name: IKKE_I_BRUK 15 | # 0b11000689bc0cfe05010f70: 16 | # name: Vindu 17 | 18 | - platform: hue 19 | host: 192.168.1.170 20 | 21 | #- platform: flux_led 22 | # automatic_add: False 23 | # devices: 24 | # 192.168.1.136: 25 | # name: Gang 26 | # 192.168.1.164: 27 | # name: Yttergang 28 | # 192.168.1.110: 29 | # name: Veranda 30 | -------------------------------------------------------------------------------- /ml_script/stovsuger.py: -------------------------------------------------------------------------------- 1 | # cvlc rtsp://admin:admin@192.168.1.106/11 --video-filter=scene --scene-prefix=$(date +"%Y%m%d%H%M") --scene-format=jpg --scene-path=./ --scene-ratio 800 --sout-x264-lookahead=10 --sout-x264-tune=stillimage --vout=dummy --run-time 60 vlc://quit 2 | 3 | import pickle 4 | import pandas as pd 5 | from sklearn.neighbors import KNeighborsClassifier 6 | from sklearn.decomposition import PCA 7 | try: 8 | from PIL import Image 9 | except: 10 | import Image 11 | import os 12 | import glob 13 | import numpy as np 14 | 15 | 16 | STANDARD_SIZE = (300, 200) 17 | N_COMPONENTS = 2 18 | img_dir_0 = "/home/dahoiv/cam/stovsuger_0" 19 | img_dir_1 = "/home/dahoiv/cam/stovsuger_1" 20 | 21 | file_open = lambda x,y: glob.glob(os.path.join(x,y)) 22 | 23 | def matrix_image(image): 24 | "opens image and converts it to a m*n matrix" 25 | image = Image.open(image) 26 | w, h = image.size 27 | crop = 300 28 | image.crop((0, crop, w, h-crop)) 29 | image = image.resize(STANDARD_SIZE) 30 | 31 | image = list(image.getdata()) 32 | image = np.array(image) 33 | return image 34 | 35 | def flatten_image(image): 36 | """ 37 | takes in a n*m numpy array and flattens it to 38 | an array of the size (1,m*n) 39 | """ 40 | s = image.shape[0] * image.shape[1] 41 | image_wide = image.reshape(1,s) 42 | return image_wide[0] 43 | 44 | if __name__ == "__main__": 45 | 46 | images = file_open(img_dir_0,"*.jpg") 47 | labels = ["0" for f in images] 48 | images2 = file_open(img_dir_1,"*.jpg") 49 | print(len(images), len(images2)) 50 | images.extend(images2) 51 | labels.extend(["1" for f in images2]) 52 | 53 | data = np.array([flatten_image(matrix_image(image)) for image in images]) 54 | y = np.where(np.array(labels)=="1", 1, 0) 55 | 56 | is_train = np.random.uniform(0, 1, len(data)) <= 0.7 57 | train_x, train_y = data[is_train], y[is_train] 58 | test_x, test_y = data[is_train==False], y[is_train==False] 59 | 60 | if False: 61 | import matplotlib.pyplot as plt 62 | pca = PCA(n_components = 2, svd_solver='randomized', whiten=True).fit(data) 63 | X = pca.fit_transform(data) 64 | df = pd.DataFrame({"x": X[:, 0], "y": X[:, 1], "label":np.where(y==1, "Stovsuger", "Ikke Stovsuger")}) 65 | colors = ["red", "yellow"] 66 | for label, color in zip(df['label'].unique(), colors): 67 | mask = df['label']==label 68 | plt.scatter(df[mask]['x'], df[mask]['y'], c=color, label=label) 69 | plt.legend() 70 | plt.show() 71 | 72 | pca = PCA(n_components = N_COMPONENTS, svd_solver='randomized', whiten=True) 73 | train_x = pca.fit_transform(train_x) 74 | knn = KNeighborsClassifier() 75 | knn.fit(train_x, train_y) 76 | 77 | with open('pca.pkl', 'wb') as f: 78 | pickle.dump(pca, f) 79 | with open('knn.pkl', 'wb') as f: 80 | pickle.dump(knn, f) 81 | 82 | test_x = pca.transform(test_x) 83 | print(pd.crosstab(test_y, knn.predict(test_x), rownames=["Actual"], colnames =["Predicted"])) 84 | -------------------------------------------------------------------------------- /packages/alarm_clock.yaml: -------------------------------------------------------------------------------- 1 | automation: 2 | - alias: 'Wake Me Up' 3 | initial_state: False 4 | trigger: 5 | - platform: time 6 | minutes: '/5' 7 | seconds: 2 8 | condition: 9 | - condition: template 10 | value_template: '{{ now().hour == (states.input_number.alarmhour.state | round(0)) and now().minute == (states.input_number.alarmminutes.state | round(0) ) }}' 11 | action: 12 | - service: script.turn_on 13 | entity_id: script.vekke_lys 14 | - service: script.turn_on 15 | entity_id: script.vekke_lyd 16 | - service: homeassistant.turn_off 17 | entity_id: automation.wake_me_up 18 | 19 | - alias: "alarm enabled" 20 | trigger: 21 | platform: state 22 | entity_id: input_number.alarmhour, input_number.alarmminutes 23 | action: 24 | - service: homeassistant.turn_on 25 | data: 26 | entity_id: 27 | - automation.wake_me_up 28 | 29 | input_number: 30 | alarmhour: 31 | name: Hour 32 | icon: mdi:timer 33 | initial: 7 34 | min: 4 35 | max: 11 36 | step: 1 37 | alarmminutes: 38 | name: Minutes 39 | icon: mdi:timer 40 | initial: 30 41 | min: 0 42 | max: 55 43 | step: 5 44 | 45 | sensor: 46 | - platform: template 47 | sensors: 48 | alarm_time: 49 | friendly_name: 'Alarm' 50 | value_template: '{% if states.input_number.alarmhour.state|round(0)|string|length == 1 %}0{% endif %}{{ states.input_number.alarmhour.state|round(0)|string }}:{% if states.input_number.alarmminutes.state|round(0)|string|length == 1 %}0{% endif %}{{ states.input_number.alarmminutes.state|round(0)|string }}' 51 | entity_id: 52 | - input_number.alarmminutes 53 | - input_number.alarmhour 54 | 55 | script: 56 | vekke_lyd: 57 | sequence: 58 | - delay: 00:00:03 59 | - service: media_player.volume_set 60 | data: 61 | entity_id: media_player.soverom 62 | volume_level: 0.4 63 | - service: media_player.play_media 64 | data: 65 | entity_id: media_player.soverom 66 | media_content_type: audio/mp3 67 | media_content_id: "https://server1.dahoiv.net/local/Wake_Up_fade_in.mp3" 68 | - delay: 00:08:00 69 | - service: media_player.volume_set 70 | data: 71 | entity_id: media_player.soverom 72 | volume_level: 0.5 73 | - service: input_select.select_option 74 | entity_id: input_select.radio_soverom 75 | data: 76 | option: "P3" 77 | - delay: 00:02:00 78 | - service: media_player.volume_set 79 | data: 80 | entity_id: media_player.soverom 81 | volume_level: 0.7 82 | - delay: 00:08:00 83 | - service: media_player.volume_set 84 | data: 85 | entity_id: media_player.soverom 86 | volume_level: 0.2 87 | - service: media_player.turn_off 88 | entity_id: media_player.soverom 89 | 90 | 91 | vekke_lys: 92 | sequence: 93 | - service: homeassistant.turn_on 94 | entity_id: 95 | - light.soverom1 96 | data: 97 | brightness: 1 98 | xy_color: [0.675,0.322] 99 | - delay: 00:00:03 100 | 101 | - service: homeassistant.turn_on 102 | entity_id: 103 | - light.soverom1 104 | data: 105 | transition: 180 106 | brightness: 10 107 | xy_color: [0.675,0.322] 108 | - delay: 109 | seconds: 10 110 | - service: homeassistant.turn_on 111 | entity_id: 112 | - light.soverom2 113 | data: 114 | transition: 170 115 | brightness: 10 116 | xy_color: [0.675,0.322] 117 | - delay: 118 | minutes: 3 119 | 120 | - service: homeassistant.turn_on 121 | entity_id: 122 | - light.soverom1 123 | data: 124 | transition: 780 125 | brightness: 120 126 | xy_color: [0.5704, 0.3978] 127 | - service: homeassistant.turn_on 128 | entity_id: 129 | - light.soverom2 130 | data: 131 | transition: 780 132 | brightness: 120 133 | xy_color: [0.5704, 0.3978] 134 | - delay: 135 | minutes: 12 136 | 137 | - service: homeassistant.turn_on 138 | entity_id: 139 | - light.soverom1 140 | data: 141 | transition: 100 142 | brightness: 255 143 | xy_color: [0.3138,0.3238] 144 | - service: homeassistant.turn_on 145 | entity_id: 146 | - light.soverom2 147 | data: 148 | transition: 100 149 | brightness: 255 150 | xy_color: [0.3138,0.3238] 151 | - delay: 152 | minutes: 2 153 | 154 | - service: homeassistant.turn_on 155 | entity_id: 156 | - light.soverom1 157 | data: 158 | flash: long 159 | - service: homeassistant.turn_on 160 | entity_id: 161 | - light.soverom2 162 | data: 163 | flash: long 164 | - delay: 165 | seconds: 1 166 | 167 | - service: homeassistant.turn_on 168 | entity_id: 169 | - light.soverom1 170 | data: 171 | effect: colorloop 172 | - service: homeassistant.turn_on 173 | entity_id: 174 | - light.soverom2 175 | data: 176 | effect: colorloop 177 | - delay: 178 | minutes: 10 179 | 180 | - service: homeassistant.turn_off 181 | entity_id: 182 | - light.soverom1 183 | - service: homeassistant.turn_off 184 | entity_id: 185 | - light.soverom2 186 | - delay: 187 | seconds: 1 188 | 189 | - service: homeassistant.turn_on 190 | entity_id: 191 | - light.soverom1 192 | data: 193 | flash: long 194 | - service: homeassistant.turn_on 195 | entity_id: 196 | - light.soverom2 197 | data: 198 | flash: long 199 | - delay: 200 | seconds: 10 201 | 202 | - service: homeassistant.turn_on 203 | entity_id: 204 | - light.soverom1 205 | data: 206 | flash: long 207 | - service: homeassistant.turn_on 208 | entity_id: 209 | - light.soverom2 210 | data: 211 | flash: long 212 | - delay: 213 | seconds: 10 214 | 215 | - service: homeassistant.turn_off 216 | entity_id: 217 | - light.soverom1 218 | - service: homeassistant.turn_off 219 | entity_id: 220 | - light.soverom2 221 | 222 | stop_alarm: 223 | sequence: 224 | 225 | - service: homeassistant.turn_off 226 | entity_id: script.vekke_lyd 227 | 228 | - service: homeassistant.turn_off 229 | entity_id: script.vekke_lys 230 | 231 | - service: homeassistant.turn_off 232 | entity_id: automation.wake_me_up 233 | 234 | - service: homeassistant.turn_off 235 | entity_id: 236 | - light.soverom1 237 | - service: homeassistant.turn_off 238 | entity_id: 239 | - light.soverom2 240 | - service: homeassistant.turn_off 241 | entity_id: media_player.soverom 242 | -------------------------------------------------------------------------------- /packages/heating.yaml: -------------------------------------------------------------------------------- 1 | 2 | heating: 3 | access_token: !secret tibber 4 | 5 | input_datetime: 6 | start_time_1: 7 | name: Start heating 1 8 | has_date: false 9 | has_time: true 10 | stop_time_1: 11 | name: Stop heating 1 12 | has_date: false 13 | has_time: true 14 | start_time_2: 15 | name: Start heating 2 16 | has_date: false 17 | has_time: true 18 | stop_time_2: 19 | name: Stop heating 2 20 | has_date: false 21 | has_time: true 22 | 23 | 24 | input_number: 25 | comfort_temp_1: 26 | name: Comport temp1 27 | min: 16 28 | max: 23 29 | step: 1 30 | comfort_temp_2: 31 | name: Comfort temp2 32 | min: 16 33 | max: 23 34 | step: 1 35 | set_temp: 36 | name: Set temp 37 | min: 0 38 | max: 30 39 | step: 0.5 40 | input_boolean: 41 | away: 42 | name: Set away mode 43 | initial: off 44 | icon: mdi:home 45 | 46 | 47 | script: 48 | temporarly_disable_heating: 49 | sequence: 50 | - service: heating.temporarly_disable 51 | -------------------------------------------------------------------------------- /packages/varmeteppe.yaml: -------------------------------------------------------------------------------- 1 | automation: 2 | - alias: 'Turn on varmeteppe' 3 | initial_state: False 4 | trigger: 5 | - platform: time 6 | minutes: '/5' 7 | seconds: 2 8 | condition: 9 | condition: and 10 | conditions: 11 | - condition: template 12 | value_template: '{{ now().hour == (states.input_number.alarmhour_varmeteppe.state | round(0)) and now().minute == (states.input_number.alarmminutes_varmeteppe.state | round(0) ) }}' 13 | - condition: template 14 | value_template: '{{ states("device_tracker.tine") == "home" or states("device_tracker.daniel") == "home" or (now()- states.device_tracker.tine.last_changed).seconds < 3600 or (now()- states.device_tracker.daniel.last_changed).seconds < 3600 }}' 15 | action: 16 | # - service: homeassistant.turn_off 17 | # entity_id: automation.turn_on_varmeteppe 18 | - service: homeassistant.turn_on 19 | entity_id: switch.varmeteppe 20 | - delay: '01:30' 21 | - service: homeassistant.turn_off 22 | entity_id: switch.varmeteppe 23 | - delay: '00:01' 24 | - service: homeassistant.turn_off 25 | entity_id: switch.varmeteppe 26 | - delay: '00:02' 27 | - service: homeassistant.turn_off 28 | entity_id: switch.varmeteppe 29 | 30 | - alias: "varmeteppe enabled" 31 | trigger: 32 | platform: state 33 | entity_id: input_number.alarmhour_varmeteppe, input_number.alarmminutes_varmeteppe 34 | action: 35 | - service: homeassistant.turn_on 36 | data: 37 | entity_id: 38 | - automation.turn_on_varmeteppe 39 | 40 | 41 | input_number: 42 | alarmhour_varmeteppe: 43 | name: Hour 44 | icon: mdi:timer 45 | min: 20 46 | max: 23 47 | step: 1 48 | alarmminutes_varmeteppe: 49 | name: Minutes 50 | icon: mdi:timer 51 | min: 0 52 | max: 55 53 | step: 5 54 | 55 | sensor: 56 | - platform: template 57 | sensors: 58 | alarm_time_varmeteppe: 59 | friendly_name: 'Varmeteppe' 60 | value_template: '{% if states.input_number.alarmhour_varmeteppe.state|round(0)|string|length == 1 %}0{% endif %}{{ states.input_number.alarmhour_varmeteppe.state|round(0)|string }}:{% if states.input_number.alarmminutes_varmeteppe.state|round(0)|string|length == 1 %}0{% endif %}{{ states.input_number.alarmminutes_varmeteppe.state|round(0)|string }}' 61 | entity_id: 62 | - input_number.alarmminutes_varmeteppe 63 | - input_number.alarmhour_varmeteppe 64 | -------------------------------------------------------------------------------- /scripts/soverom.yaml: -------------------------------------------------------------------------------- 1 | brightness_soverom: 2 | alias: "Brightness soverom" 3 | sequence: 4 | - service: homeassistant.turn_on 5 | entity_id: light.soverom 6 | data_template: 7 | transition: 1 8 | brightness: '{% if states.light.soverom.attributes.brightness%}{{(states.light.soverom.attributes.brightness+25)|min(255)}}{% else %}25{% endif %}' 9 | - service: script.turn_on 10 | entity_id: script.brightness_soverom_temp 11 | 12 | brightness_soverom_temp: 13 | sequence: 14 | - delay: '00:00:01' 15 | - service: script.turn_on 16 | entity_id: script.brightness_soverom 17 | -------------------------------------------------------------------------------- /sensors.yaml: -------------------------------------------------------------------------------- 1 | - platform: rfxtrx 2 | automatic_add: False 3 | devices: 4 | 0a5208030401001f1d0259: 5 | name: Ute_veranda 6 | 0a52080705020095220269: 7 | name: Stue 8 | 0a5208450a0101e8160279: 9 | name: varmtvann 10 | 0a5208020201019e230249: 11 | name: varmtvann_ut 12 | 0a52080b070100eb230259: 13 | name: varmtvann_nede 14 | 15 | - platform: yr 16 | monitored_conditions: 17 | - symbol 18 | - precipitation 19 | - temperature 20 | 21 | #- platform: xiaomi_scale 22 | # mac: 'c8:0f:10:a1:4d:18' 23 | # name: Vekt 24 | #- platform: scale 25 | 26 | - platform: template 27 | sensors: 28 | relative_alarm_time: 29 | value_template: > 30 | {% set relative_time = (states.input_number.alarmhour.state|float|multiply(60) + states.input_number.alarmminutes.state|float) - (now().hour|float|multiply(60) + now().minute) %} 31 | {%- if relative_time < 0 -%} 32 | {{24*60+relative_time}} 33 | {%- else -%} 34 | {{relative_time}} 35 | {%- endif %} 36 | unit_of_measurement: "min" 37 | entity_id: 38 | - sensor.time 39 | phone_battery: 40 | friendly_name: 'Telefon batteri' 41 | value_template: '{{ states.device_tracker.daniel.attributes.battery|float }}' 42 | unit_of_measurement: "%" 43 | entity_id: 44 | - device_tracker.daniel 45 | phone_speed: 46 | friendly_name: 'Telefon speed' 47 | value_template: '{{ states.device_tracker.daniel.attributes.speed|float }}' 48 | unit_of_measurement: " " 49 | entity_id: 50 | - device_tracker.daniel 51 | phone_direction: 52 | friendly_name: 'Telefon direction' 53 | value_template: '{{ states.device_tracker.daniel.attributes.direction }}' 54 | unit_of_measurement: "" 55 | entity_id: 56 | - device_tracker.daniel 57 | phone_activity: 58 | friendly_name: 'Telefon activity' 59 | value_template: '{{ states.device_tracker.daniel.attributes.activity }}' 60 | unit_of_measurement: "" 61 | entity_id: 62 | - device_tracker.daniel 63 | effekt_vaskemaskin: 64 | friendly_name: 'Effekt vaskemasin' 65 | value_template: '{{ states.sensor.fibaro_system_fgwpef_wall_plug_gen5_power.state|float }}' 66 | unit_of_measurement: "W" 67 | entity_id: 68 | - sensor.fibaro_system_fgwpef_wall_plug_gen5_power 69 | set_temp: 70 | value_template: '{{states.climate.stue.attributes.temperature|float }}' 71 | unit_of_measurement: "°C" 72 | entity_id: 73 | - climate.stue 74 | max_tibber: 75 | friendly_name: "Max price" 76 | unit_of_measurement: 'kr' 77 | value_template: "{{states.sensor.electricity_price_hamretunet_10.attributes.max_price}}" 78 | 79 | - platform: time_date 80 | display_options: 81 | - 'time' 82 | 83 | - platform: brain_fm 84 | email: !secret email 85 | password: !secret brain_fm_password 86 | 87 | 88 | - platform: tibber 89 | access_token: !secret tibber 90 | 91 | #- platform: nordpool 92 | # currency: 'NOK' 93 | # region: 'Molde' 94 | # name: "Elspot kWh" 95 | 96 | - platform: google_travel_time 97 | name: Phone To Home 98 | api_key: !secret google_api_key 99 | origin: device_tracker.daniel 100 | destination: zone.home 101 | options: 102 | mode: bicycling 103 | 104 | - platform: google_travel_time 105 | name: Phone To home car 106 | api_key: !secret google_api_key 107 | origin: device_tracker.daniel 108 | destination: zone.home 109 | options: 110 | mode: driving 111 | 112 | - platform: mqtt 113 | name: "Total effect" 114 | state_topic: "6970631400499129/effect/watt" 115 | value_template: "{{ float(value)/1000.0 }}" 116 | unit_of_measurement: "kW" 117 | 118 | 119 | - platform: mqtt 120 | name: "Cumulative_hourly_reactive_import_energy" 121 | state_topic: "6970631400499129/Cumulative_hourly_reactive_import_energy/wh" 122 | value_template: "{{ value }}" 123 | unit_of_measurement: "Wh" 124 | 125 | 126 | - platform: mqtt 127 | name: "Cumulative_hourly_active_import_energy" 128 | state_topic: "6970631400499129/Cumulative_hourly_active_import_energy/wh" 129 | value_template: "{{ float(value) /1000.0 }}" 130 | unit_of_measurement: "kWh" 131 | 132 | - platform: mqtt 133 | name: "Diff_cumulative_hourly_active_import_energy" 134 | state_topic: "6970631400499129/Diff_cumulative_hourly_active_import_energy/wh" 135 | value_template: "{{ float(value) /1000.0 }}" 136 | unit_of_measurement: "kWh" 137 | 138 | - platform: mqtt 139 | name: "Houtly_cons" 140 | state_topic: "6970631400499129/hourly_cons/ws" 141 | value_template: "{{ float(value) /3600000.0 }}" 142 | unit_of_measurement: "kWh" 143 | 144 | 145 | - platform: filter 146 | name: "filtered total effect" 147 | entity_id: sensor.total_effect 148 | filters: 149 | - filter: time_simple_moving_average 150 | window_size: 00:30 151 | precision: 2 152 | -------------------------------------------------------------------------------- /switches.yaml: -------------------------------------------------------------------------------- 1 | - platform: rfxtrx 2 | # automatic_add: True 3 | signal_repetitions: 2 4 | devices: 5 | 0b1100cd0213c7f210010f70: 6 | name: Movement1 7 | fire_event: True 8 | 0b11000a02ef9ff210010f50: 9 | name: Movement2 10 | 0b1100000213b3f210010f70: 11 | name: Movement_do 12 | fire_event: True 13 | 09130000db153a012670: 14 | name: Movement_soverom 15 | fire_event: True 16 | 0913009c94597a013670: 17 | name: Movement_soverom_seng 18 | fire_event: True 19 | 0913002355997a013d60: 20 | name: Movement_kjokken 21 | fire_event: True 22 | 091300122ebf8a013670: 23 | name: Movement_stue 24 | fire_event: True 25 | 0b1100e003af16aa10000060: 26 | name: Door 27 | fire_event: True 28 | 0b110053026cfeaa10010f70: 29 | name: Door_bad 30 | fire_event: True 31 | 0b11000300932eaa10000060: 32 | name: door_bedroom 33 | fire_event: True 34 | 0b11001d00c9277801010f70: 35 | name: ovn_soverom 36 | 0b11001901f973ca01010f70: 37 | name: varmeteppe 38 | 0b110086035f46aa10000050: 39 | name: door_do 40 | fire_event: True 41 | 09130006545503020e60: 42 | name: leggetid 43 | fire_event: True 44 | 0b11005b0154b55e01040f60: 45 | name: knapp_soverom 46 | fire_event: True 47 | 0913000e9da2da015270: 48 | name: Movement3 49 | fire_event: True 50 | 0b11000d00e6ce760d010f60: 51 | name: soverom_switch1 52 | fire_event: True 53 | 09130004d55555019870: 54 | name: stovsuger_running 55 | fire_event: True 56 | 09130009d744fc01a870: 57 | name: lyd_soverom 58 | fire_event: True 59 | 0b11003d0097251e01040f50: 60 | name: main 61 | fire_event: True 62 | 0b110002012c299a0a000070: 63 | name: lux 64 | fire_event: True 65 | 09130051aebf8a013640: 66 | name: vann_bad 67 | fire_event: True 68 | 091300381cd55001f670: 69 | name: vann_kjokken 70 | fire_event: True 71 | 0913001587a768022e60: 72 | name: news_soverom 73 | fire_event: True 74 | 09130033154c02011d70: 75 | name: knapp 76 | fire_event: True 77 | 78 | - platform: broadlink 79 | host: 192.168.80.143 80 | mac: 'B4:43:0D:CC:0F:57' 81 | timeout: 15 82 | switches: 83 | tv: 84 | friendly_name: "Tv" 85 | command_on: 'JgAcAB0dHB44HhweGx4cHR06HB0cHhwdHB8bHhwADQUAAAAAAAAAAAAAAAA=' 86 | command_off: 'JgAaABweOR4bHhwdHB4dHRw6HhsdHR0dOTocAA0FAAAAAAAAAAAAAAAAAAA=' 87 | stovsuger: 88 | friendly_name: "Stovsuger" 89 | command_on: 'JgBIAAABJpMSOBITEhMSExITEhMSExITFw4SNxI4EjcSOBI4ETgSOBITEhMSNxITEhMSExITEhMSOBI3EhMSOBI4ETgSOBI3EgANBQ==' 90 | command_off: 'JgBQAAABJpMTNhITEhMSExITEhQRExITEhMSOBI4ETgSOBI3FDYSOBETFDYUNhMSEhMSExITExISNxQREhMSOBM2EjgSOBE4EgAFEAABJUoSAA0FAAAAAAAAAAA=' 91 | 92 | - platform: switchmate 93 | mac: 'cb:25:0b:c2:c9:8d' 94 | friendly_name: "VV" 95 | 96 | - platform: vv_automation 97 | access_token: !secret tibber 98 | --------------------------------------------------------------------------------