Added <Camera> dashboard widget

This commit is contained in:
Fabio Manganiello 2021-03-29 21:13:36 +02:00
parent 177c697f83
commit a147a4d37a
22 changed files with 136 additions and 23 deletions

View file

@ -7,6 +7,8 @@ Given the high speed of development in the first phase, changes are being report
### Added
- Added `<Camera>` dashboard widget.
- Added support for custom dashboard widgets with customized (see https://git.platypush.tech/platypush/platypush/-/wikis/Backends#creating-custom-widgets).
## [0.20.7] - 2021-03-26

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-31bc5041"],{6341:function(e,n,t){"use strict";t.r(n);t("b64b");var c=t("7a23"),r=Object(c["K"])("data-v-eac2ea44");Object(c["u"])("data-v-eac2ea44");var i={class:"switches-container"},s={class:"switch-plugins"},u={key:0,class:"no-content"},a={key:0,class:"refresh col-2"},o=Object(c["h"])("i",{class:"fa fa-sync"},null,-1),l={class:"refresh-button"},d=Object(c["h"])("i",{class:"fa fa-sync"},null,-1);Object(c["s"])();var b=r((function(e,n,t,r,b,h){var f=Object(c["z"])("Loading");return Object(c["r"])(),Object(c["e"])("div",i,[b.loading?(Object(c["r"])(),Object(c["e"])(f,{key:0})):Object(c["f"])("",!0),Object(c["h"])("div",s,[Object.keys(b.plugins).length?Object(c["f"])("",!0):(Object(c["r"])(),Object(c["e"])("div",u,"No switch plugins configured")),(Object(c["r"])(!0),Object(c["e"])(c["a"],null,Object(c["x"])(Object.keys(b.plugins),(function(e){return Object(c["r"])(),Object(c["e"])("div",{class:"switch-plugin",key:e,onClick:function(n){return b.selectedPlugin=b.selectedPlugin===e?null:e}},[Object(c["h"])("div",{class:["header",{selected:b.selectedPlugin===e}]},[Object(c["h"])("div",{class:"name col-10",textContent:Object(c["C"])(e)},null,8,["textContent"]),b.selectedPlugin===e?(Object(c["r"])(),Object(c["e"])("div",a,[Object(c["h"])("button",{onClick:Object(c["J"])((function(n){return b.bus.emit("refresh",e)}),["stop"]),title:"Refresh plugin",disabled:b.loading},[o],8,["onClick","disabled"])])):Object(c["f"])("",!0)],2),Object(c["h"])("div",{class:["body",{hidden:b.selectedPlugin!==e}]},[(Object(c["r"])(),Object(c["e"])(Object(c["A"])(b.components[e]),{config:b.plugins[e],"plugin-name":e,selected:b.selectedPlugin===e,bus:b.bus},null,8,["config","plugin-name","selected","bus"]))],2)],8,["onClick"])})),128))]),Object(c["h"])("div",l,[Object(c["h"])("button",{onClick:n[1]||(n[1]=function(){return h.refresh.apply(h,arguments)}),disabled:b.loading,title:"Refresh plugins"},[d],8,["disabled"])])])})),h=(t("4160"),t("a15b"),t("d81d"),t("fb6a"),t("d3b7"),t("ac1f"),t("1276"),t("159b"),t("96cf"),t("1da1")),f=t("3a5e"),p=t("3e54"),O=t("14b7"),j={name:"Switches",components:{Loading:f["a"]},mixins:[p["a"]],data:function(){return{loading:!1,plugins:{},components:{},selectedPlugin:null,bus:Object(O["a"])()}},methods:{initPanels:function(){var e=this;this.components={},Object.keys(this.plugins).forEach(function(){var n=Object(h["a"])(regeneratorRuntime.mark((function n(r){var i,s,u;return regeneratorRuntime.wrap((function(n){while(1)switch(n.prev=n.next){case 0:return i=r.split(".").map((function(e){return e[0].toUpperCase()+e.slice(1)})).join(""),s=null,n.prev=2,n.next=5,t("c1da")("./".concat(i,"/Index"));case 5:s=n.sent,n.next=11;break;case 8:return n.prev=8,n.t0=n["catch"](2),n.abrupt("return");case 11:u=Object(c["i"])(Object(h["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return e.abrupt("return",s);case 1:case"end":return e.stop()}}),e)})))),e.$options.components[r]=u,e.components[r]=u;case 14:case"end":return n.stop()}}),n,null,[[2,8]])})));return function(e){return n.apply(this,arguments)}}())},refresh:function(){var e=this;return Object(h["a"])(regeneratorRuntime.mark((function n(){return regeneratorRuntime.wrap((function(n){while(1)switch(n.prev=n.next){case 0:return e.loading=!0,n.prev=1,n.next=4,e.request("utils.get_switch_plugins");case 4:e.plugins=n.sent,e.initPanels();case 6:return n.prev=6,e.loading=!1,n.finish(6);case 9:case"end":return n.stop()}}),n,null,[[1,,6,9]])})))()}},mounted:function(){this.refresh()}};t("84aa");j.render=b,j.__scopeId="data-v-eac2ea44";n["default"]=j},"7ac9":function(e,n,t){},"84aa":function(e,n,t){"use strict";t("7ac9")},c1da:function(e,n,t){var c={"./LightHue/Index":["0219","chunk-06539e5d","chunk-5d632024","chunk-e017dc3e"],"./Smartthings/Index":["6e68","chunk-06539e5d","chunk-5d632024","chunk-972487d6"],"./SwitchSwitchbot/Index":["5083","chunk-06539e5d","chunk-5d632024","chunk-0021f7ee"],"./SwitchTplink/Index":["d11f","chunk-06539e5d","chunk-5d632024","chunk-c4aee99e"],"./SwitchWemo/Index":["bedd","chunk-06539e5d","chunk-5d632024","chunk-60dbbc82"],"./ZigbeeMqtt/Index":["65d6","chunk-06539e5d","chunk-5d632024","chunk-07773226"],"./Zwave/Index":["e170","chunk-06539e5d","chunk-5d632024","chunk-0827360a"]};function r(e){if(!t.o(c,e))return Promise.resolve().then((function(){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}));var n=c[e],r=n[0];return Promise.all(n.slice(1).map(t.e)).then((function(){return t(r)}))}r.keys=function(){return Object.keys(c)},r.id="c1da",e.exports=r}}]);
//# sourceMappingURL=chunk-31bc5041.821f5281.js.map
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-31bc5041"],{6341:function(e,n,t){"use strict";t.r(n);t("b64b");var c=t("7a23"),r=Object(c["K"])("data-v-eac2ea44");Object(c["u"])("data-v-eac2ea44");var i={class:"switches-container"},s={class:"switch-plugins"},u={key:0,class:"no-content"},a={key:0,class:"refresh col-2"},o=Object(c["h"])("i",{class:"fa fa-sync"},null,-1),l={class:"refresh-button"},d=Object(c["h"])("i",{class:"fa fa-sync"},null,-1);Object(c["s"])();var b=r((function(e,n,t,r,b,h){var f=Object(c["z"])("Loading");return Object(c["r"])(),Object(c["e"])("div",i,[b.loading?(Object(c["r"])(),Object(c["e"])(f,{key:0})):Object(c["f"])("",!0),Object(c["h"])("div",s,[Object.keys(b.plugins).length?Object(c["f"])("",!0):(Object(c["r"])(),Object(c["e"])("div",u,"No switch plugins configured")),(Object(c["r"])(!0),Object(c["e"])(c["a"],null,Object(c["x"])(Object.keys(b.plugins),(function(e){return Object(c["r"])(),Object(c["e"])("div",{class:"switch-plugin",key:e,onClick:function(n){return b.selectedPlugin=b.selectedPlugin===e?null:e}},[Object(c["h"])("div",{class:["header",{selected:b.selectedPlugin===e}]},[Object(c["h"])("div",{class:"name col-10",textContent:Object(c["C"])(e)},null,8,["textContent"]),b.selectedPlugin===e?(Object(c["r"])(),Object(c["e"])("div",a,[Object(c["h"])("button",{onClick:Object(c["J"])((function(n){return b.bus.emit("refresh",e)}),["stop"]),title:"Refresh plugin",disabled:b.loading},[o],8,["onClick","disabled"])])):Object(c["f"])("",!0)],2),Object(c["h"])("div",{class:["body",{hidden:b.selectedPlugin!==e}]},[(Object(c["r"])(),Object(c["e"])(Object(c["A"])(b.components[e]),{config:b.plugins[e],"plugin-name":e,selected:b.selectedPlugin===e,bus:b.bus},null,8,["config","plugin-name","selected","bus"]))],2)],8,["onClick"])})),128))]),Object(c["h"])("div",l,[Object(c["h"])("button",{onClick:n[1]||(n[1]=function(){return h.refresh.apply(h,arguments)}),disabled:b.loading,title:"Refresh plugins"},[d],8,["disabled"])])])})),h=(t("4160"),t("a15b"),t("d81d"),t("fb6a"),t("d3b7"),t("ac1f"),t("1276"),t("159b"),t("96cf"),t("1da1")),f=t("3a5e"),p=t("3e54"),O=t("14b7"),j={name:"Switches",components:{Loading:f["a"]},mixins:[p["a"]],data:function(){return{loading:!1,plugins:{},components:{},selectedPlugin:null,bus:Object(O["a"])()}},methods:{initPanels:function(){var e=this;this.components={},Object.keys(this.plugins).forEach(function(){var n=Object(h["a"])(regeneratorRuntime.mark((function n(r){var i,s,u;return regeneratorRuntime.wrap((function(n){while(1)switch(n.prev=n.next){case 0:return i=r.split(".").map((function(e){return e[0].toUpperCase()+e.slice(1)})).join(""),s=null,n.prev=2,n.next=5,t("c1da")("./".concat(i,"/Index"));case 5:s=n.sent,n.next=11;break;case 8:return n.prev=8,n.t0=n["catch"](2),n.abrupt("return");case 11:u=Object(c["i"])(Object(h["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return e.abrupt("return",s);case 1:case"end":return e.stop()}}),e)})))),e.$options.components[r]=u,e.components[r]=u;case 14:case"end":return n.stop()}}),n,null,[[2,8]])})));return function(e){return n.apply(this,arguments)}}())},refresh:function(){var e=this;return Object(h["a"])(regeneratorRuntime.mark((function n(){return regeneratorRuntime.wrap((function(n){while(1)switch(n.prev=n.next){case 0:return e.loading=!0,n.prev=1,n.next=4,e.request("utils.get_switch_plugins");case 4:e.plugins=n.sent,e.initPanels();case 6:return n.prev=6,e.loading=!1,n.finish(6);case 9:case"end":return n.stop()}}),n,null,[[1,,6,9]])})))()}},mounted:function(){this.refresh()}};t("84aa");j.render=b,j.__scopeId="data-v-eac2ea44";n["default"]=j},"7ac9":function(e,n,t){},"84aa":function(e,n,t){"use strict";t("7ac9")},c1da:function(e,n,t){var c={"./LightHue/Index":["0219","chunk-06539e5d","chunk-5d632024","chunk-35986630"],"./Smartthings/Index":["6e68","chunk-06539e5d","chunk-5d632024","chunk-972487d6"],"./SwitchSwitchbot/Index":["5083","chunk-06539e5d","chunk-5d632024","chunk-0021f7ee"],"./SwitchTplink/Index":["d11f","chunk-06539e5d","chunk-5d632024","chunk-c4aee99e"],"./SwitchWemo/Index":["bedd","chunk-06539e5d","chunk-5d632024","chunk-60dbbc82"],"./ZigbeeMqtt/Index":["65d6","chunk-06539e5d","chunk-5d632024","chunk-07773226"],"./Zwave/Index":["e170","chunk-06539e5d","chunk-5d632024","chunk-0827360a"]};function r(e){if(!t.o(c,e))return Promise.resolve().then((function(){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}));var n=c[e],r=n[0];return Promise.all(n.slice(1).map(t.e)).then((function(){return t(r)}))}r.keys=function(){return Object.keys(c)},r.id="c1da",e.exports=r}}]);
//# sourceMappingURL=chunk-31bc5041.ff5b04fa.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5d73ace1"],{"74d4":function(e,t,c){"use strict";c("8a53")},"8a53":function(e,t,c){},"9b92":function(e,t,c){"use strict";c.r(t);c("b0c0");var i=c("7a23"),n=Object(i["K"])("data-v-0fad5251");Object(i["u"])("data-v-0fad5251");var a={class:"camera component-row"},r={class:"feed-container",ref:"container"},s={key:2},b={class:"controls"},o={key:0,class:"fa fa-play"},l={key:1,class:"fa fa-pause"};Object(i["s"])();var d=n((function(e,t,c,n,d,u){return Object(i["r"])(),Object(i["e"])("div",a,[Object(i["h"])("div",r,[d.visible?Object(i["f"])("",!0):(Object(i["r"])(),Object(i["e"])("div",{key:0,class:"no-content",textContent:Object(i["C"])(c.name)},null,8,["textContent"])),d.visible&&"image"===c.type?(Object(i["r"])(),Object(i["e"])("img",{key:1,alt:"Camera feed",src:u.imgUrl},null,8,["src"])):d.visible&&"video"===c.type?(Object(i["r"])(),Object(i["e"])("video",s,[Object(i["h"])("source",{src:c.src},null,8,["src"])])):Object(i["f"])("",!0)],512),Object(i["h"])("div",b,[Object(i["h"])("button",{class:"toggle-btn",onClick:t[1]||(t[1]=function(e){return d.visible=!d.visible})},[d.visible?(Object(i["r"])(),Object(i["e"])("i",l)):(Object(i["r"])(),Object(i["e"])("i",o))])])])})),u=(c("c975"),c("d3b7"),c("25f0"),c("3e54")),f={name:"Camera",mixins:[u["a"]],props:{src:{type:String,required:!0},type:{type:String,default:"image"},name:{type:String}},computed:{imgUrl:function(){if("image"===this.type)return this.src+(this.src.indexOf("?")>0?"&":"?")+"_t="+(new Date).getTime().toString()}},data:function(){return{visible:!1}}};c("74d4");f.render=d,f.__scopeId="data-v-0fad5251";t["default"]=f}}]);
//# sourceMappingURL=chunk-5d73ace1.8c73eb72.js.map

View file

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./src/components/widgets/Camera/Index.vue?60da","webpack:///./src/components/widgets/Camera/Index.vue","webpack:///./src/components/widgets/Camera/Index.vue?b1bc"],"names":["class","ref","visible","type","alt","src","imgUrl","name","mixins","Utils","props","String","required","default","computed","this","indexOf","Date","getTime","toString","data","render","__scopeId"],"mappings":"kHAAA,W,2KCCOA,MAAM,wB,GACJA,MAAM,iBAAiBC,IAAI,a,aAQ3BD,MAAM,Y,SAEJA,MAAM,c,SACNA,MAAM,e,wEAZf,eAeM,MAfN,EAeM,CAdJ,eAMM,MANN,EAMM,CALyC,EAAAE,Q,wCAA7C,eAAwD,O,MAAnDF,MAAM,a,YAAa,eAAa,EAAD,O,yBACO,EAAAE,SAAe,UAAJ,EAAAC,M,iBAAtD,eAAwE,O,MAAnEC,IAAI,cAAeC,IAAK,EAAAC,Q,iBACX,EAAAJ,SAAe,UAAJ,EAAAC,M,iBAA7B,eAEQ,WADN,eAAmB,UAAVE,IAAK,EAAAA,KAAG,mB,4BAIrB,eAKM,MALN,EAKM,CAJJ,eAGS,UAHDL,MAAM,aAAc,QAAK,+BAAE,EAAAE,SAAW,EAAAA,W,CACf,EAAAA,S,iBAC7B,eAAgC,IAAhC,K,iBADA,eAAwC,IAAxC,a,4CAaO,GACbK,KAAM,SACNC,OAAQ,CAACC,EAAA,MACTC,MAAO,CAKLL,IAAK,CACHF,KAAMQ,OACNC,UAAU,GAMZT,KAAM,CACJA,KAAMQ,OACNE,QAAS,SAMXN,KAAM,CACJJ,KAAMQ,SAIVG,SAAU,CACRR,OADQ,WAEN,GAAkB,UAAdS,KAAKZ,KAGT,OAAOY,KAAKV,KAAOU,KAAKV,IAAIW,QAAQ,KAAO,EAAI,IAAM,KAAO,OAAS,IAAIC,MAAOC,UAAUC,aAI9FC,KAtCa,WAuCX,MAAO,CACLlB,SAAS,K,UC5Df,EAAOmB,OAAS,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-5d73ace1.8c73eb72.js","sourcesContent":["export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../node_modules/vue-loader-v16/dist/stylePostLoader.js!../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader-v16/dist/index.js??ref--0-1!./Index.vue?vue&type=style&index=0&id=0fad5251&lang=scss&scoped=true\"","<template>\n <div class=\"camera component-row\">\n <div class=\"feed-container\" ref=\"container\">\n <div class=\"no-content\" v-text=\"name\" v-if=\"!visible\" />\n <img alt=\"Camera feed\" :src=\"imgUrl\" v-if=\"visible && type === 'image'\">\n <video v-else-if=\"visible && type === 'video'\">\n <source :src=\"src\">\n </video>\n </div>\n\n <div class=\"controls\">\n <button class=\"toggle-btn\" @click=\"visible = !visible\">\n <i class=\"fa fa-play\" v-if=\"!visible\" />\n <i class=\"fa fa-pause\" v-else />\n </button>\n </div>\n </div>\n</template>\n\n<script>\nimport Utils from \"@/Utils\";\n\n/**\n * This component can be used to view a feed from a camera.\n */\nexport default {\n name: \"Camera\",\n mixins: [Utils],\n props: {\n /**\n * Camera feed URL.\n * For instance, in the case of a PiCamera feed: http://host:8008/camera/pi/video.mjpeg\n */\n src: {\n type: String,\n required: true,\n },\n\n /**\n * Camera feed type - it can be \"image\" (usually in case of MJPEG feeds) or \"video\".\n */\n type: {\n type: String,\n default: \"image\",\n },\n\n /**\n * Camera feed name.\n */\n name: {\n type: String,\n },\n },\n\n computed: {\n imgUrl() {\n if (this.type !== 'image')\n return\n\n return this.src + (this.src.indexOf('?') > 0 ? '&' : '?') + '_t=' + (new Date().getTime().toString())\n },\n },\n\n data() {\n return {\n visible: false,\n }\n },\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.camera {\n width: calc(100% + 2em);\n height: calc(100% + 2em);\n position: relative;\n background: black;\n color: #888;\n margin: -1em;\n\n .feed-container {\n width: 100%;\n height: calc(100% - 3em);\n display: flex;\n justify-content: center;\n align-items: center;\n }\n\n .controls {\n width: 100%;\n height: 3em;\n position: absolute;\n bottom: 0;\n\n button {\n background: none;\n border: none;\n color: #888;\n }\n }\n}\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=0fad5251&scoped=true\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\n\nimport \"./Index.vue?vue&type=style&index=0&id=0fad5251&lang=scss&scoped=true\"\nscript.render = render\nscript.__scopeId = \"data-v-0fad5251\"\n\nexport default script"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,102 @@
<template>
<div class="camera component-row">
<div class="feed-container" ref="container">
<div class="no-content" v-text="name" v-if="!visible" />
<img alt="Camera feed" :src="imgUrl" v-if="visible && type === 'image'">
<video v-else-if="visible && type === 'video'">
<source :src="src">
</video>
</div>
<div class="controls">
<button class="toggle-btn" @click="visible = !visible">
<i class="fa fa-play" v-if="!visible" />
<i class="fa fa-pause" v-else />
</button>
</div>
</div>
</template>
<script>
import Utils from "@/Utils";
/**
* This component can be used to view a feed from a camera.
*/
export default {
name: "Camera",
mixins: [Utils],
props: {
/**
* Camera feed URL.
* For instance, in the case of a PiCamera feed: http://host:8008/camera/pi/video.mjpeg
*/
src: {
type: String,
required: true,
},
/**
* Camera feed type - it can be "image" (usually in case of MJPEG feeds) or "video".
*/
type: {
type: String,
default: "image",
},
/**
* Camera feed name.
*/
name: {
type: String,
},
},
computed: {
imgUrl() {
if (this.type !== 'image')
return
return this.src + (this.src.indexOf('?') > 0 ? '&' : '?') + '_t=' + (new Date().getTime().toString())
},
},
data() {
return {
visible: false,
}
},
}
</script>
<style lang="scss" scoped>
.camera {
width: calc(100% + 2em);
height: calc(100% + 2em);
position: relative;
background: black;
color: #888;
margin: -1em;
.feed-container {
width: 100%;
height: calc(100% - 3em);
display: flex;
justify-content: center;
align-items: center;
}
.controls {
width: 100%;
height: 3em;
position: absolute;
bottom: 0;
button {
background: none;
border: none;
color: #888;
}
}
}
</style>

View file

@ -176,12 +176,17 @@ export default {
props.handlers = this._parseHandlers(element)
props._vars = this._parseVars(element)
const newEl = document.createElement('div')
newEl.setAttribute('class', 'component')
newEl.innerHTML = element.innerHTML
element.parentNode.replaceChild(newEl, element)
createApp({
render() { return h(component, props) },
data() {
return { bus: bus }
},
}).mount(element)
}).mount(newEl)
})
})

View file

@ -1,11 +1,11 @@
import Run from './components/Run'
import Switch from './components/Switch'
import Sensor from "./components/Sensor";
import Slider from './components/Slider'
import Sensor from "@/components/widgets/Component/components/Sensor";
import Switch from './components/Switch'
export default {
Run,
Switch,
Slider,
Sensor,
Slider,
Switch,
}